I want to show the progress of a file upload by filling an arc untill it hits 100% (a circle). This is quite easy to do. I only needed it to rotate animated as well. When I did that I came to the conclusion that the circle is not perfectly round cause it was bouncing a bit.
Below you'll see the arc when it is at 100%.
And here a green overlay added with Photoshop which displays the imperfection of the circle.
Why is the arc/circle not round? Is this anti-aliasing? Beside that when I'm just redraw the arc on different places at the frame rate it still shows the bouncing effect.
The "debugging" code I use:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path path = new Path();
RectF drawBounds = new RectF(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
if(mProgress != 100) {
path.arcTo(drawBounds, mStartDegree, mProgress * 3.6f, true);
canvas.drawPath(path, mLoaderPaint);
}
if (mStartDegree >= 359)
mStartDegree = 0;
else
mStartDegree ++;
invalidate();
}
seems that arc drawing routine is somewhat broken, run my code and wait till angle is < 270 degs or less:
class V extends View implements Runnable {
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF mRect = new RectF();
private float mStartAngle;
private float mAngle;
private Xfermode mClearMmode = new PorterDuffXfermode(Mode.CLEAR);
public V(Context context) {
super(context);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mRect.set(0, 0, w, w);
getHandler().post(this);
}
#Override
protected void onDetachedFromWindow() {
Log.d(TAG, "onDetachedFromWindow ");
super.onDetachedFromWindow();
getHandler().removeCallbacks(this);
}
#Override
protected void onDraw(Canvas canvas) {
mPaint.setColor(0xff00aa00);
mPaint.setXfermode(null);
canvas.drawOval(mRect, mPaint);
mPaint.setXfermode(mClearMmode);
canvas.drawArc(mRect, mStartAngle, mAngle, true, mPaint);
}
#Override
public void run() {
mStartAngle += 3.65;
mAngle += 0.2f;
invalidate();
getHandler().postDelayed(this, 20);
}
}
the good news is that you will easily find out how to use this code to get what you want
Related
I wanna to do line number for children app
The line should look like the following
I think to use seekbar but I found it not possible as a range, not static also need to do an action when the user touches each number. so I ask for help or suggestion.
also has to do functionality of drawing an arc
the arc has to be drawn dynamically while the user moves his finger on numbers
also draw a rectangle when touching any number.
I'd like to ask for any advice or help in implementing something like that.
edit1: the line used to solve an equation as in the third image
You can use Custom View (Custom Drawing) which can help you in drawing the shaps check the link hope it helpscustom-views android-developers
There's probably a much better way to do this but here's what I came up with, hope its enough to point you in the right direction:
Activity:
public class DrawActivity extends AppCompatActivity {
float startPointX = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_draw);
MyDrawable drawing = new MyDrawable(this);
setContentView(drawing);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
MyDrawable drawing = new MyDrawable(this);
if (event.getY() > 1000) { // Click below line to reset
startPointX = 0;
} else if (startPointX == 0) { // Draw rectangle
startPointX = event.getX();
drawing = new MyDrawable(this, startPointX);
} else if (startPointX > 0) { // Draw arc
drawing = new MyDrawable(this, startPointX, event.getX());
}
setContentView(drawing);
return true;
}
}
Drawable:
public class MyDrawable extends View {
private Canvas mCanvas;
private Paint mRedPaint;
private Paint mGreenPaint;
private Paint mBluePaint;
float baseLine = 500;
float rectangleX;
float arcEnd;
public MyDrawable(Context context) {
super(context);
}
public MyDrawable(Context context, float startPoint) {
super(context);
rectangleX = startPoint;
}
public MyDrawable(Context context, float startPoint, float endPoint) {
super(context);
rectangleX = startPoint;
arcEnd = endPoint;
}
#Override
public void onDraw(Canvas canvas) {
mCanvas = canvas;
int width = 1000;
setupPaint();
canvas.drawLine(50, baseLine, width, baseLine, mBluePaint);
int mark = 50;
while(mark < width) {
canvas.drawLine(mark, baseLine-50, mark, baseLine+50, mGreenPaint);
mark += (width /10);
}
drawRectangle();
drawArc();
}
private void setupPaint() {
mRedPaint = new Paint();
mRedPaint.setARGB(255, 180, 0, 0);
mRedPaint.setStyle(Paint.Style.STROKE);
mRedPaint.setStrokeWidth(15);
mGreenPaint = new Paint();
mGreenPaint.setARGB(255, 0, 180, 0);
mGreenPaint.setStrokeWidth(15);
mBluePaint = new Paint();
mBluePaint.setARGB(255, 0, 0, 180);
mBluePaint.setStrokeWidth(30);
}
public void drawRectangle() {
if ( rectangleX > 0) {
mCanvas.drawRect(rectangleX - 25, baseLine-50, rectangleX + 25, baseLine+50, mRedPaint);
}
}
public void drawArc() {
if (arcEnd != 0 && arcEnd < rectangleX) {
mCanvas.drawArc(arcEnd, baseLine-200, rectangleX, baseLine, 0, -180, false, mRedPaint);
} else if (arcEnd != 0 && arcEnd > rectangleX) {
mCanvas.drawArc(rectangleX, baseLine-200, arcEnd, baseLine, 0, -180, false, mRedPaint);
}
}
}
I have a view and I want to draw a shape on it (circle for example) after click.
I've tried to do this but there are two problems -
onDraw is never called.
Not sure the setLayoutParams(v.getLayoutParams) will give me the result I want.
#Override
public void onClick(View v) {
CircleView circle = new CircleView(GameXoActivity.this, v.getWidth(), v.getHeight());
circle.setLayoutParams(v.getLayoutParams());
circle.startDrawing();
}
CircleView:
public CircleView(Context context, int width, int height) {
super(context);
this.width = width;
this.height = height;
}
protected void startDrawing() {
this.postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
Log.d("TAG", "onDraw");
// draw circle
}
}
}
UPDATE:
The shape is not an image and I want to draw it with animation (I didn't write the entire code).
Also, the shape is not always a circle, so using a drawable-state is not an option.
Because there is not just one view, but 9, I don't think the making 9 more on top of them would be right.
As I'm sure you'll need to customize this quite a bit, I've left things rather generic. The following example will animate a blue circle being drawn clockwise, starting from the east (0 degrees), on top of the View's content when the View is clicked.
public class CircleView extends View
{
private static final int MARGIN = 50;
Handler handler = new Handler();
Paint paint = new Paint();
RectF rect = new RectF();
boolean drawing = false;
float sweep = 0;
public CircleView(Context context)
{
this(context, null);
}
public CircleView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(15);
paint.setColor(Color.BLUE);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawArc(rect, 0, sweep, false, paint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
rect.set(MARGIN, MARGIN, w - MARGIN, h - MARGIN);
}
public void startAnimation()
{
drawing = true;
handler.post(runnable);
}
Runnable runnable = new Runnable()
{
#Override
public void run()
{
sweep += 10;
if (!(sweep > 360))
{
invalidate();
handler.postDelayed(this, 20);
}
else
{
drawing = false;
sweep = 0;
}
}
};
}
In this Activity example, I used an image that most developers would already have in their project, but it can obviously be changed to your custom image. Also, for the sake of simplicity and brevity, the CircleView is set as the entire content of the Activity, but it can easily be listed in an xml layout, as well.
public class MainActivity extends Activity
{
CircleView circle;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
circle = new CircleView(this);
circle.setBackgroundResource(R.drawable.ic_launcher);
circle.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
circle.startAnimation();
}
}
);
setContentView(circle);
}
}
I suggest, create two imageView with same dimensions, set the image you want to display on image view and then make the second image invisible .
For example :
circleimage.setVisibility(View.INVISIBLE);//now its hidden(do it OnCreate)
and then show 2ndimage when 1stimage is clicked(do it in onclick of 1st image)
circleimage.setVisibility(View.VISIBLE);
If you want to mess with drawing of the object you should overridepublic void draw(Canvas canvas) and not protected void onDraw(Canvas canvas)
EDIT:Please read comments, this first statement of my answer is probably wrong
but I would use a FrameLayout or a RelativeLayout and put the images one on top of another.
Then you can play with the visibility of the overlaying image in order to hide/show it.
EDIT:
In case your circle is not an image and needs to be drawn, make your own circle class extending View and use it as a component in the FrameLayout or RelativeLayout as you would do if it were an image
I newbie to Android Development, and Canvas drawings.
When I draw stuff on canvas, its easier for me to work on rectangle portion of width=height=1.
I try to scale according, but when trying to drawArc its draw it badly.
Can you please tell me what I'm doing wrong?
public class MyChart extends View{
private RectF dimentionRect;
private Paint dimentionPaint;
private static final String TAG = "VERBOSE";
public MyChart(Context context){
super(context);
initDrawingTools();
}
public MyChart(Context context,AttributeSet attrs) {
super(context,attrs);
initDrawingTools();
}
private void initDrawingTools(){
dimentionPaint = new Paint();
dimentionPaint.setColor(Color.GREEN);
dimentionPaint.setStyle(Style.FILL);
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenDimention = Math.min(widthSize, heightSize);
setMeasuredDimension(chosenDimention, chosenDimention);
Log.v(TAG, "onMeasure: "+chosenDimention);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.v(TAG, "onSizeChange");
}
#Override
public void onDraw(Canvas canvas){
float width = (float)getWidth();
Log.v(TAG, "onDraw: "+width);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(width, width);
dimentionRect = new RectF(0,0,1f,1f);
//canvas.drawRect(dimentionRect, dimentionPaint); //
canvas.drawArc(dimentionRect, 0, 180, true, dimentionPaint);
// canvas.drawCircle(0.5f, 0.5f, 0.3f, dimentionPaint);
canvas.restore();
}
}
Ok, I didn't find a solution but I was able to find a work-around for it.
I created a background bitmap and using it for a local Canvas which do the drawing stuff. after all drawings I draw the bitmap again into my canvas.
I know its not elegant but thats the solution I found
private void drawBackground(Canvas canvas){
if(background != null){
canvas.drawBitmap(background, 0,0,backgroundPaint);
Log.e(TAG, "Drawing background");
}
}
private void regenerateBackground(){
if(background != null){
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getWidth(), Config.ARGB_8888);
RectF rect = new RectF(0,0,getWidth(),getWidth());
Canvas c = new Canvas(background);
c.drawRect(rect, backgroundPaint);
}
#Override
protected void onDraw(Canvas canvas){
drawBackground(canvas);
float width = (float)getWidth();
Canvas c = new Canvas(background);
c.save(Canvas.MATRIX_SAVE_FLAG);
c.scale(width,width);
border = new RectF(0,0,1,1);
//
Paint p = new Paint();
p.setColor(Color.GREEN);
p.setStyle(Style.FILL);
c.drawArc(border, 0, 360,true, p);
c.restore();
}
I am using translate animation to make imageview trace a path.Initially I am just making my imageview to translate through a particular set of points but it does not.here is my code in ondraw method:
#Override
public void onDraw(Canvas canvas) {
Log.w(this.getClass().getName(),"onDraw of Balls called");
super.onDraw(canvas);
mCanvas = canvas;
canvas.drawLine(0, 240, 160, 0, mPaint);
canvas.drawLine(160, 0, 320, 240, mPaint);
mBal = (ImageView)findViewById(R.id.strike);
TranslateAnimation mTrans = new TranslateAnimation(0, 240, 160, 0);
mTrans.setDuration(6000);
mTrans.setFillEnabled(true);
mTrans.setFillAfter(true);
mTrans.start();
}
plz help.
=============================================================
Edit 1:-
This is my modified code but the translation is still not working.PLz help
#Override
public void onDraw(Canvas canvas) {
Log.w(this.getClass().getName(),"onDraw of Balls called");
BallsOnDraw(canvas);
}
void BallsOnDraw(Canvas canvas)
{
canvas.drawLine(0, 240, 160, 0, mPaint);
canvas.drawLine(160, 0, 320, 240, mPaint);
TranslateAnimation mTrans = new TranslateAnimation(0, 320, 0,240);
mTrans.setDuration(6000);
SitoliaActivity.mBal.startAnimation(mTrans);
}
You are mixing custom classes with ImageViews and animations. IMHO you should use only one of the methods. Also I don't see that you actually connect the TranslationAnimation with the ImageView itself.
So, one solution would be to use TranslateAnimation, but then you would need to call mBal.startAnimation(mTrans) instead of mTrans.start().
The other solution would be to use a custom view, override onDraw, and handle the animation yourself:
save the current time (System.curentTimeMillis)
draw the Bitmap directly to the Canvas using canvas.drawBitmap(bitmap, x, y, paint);
update the coordinates based on the elapsed time
if the animation should still run, call postInvalidateDelayed(40);
Here is an example custom view:
public class C extends View {
private static final float FROM_X = 400;
private static final float FROM_Y = 100;
private static final float TO_X = 10;
private static final float TO_Y = 400;
private static final int DURATION = 2*1000; // 2 seconds
private Bitmap mImage;
private long mStart = -1;
private Paint mPaint = new Paint();
public C(Context context) {
super(context);
mImage = ((BitmapDrawable)context.getResources().getDrawable(R.drawable.icon)).getBitmap();
}
#Override
public void onDraw(Canvas canvas) {
boolean finished = false;
if (mStart == -1) {
// Time to start the animation
mStart = SystemClock.uptimeMillis();
}
int elapsed = (int)(SystemClock.uptimeMillis() - mStart);
if (elapsed >= DURATION) {
elapsed = DURATION;
finished = true;
}
float x = FROM_X + (TO_X - FROM_X) * elapsed / DURATION;
float y = FROM_Y + (TO_Y - FROM_Y) * elapsed / DURATION;
canvas.drawBitmap(mImage, x, y, mPaint);
if (!finished) {
postInvalidateDelayed(10);
}
}
}
Can anyone help me on how to redraw the canvas. I tried many examples and source code from the internet, but it still didn't work on my PC like the invalidate func, canvas.save, canvas.restore, etc. I want to do some translation and scaling for the canvas, but when I follow the step on the internet it shows nothing. This is my source code. (I'm still new to Java/Android programming.)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
drawMaps.j=1;
resources = this.getResources();
try {
GetAttributes("path");
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
SeekBar seekBar = (SeekBar)findViewById(R.id.seekBar1);
panel = new Panel(this);
setContentView(R.layout.main);
panel.onDraw(canvas2);
ImageView image = (ImageView) findViewById(R.id.mapImage);
image.setImageBitmap(bufMaps);
}
class Panel extends View{
Paint paint = new Paint();
public Panel(Context context) {
super(context);
setFocusable(true);
}
public Bitmap quicky_XY(Bitmap bitmap,int pos_x,int pos_y){
Bitmap bufMap = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bufMap);
canvas.save();
final Paint paint = new Paint();
width = canvas.getWidth();//start
height = canvas.getHeight();//end
drawMaps.xpos = width / 30;
drawMaps.ypos = height/ 20;
paint.setStrokeWidth(0);
for (int i = 0; i < 30; i++) {
paint.setColor(Color.DKGRAY);
canvas.drawLine(drawMaps.xpos +(drawMaps.xpos*i), 0,
drawMaps.xpos +(drawMaps.xpos*i), height, paint);
//canvas.drawLine(startX, startY, stopX, stopY, paint)
}
for (int i = 0; i < 20; i++) {
paint.setColor(Color.DKGRAY);
canvas.drawLine(0, drawMaps.ypos+(drawMaps.ypos*i),
width, drawMaps.ypos+(drawMaps.ypos*i), paint);
}
canvas.translate(pos_x,pos_y);
drawMaps.addPath(canvas);
canvas.restore();
invalidate();
return bufMap;
}
#Override
public void onDraw(Canvas canvas) {
canvas.save();
bufMaps = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
bufMaps = quicky_XY(emptyBmap,positionX,positionY);
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
positionX = (int)event.getRawX();
positionY = (int)event.getRawY();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN: {
prevX = positionX;
prevY = positionY;
}
break;
case MotionEvent.ACTION_MOVE: {
final int distY = Math.abs(positionY - prevY);
final int distX = Math.abs(positionX - prevX);
if (distX > mTouchSlop || distY > mTouchSlop){
panel.getDrawingCache();
panel.invalidate();
}
Log.e("LSDEBUG", "touch X, " + positionX);
}
break;
}
return true;
}
You do not call onDraw() yourself. Instead, you call to invalidate() and it will make sure onDraw() is called as soon as the it can.
Also, if you are trying to draw on the canvas from outside the onDraw() method, you need to get a reference to the canvas.
Inside your onDraw() the canvas is not being changed. only saved (again, called on invalidate() or whenever the system needs to redraw this View):
#Override
public void onDraw(Canvas canvas) {
canvas.save();
bufMaps = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
bufMaps = quicky_XY(emptyBmap,positionX,positionY);
}
Accessing the canvas from outside the onDraw() is done using a Holder().lockCanvas() to get reference to the canvas. After drawing, you unlock it again, using unlockAndPost() and that's it.
You will also need to implement the Callback.surfaceCreated interface to find out when the Surface is available to use.
Take a look at the android reference for SurfaceHolder.
This post explains it pretty well.