I want to create custom sliders or seekbars in android (just as in the gif, slider on the bottom and right), could you provide me with any relevant process how to achieve this.
After searching for several days I have finally got enough resources to address the problem statement.
For staters go through the following resources:
1) https://guides.codepath.com/android/Basic-Painting-with-Views
2) https://guides.codepath.com/android/Progress-Bar-Custom-View
3) https://developer.android.com/guide/topics/ui/custom-components
Basics Steps -
Extend an existing View class or subclass with your own class.
Override some of the methods from the superclass. The superclass methods to override start with 'on', for example, onDraw(), onMeasure(), and onKeyDown(). This is similar to the on... events in Activity or ListActivity that you override for lifecycle and other functionality hooks.
Use your new extension class. Once completed, your new extension class can be used in place of the view upon which it was based.
Below is the code that demonstrate a working Clock in canvas -
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.Calendar;
/**
* Created by moonis
* on 23/06/18.
*/
public class CustomClock extends View {
private int height, width = 0;
private int padding = 0;
private int fontSize = 0;
int numeralSpacing = 0;
private int handTruncation, hourHandTruncation = 0;
private int radius = 0;
private Paint paint;
private boolean isInit;
private int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
private Rect rect = new Rect();
public CustomClock(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
}
private void initClock() {
height = getHeight();
width = getWidth();
padding = numeralSpacing + 50;
fontSize = (int) DeviceDimensionHelper.convertDpToPixel(13, getContext());
int min = Math.min(height, width);
radius = min / 2 - padding;
handTruncation = min / 20;
hourHandTruncation = min / 7;
paint = new Paint();
isInit = false;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isInit) {
initClock();
}
canvas.drawColor(Color.BLACK);
drawCircle(canvas);
drawCentre(canvas);
drawNumeral(canvas);
drawHands(canvas);
postInvalidateDelayed(500);
}
private void drawCircle(Canvas canvas) {
paint.reset();
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(width / 2, height / 2, radius + padding - 10, paint);
}
private void drawCentre(Canvas canvas) {
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(width / 2, height / 2, 12, paint);
}
private void drawNumeral(Canvas canvas) {
paint.setTextSize(fontSize);
for (int number : numbers) {
String tmp = String.valueOf(number);
paint.getTextBounds(tmp, 0, tmp.length(), rect);
double angle = Math.PI / 6 * (number - 3);
int x = (int) (width / 2 + Math.cos(angle) * radius - rect.width() / 2);
int y = (int) (height / 2 + Math.sin(angle) * radius - rect.height() / 2);
canvas.drawText(tmp, x, y, paint);
}
}
private void drawHands(Canvas canvas) {
Calendar c = Calendar.getInstance();
float hour = c.get(Calendar.HOUR_OF_DAY);
hour = hour > 12 ? hour - 12 : hour;
drawHand(canvas, (hour + c.get(Calendar.MINUTE) / 60) * 5f, true);
drawHand(canvas, c.get(Calendar.MINUTE), false);
drawHand(canvas, c.get(Calendar.SECOND), false);
}
private void drawHand(Canvas canvas, double loc, boolean isHour) {
double angle = Math.PI * loc / 30 - Math.PI / 2;
int handRadius = isHour ? radius - handTruncation - hourHandTruncation : radius - handTruncation;
canvas.drawLine(width / 2, height / 2, (float) (width / 2 + Math.cos(angle) * handRadius), (float) (height / 2 + Math.sin(angle) * handRadius), paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//code to move clock hands on screen gestures
break;
case MotionEvent.ACTION_MOVE:
//code to move clock hands on screen gestures
break;
default:
return false;
}
//redraw view
postInvalidate();
return true;
}
}
Finally this library can be used to achieve the desired output -
https://github.com/moldedbits/android-dial-picker
have a look at this Wheelview Library to achieve the bottom wheel
and this for your vertical ruler
to scale your image horizontally and vertically, probably you might have to go with some sort of custom solution, Vector images would be a suitable fit.
Also refer this
Hope this helps you.
Related
I am Trying to implement a swipe touch to control the paddle in the app, I've managed to get the paddle move by detecting a single touch but I can't understand how to make a fling work. I've tried implementing ths solution by referring the official android docs but this crashes the app. Any Advice?
package com.nblsoft.pong;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v4.view.GestureDetectorCompat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class PongLogic extends View {
MainActivity mainactivity = (MainActivity) getContext();
private GestureDetectorCompat mDetector = new GestureDetectorCompat(mainactivity, new MyGestureListner());
//set screen constrains in dip
Configuration configuration = this.getResources().getConfiguration();
int dpHeight = configuration.screenHeightDp; //The current height of the available screen space, in dp units, corresponding to screen height resource qualifier.
int dpWidth = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
//int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
//DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
//float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
//float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
private int dptopixel(int DESIRED_DP_VALUE) {
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_DP_VALUE) * scale + 0.5f);
}
private int pixeltodp(int DESIRED_PIXEL_VALUE) {
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_PIXEL_VALUE) - 0.5f / scale);
}
//set paddle size, speed, position vector
int AI_paddle_pos_x = 4 * (dptopixel(dpWidth) / 100); //3 for 320x480, 10 for 1080x1920 etc.
int paddle_width = (dptopixel(dpWidth) / 10); //
int AI_paddle_pos_y = (dptopixel(dpHeight) / 10); //48 for 320x480, 190 for 1080x1920 etc.
int paddle_height = (dptopixel(dpHeight) / 100) + 3; //the paddle is 100% of the total height of phone.
int user_paddle_pos_x = 4 * (dptopixel(dpWidth) / 100);
int user_paddle_pos_y = dptopixel(dpHeight) - ((dptopixel(dpHeight) / 10) + (dptopixel(dpHeight) / 100) + 3);
//Score
int score_user = 0;
int score_AI = 0;
//User Paddle
public Rect paddle_user = new Rect(user_paddle_pos_x,
user_paddle_pos_y,
user_paddle_pos_x + paddle_width,
user_paddle_pos_y + paddle_height);
int user_paddle_vel = 0;
//AI paddle
Rect paddle_AI = new Rect(AI_paddle_pos_x,
AI_paddle_pos_y,
AI_paddle_pos_x + paddle_width,
AI_paddle_pos_y + paddle_height);
//set ball position vector, Velocity vector, acceleration
int ball_pos_x = 0;
int ball_pos_y = (dptopixel(dpHeight) / 2);
int ball_size = dptopixel(dpWidth) / 100;
int ball_velocity_x = 1;
int ball_velocity_y = 3;
// Ball
Rect ball = new Rect(ball_pos_x,
ball_pos_y,
ball_pos_x + ball_size,
ball_pos_y + ball_size);
//Override onDraw method
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint myUI = new Paint();
myUI.setColor(Color.WHITE);
Paint myscore = new Paint();
myscore.setTextSize(dptopixel(dpWidth / 20));
myscore.setColor(Color.WHITE);
//mytext.setStyle(Paint.Style.STROKE);
//mytext.setStrokeWidth(2);
// Draw Score
canvas.drawText(Integer.toString(score_AI), (dptopixel(dpWidth) / 10), (dptopixel(dpHeight) / 4), myscore);
canvas.drawText(Integer.toString(score_user), (dptopixel(dpWidth) / 10), 3 * (dptopixel(dpHeight) / 4), myscore);
// Draw Middle point
canvas.drawRect(0, ((dptopixel(dpHeight)) / 2), (dptopixel(dpWidth)), (((dptopixel(dpHeight)) / 2) + 2), myUI);
// Draw both paddles
canvas.drawRect(paddle_user, myUI);
canvas.drawRect(paddle_AI, myUI);
// Draw ball
canvas.drawRect(ball, myUI);
//Practise Methods
//canvas.drawText(Integer.toString(dptopixel(dpHeight)),300,300,mytext);
//canvas.drawText(Integer.toString(dptopixel(dpWidth)), 400, 400, mytext);
//canvas.drawText(Integer.toString(dpHeight),500,500,mytext);
//canvas.drawText(Integer.toString(dpWidth),600,600,mytext);
//canvas.drawText("Fuck", 700, 700, mytext);
//canvas.drawRect(0,0,dptopixel(dpWidth),dptopixel(dpHeight),mytext);
//Game Loop Updater
update();
invalidate();
}
private void update() {
if(ball.centerY() < dptopixel(dpHeight)/2){ paddle_AI.offsetTo(ball.centerX()- dptopixel(10), AI_paddle_pos_y); }
if (paddle_user.contains(ball)) {
ball_velocity_y = ball_velocity_y * -1;
} else if (paddle_AI.contains(ball)) {
ball_velocity_y = ball_velocity_y * -1;
} else if ((ball.centerX() > (dptopixel(dpWidth))) || (ball.centerX() < 0)) {
ball_velocity_x = ball_velocity_x * -1;
} else if (ball.centerY() < 0) {
//Update the user score
score_user = 1 + score_user;
//re draw the ball
ball.offsetTo(0, (dptopixel(dpHeight) / 2));
} else if (ball.centerY() > (dptopixel(dpHeight))) {
//Update the AI score
score_AI = 1 + score_AI;
//re draw the ball
ball.offsetTo(0, (dptopixel(dpHeight) / 2));
}
ball.offset(ball_velocity_x, ball_velocity_y);
}
//Override Touch method
/*
//
NOT WORKING SWIPE METHODS
float x1, x2, y1, y2;
final int MIN_DISTANCE = 70;
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x1 = event.getX();
y1 = event.getY();
break;
case MotionEvent.ACTION_UP:
x2 = event.getX();
y2 = event.getY();
float deltaX = x2 - x1;
float deltaY = y2 - y1;
if (deltaX > MIN_DISTANCE)
{
paddle_user.offset(1, 0);
break;
}
else if (Math.abs(deltaX) > MIN_DISTANCE)
{
paddle_user.offset(-1, 0);
break;
}
}
return true;
}*/
#Override
public boolean onTouchEvent(MotionEvent event) {
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
/*
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (event.getX() > dptopixel(dpWidth) / 2) {
paddle_user.offset(10, 0);
} else {
paddle_user.offset(-10, 0);
}
}
return true;
*/
}
/* #Override
public boolean onTouch(View v, MotionEvent event) {
this.paddle_user.offsetTo(10,10);
return true; //Event Handled
}
*/
public PongLogic(Context context) {
super(context);
setBackgroundColor(Color.BLACK); //to set background
this.setFocusableInTouchMode(true); //to enable touch mode
}
class MyGestureListner extends GestureDetector.SimpleOnGestureListener{
#Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
if (event1.getAction() == MotionEvent.ACTION_DOWN) {
user_paddle_vel = (int) velocityX;
paddle_user.offset(user_paddle_vel,0);
}
else if(event1.getAction() == MotionEvent.ACTION_UP){
user_paddle_vel = 0;
}
return true;
}
}
}
You can take a look to my article in Medium, I explained step by step how to do it: https://medium.com/#euryperez/android-pearls-detect-swipe-and-touch-over-a-view-203ae2c028dc#.lcmg4jytc
I am trying to get this paddle_user to move vertically on when the screen is touched. But the paddle isn't moving. I've double checked the onTouch Code but i'm still no closer to finding out what I am doing wrong.
package com.nblsoft.pong;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
public class PongLogic extends View implements View.OnTouchListener {
//set screen constrains in dip
Configuration configuration = this.getResources().getConfiguration();
int dpHeight = configuration.screenHeightDp; //The current height of the available screen space, in dp units, corresponding to screen height resource qualifier.
int dpWidth = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier.
//int smallestScreenWidthDp = configuration.smallestScreenWidthDp; //The smallest screen size an application will see in normal operation, corresponding to smallest screen width resource qualifier.
//DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
//float dpHeight = displayMetrics.heightPixels / displayMetrics.density;
//float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
private int dptopixel(int DESIRED_DP_VALUE){
final float scale = getResources().getDisplayMetrics().density;
return (int)((DESIRED_DP_VALUE) * scale + 0.5f);
}
private int pixeltodp(int DESIRED_PIXEL_VALUE){
final float scale = getResources().getDisplayMetrics().density;
return (int) ((DESIRED_PIXEL_VALUE) - 0.5f / scale);
}
//set paddle size, speed, position vector
int paddle_pos_x = 4 * (dptopixel(dpWidth)/100); //3 for 320x480, 10 for 1080x1920 etc.
int paddle_width = (dptopixel(dpWidth)/10); //
int paddle_pos_y = (dptopixel(dpHeight)/10); //48 for 320x480, 190 for 1080x1920 etc.
int paddle_height = (dptopixel(dpHeight)/100) + 3; //the paddle is 100% of the total height of phone.
int user_paddle_pos_x = 4 * (dptopixel(dpWidth)/100) ;
int user_paddle_pos_y = dptopixel(dpHeight) - ((dptopixel(dpHeight)/10) + (dptopixel(dpHeight)/100) + 3) ;
//User Paddle
public Rect paddle_user = new Rect(user_paddle_pos_x,
user_paddle_pos_y,
user_paddle_pos_x + paddle_width,
user_paddle_pos_y + paddle_height);
//AI paddle
Rect paddle_AI = new Rect(paddle_pos_x,
paddle_pos_y,
paddle_pos_x + paddle_width,
paddle_pos_y + paddle_height);
//set ball position vector, Velocity vector, acceleration
//Override onDraw method
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint mytext = new Paint();
mytext.setColor(Color.WHITE);
// Draw Middle point
canvas.drawRect(0, ((dptopixel(dpHeight)) / 2), (dptopixel(dpWidth)), (((dptopixel(dpHeight)) / 2) + 2), mytext);
canvas.drawRect(paddle_user,mytext);
canvas.drawRect(paddle_AI, mytext);
//Practise Methods
//canvas.drawText(Integer.toString(dptopixel(dpHeight)),300,300,mytext);
//canvas.drawText(Integer.toString(dptopixel(dpWidth)), 400, 400, mytext);
//canvas.drawText(Integer.toString(dpHeight),500,500,mytext);
//canvas.drawText(Integer.toString(dpWidth),600,600,mytext);
//canvas.drawText("Fuck", 700, 700, mytext);
//canvas.drawRect(0,0,dptopixel(dpWidth),dptopixel(dpHeight),mytext);
}
//Override Touch method
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
paddle_user.offsetTo(10,0);
}
return true; //Event Handled
}
public PongLogic(Context context) {
super(context);
setBackgroundColor(Color.BLACK); //to set background
this.setFocusableInTouchMode(true); //to enable touch mode
this.setOnTouchListener(this);
}
}
You have to implement the OnTouchListener interface and do this.setOnTouchListener(this).
Edit:
public class CustomClass extends View implements View.OnTouchListener{}
then in your constructor you add this.setOnTouchListener(this);
Edit2:
Ok so I forgot to tell you but when you does some modification for exemple with you rect you have to call the draw method and to do that properly you call invalidate. So in your ontouch method add invalidate().
here is the code that I did if you wan to check (I just cut your code to have a easier exemple):
public class PongLogic extends View implements View.OnTouchListener {
//set ball position vector, Velocity vector, acceleration
Rect paddle_user = new Rect(0, 100, 100, 200);
public PongLogic(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnTouchListener(this);
setBackgroundColor(Color.BLACK); //to set background
this.setFocusableInTouchMode(true);
}
//Override onDraw method
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
Paint mytext = new Paint();
mytext.setColor(Color.WHITE);
mytext.setStyle(Paint.Style.STROKE);
mytext.setStrokeWidth(2);
canvas.drawRect(paddle_user, mytext);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
paddle_user.offsetTo(0,0);
invalidate();
return false;
}
}
So what you missed was basically the invalidate() method. So what you did in the begging was not wrong with the first OnTouch method but better to do with the interface if you make a custom view.
I created my own TextView by extending the TextView class in order to improve its display. I created various Paint and stuff to add a kind of margin. Then text has to be displayed right after the margin. If I set
android:layout_width="fill_parent"
the display is ok and my line is fully filled with a white background (as defined in my layout).
BUT if I set
android:layout_width="wrap_content"
the display goes wrong and the end of the text of my TextView is cropped. I guess this is due to the fact that I made a Translate in the onDraw method of my TextView but I don't know how to fix it.
Please note that I need the set wrap_content because I want to add another TextBox right after, and a LinearLayout around both, but for the moment the other TextBox erase a part of the content of the first one.
The code of my new TextBox is the following one :
package com.flo.ui;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.widget.TextView;
import com.flo.musicalnotes.R;
public class NoteItemTextView extends TextView {
// Properties
private Paint marginPaint;
private Paint linePaint;
private Paint circlePaint;
private int paperColor;
private float margin;
private float marginEnd;
private float textStart;
// Initialization
public NoteItemTextView(Context context) {
super(context);
this.Init(context);
}
public NoteItemTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.Init(context);
}
private void Init(Context context)
{
// Resources retrieval
Resources myResources = getResources();
// Brush definition
this.marginPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.marginPaint.setColor(myResources.getColor(R.color.marginColor));
this.marginPaint.setStrokeWidth((float) 1.8);
this.linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.linePaint.setColor(myResources.getColor(R.color.underlineColor));
this.circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.circlePaint.setColor(myResources.getColor(R.color.marginColor));
this.circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
// various resources
this.paperColor = myResources.getColor(R.color.bgColor);
this.margin = myResources.getDimension(R.dimen.marginSize);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int ot = getResources().getConfiguration().orientation;
switch(ot)
{
case Configuration.ORIENTATION_LANDSCAPE:
this.marginEnd = this.margin + metrics.widthPixels / 100;
this.textStart = this.marginEnd + metrics.widthPixels / 100;
case Configuration.ORIENTATION_PORTRAIT:
this.marginEnd = this.margin + metrics.heightPixels / 100;
this.textStart = this.marginEnd + metrics.heightPixels / 100;
default:
this.marginEnd = this.margin + 5;
this.textStart = this.marginEnd + 10;
}
}
//#Override
protected void onDraw(Canvas canvas) {
// paper color
canvas.drawColor(this.paperColor);
// lines drawing
canvas.drawLine(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight(), this.linePaint);
// marge drawing
canvas.drawLine(this.margin, 0, this.margin, getMeasuredHeight(), this.marginPaint);
canvas.drawLine(this.marginEnd, 0, this.marginEnd, getMeasuredHeight(), this.marginPaint);
double x = (this.textStart + this.marginEnd) / 1.8;
float y1 = getMeasuredHeight() / 3;
float y2 = getMeasuredHeight() * 2 / 3;
float radius = (float) 2.5;
canvas.drawCircle((float) x, y1, radius, this.circlePaint);
canvas.drawCircle((float) x, y2, radius, this.circlePaint);
canvas.save();
canvas.translate(this.textStart, 0);
super.onDraw(canvas);
canvas.restore();
}
}
Thanks for your help !
Try to add this code to your custom textview class
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = getMeasuredHeight();
int width = getMeasuredWidth();
Log.e(getClass().getSimpleName() , String.format("height x %s ::: width x %s ",height , width));
float density = getResources().getDisplayMetrics().density;
//Extra space after last letter.
float px = 2 * density;
int adjustedWidth = (int) (width + textStart + px);
setMeasuredDimension(adjustedWidth, height);
}
add this to your textview
android:paddingRight="25dp"
I wish I could implement in Android what I show in this screenshot:
The number inside the circle there is no problem for me (simply make some accounts). But I do not know how to put the circle or as putting the marker.
I mean, it's like a speedometer. Then I want depending on the numerical value, the marker is in the position of the circle that matches the numerical value (the value is moved in a range of 0-100, so that the minimum value of the marker in the circle will be 0 or 100, obviously).
I'm a bit stuck with this, any help would be welcome. Thank you.
i created a circleView for you..just give a try...
CircleView.java
package com.example.err2;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class Circle extends View {
int radius, number, fontSize = 80;
Paint myPaint;
public void setRadius(int radius) {
this.radius = radius;
}
public void setNumber(int number) {
this.number = number;
}
private void init() {
Log.d("init", "start");
myPaint = new Paint();
myPaint.setColor(Color.RED);
}
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.save();
// Drawing main crcle
canvas.drawCircle(getWidth() / 2,
getHeight() / 2,
radius, myPaint);
myPaint.setColor(Color.WHITE);
canvas.drawCircle(getWidth() / 2,
getHeight() / 2,
radius - 10, myPaint);
// end of main circle
// drawing text-number
myPaint.setColor(Color.BLACK);
myPaint.setTextSize(fontSize);
myPaint.setTextAlign(Align.CENTER);
myPaint.setFakeBoldText(true);
canvas.drawText(number + "",
getWidth() / 2,
getHeight() / 2 + fontSize / 3, myPaint);
// end of drwaing text
// drawing point on circle boundry
// findPointLocation();
double deg = number * 3.6f;
double radians = Math.toRadians(deg);
int px = (int) Math.abs(radius * Math.sin(radians));
int py = (int) Math.abs(radius * Math.cos(radians));
if (number <= 25) {
px = -px;
} else if (number <= 50) {
px = -px;
py = -py;
} else if (number <= 75) {
py = -py;
}
// end od find point
myPaint.setColor(Color.GREEN);
canvas.drawCircle(getWidth() / 2 + px, getHeight() / 2 + py, 15,
myPaint);
// end of drawing point
canvas.restore();
}
}
below given the mainactivity code
MainActivity.java
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Circle circle=(Circle)findViewById(R.id.circle1);
circle.setRadius(100);
circle.setNumber(90);
}
}
given below xml code
XML
<com.example.err2.Circle
android:id="#+id/circle1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="73dp"
android:background="#778888" />
I want to paint graphics onto a Canvas such that the colors are additive. For example, I want to produce this:
But instead, I get this:
Note that the half white, half black background is intentional, just to see how alpha interacts with both backgrounds. I will be happy to have this work with either background. Here is my code:
public class VennColorsActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
class VennView extends View {
public VennView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int alpha = 60, val = 255;
int ar = Color.argb(alpha, val, 0, 0);
int ag = Color.argb(alpha, 0, val, 0);
int ab = Color.argb(alpha, 0, 0, val);
float w = canvas.getWidth();
float h = canvas.getHeight();
float cx = w / 2f;
float cy = h / 2;
float r = w / 5;
float tx = (float) (r * Math.cos(30 * Math.PI / 180));
float ty = (float) (r * Math.sin(30 * Math.PI / 180));
float expand = 1.5f;
Paint paint = new Paint();
paint.setColor(Color.WHITE);
canvas.drawRect(new Rect(0, 0, (int) w, (int) (h / 2)), paint);
PorterDuff.Mode mode = android.graphics.PorterDuff.Mode.ADD;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColorFilter(new PorterDuffColorFilter(ar, mode));
paint.setColor(ar);
canvas.drawCircle(cx, cy - r, expand * r, paint);
paint.setColorFilter(new PorterDuffColorFilter(ag, mode));
paint.setColor(ag);
canvas.drawCircle(cx - tx, cy + ty, expand * r, paint);
paint.setColorFilter(new PorterDuffColorFilter(ab, mode));
paint.setColor(ab);
canvas.drawCircle(cx + tx, cy + ty, expand * r, paint);
}
}
this.setContentView(new VennView(this));
}
}
Can someone please help me understand how to paint with additive colors in Android graphics?
You are on the right track. There are 3 major issues in your code:
You need to set xfer mode iso color filter
Use temp bitmap for rendering your image
Alpha should be 0xFF in order to get results you are looking for
Here is what I've got by using xfer mode. What I'm doing - is drawing everything into temporary bitmap and then rendering entire bitmap to main canvas.
You ask why do you need temp bitmap? Good question! If you are drawing everything on a main canvas, your colors will be blended with main canvas background color, so all colors will get messed up. Transparent temp bitmap helps to keep your colors away of other parts of UI
Please make sure you are not allocating anything in onDraw() - you will run out memory very soon in this way.. Also make sure you have recycled your temp bitmap when you no longer need it.
package com.example.stack2;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.os.Bundle;
import android.view.View;
public class YouAreWelcome extends Activity {
Bitmap tempBmp = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
Canvas c = new Canvas();
Paint paint = new Paint();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
class VennView extends View {
public VennView(Context context) {
super(context);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(tempBmp.isRecycled() || tempBmp.getWidth()!=canvas.getWidth() || tempBmp.getHeight()!=canvas.getHeight())
{
tempBmp.recycle();
tempBmp = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888);
c.setBitmap(tempBmp);
}
//clear previous drawings
c.drawColor(Color.TRANSPARENT, Mode.CLEAR);
int alpha = 255, val = 255;
int ar = Color.argb(alpha, val, 0, 0);
int ag = Color.argb(alpha, 0, val, 0);
int ab = Color.argb(alpha, 0, 0, val);
float w = canvas.getWidth();
float h = canvas.getHeight();
float cx = w / 2f;
float cy = h / 2;
float r = w / 5;
float tx = (float) (r * Math.cos(30 * Math.PI / 180));
float ty = (float) (r * Math.sin(30 * Math.PI / 180));
float expand = 1.5f;
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(Mode.ADD));
paint.setColor(ar);
c.drawCircle(cx, cy - r, expand * r, paint);
paint.setColor(ag);
c.drawCircle(cx - tx, cy + ty, expand * r, paint);
paint.setColor(ab);
c.drawCircle(cx + tx, cy + ty, expand * r, paint);
canvas.drawBitmap(tempBmp, 0, 0, null);
}
}
this.setContentView(new VennView(this));
}
}
Thanks again Pavel. That would have been very difficult for me to have figured out on my own. I am answering my own question to better drill into details, but I've accepted yours as the best answer.
You are right that I prefer not to have to create and manage an off-screen Bitmap (and Canvas). That is essentially why I mentioned that a black or white background would be fine to make this work.
I'm never concerned with performance before I see something working but I share your caution after that point. Editing your version to fix that and remove those members gives the implementation below.
Is this robust? Note the two calls to Canvas.drawColor() which I suspect could also be combined into one.
package com.superliminal.android.test.venn;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.os.Bundle;
import android.view.View;
public class VennColorsActivity extends Activity {
private Paint paint = new Paint();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
class VennView extends View {
public VennView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
int alpha = 255, val = 255;
int ar = Color.argb(alpha, val, 0, 0);
int ag = Color.argb(alpha, 0, val, 0);
int ab = Color.argb(alpha, 0, 0, val);
float w = canvas.getWidth();
float h = canvas.getHeight();
float cx = w / 2f;
float cy = h / 2;
float r = w / 5;
float tx = (float) (r * Math.cos(30 * Math.PI / 180));
float ty = (float) (r * Math.sin(30 * Math.PI / 180));
float expand = 1.5f;
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(Mode.ADD));
paint.setColor(ar);
canvas.drawCircle(cx, cy - r, expand * r, paint);
paint.setColor(ag);
canvas.drawCircle(cx - tx, cy + ty, expand * r, paint);
paint.setColor(ab);
canvas.drawCircle(cx + tx, cy + ty, expand * r, paint);
}
}
setContentView(new VennView(this));
}
}