Drawing onClick in Android - android

I am trying to make a program the makes a graph using user input values from 2 EditText Fields. The program should use this input to draw a line from the center of an axis to a point specified by the information. I have a button that each time it is clicked should make a new line to the point specified (so there can be more than one line) I have created a custom view to hold the axis, but that utilizes its onDraw method, obviously, so I cant use it also to draw the new line.
Here is the code for my custom view:
package android.physicsengine;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class AxisDrawing extends View{
public AxisDrawing(Context context){
super(context);
}
public AxisDrawing(Context context, AttributeSet attrs){
super(context, attrs);
}
public AxisDrawing(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(Color.RED);
canvas.drawLine(canvas.getWidth()/2, canvas.getHeight()/2-200, canvas.getWidth()/2 ,canvas.getHeight()/2+100, linePaint);
canvas.drawLine(canvas.getWidth()/2-150, canvas.getHeight()/2-75, canvas.getWidth()/2+150 ,canvas.getHeight()/2-75, linePaint);
}
}

If your custom view class is defined in the activity, then it is an inner class of that activity and has access to variables and arrays which are defined on the activity level.
Every time the user clicks the button, you should process and store the information into these common variables or arrays which onDraw can access and from them calculate the next line or the entire graph. If your custom view is a separate class, then you need to pass the data, one way to do it is to use static varibales..
to make onDraw() method draw again your graph you need to state:
myCustomView.invalidate();
in the button click event, just after you set the new data for the graph.

You simply need to set the data in the custum view (Globally ) and call invalidate which will redraw the view.
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.util.AttributeSet;
importandroid.view.View;
#Override
protectedvoidonDraw(Canvascanvas
{
canvas.drawColor(Color.BLACK);
PaintlinePaint=newPaint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(Color.RED);
canvas.drawLine(data,data,getWidtt()-data,getHeight()-data,linePaint);
//you can also pplaceinvalidate() here which will recursively redraw the canvas in aloop
}
publicvoidsetData(intdata)
{
this.data=data;
invalidate();
}
}

Related

custom made arc shaped seekbar

What I am trying to achieve is to make an arc shaped seekbar. I know there are plenty of libraries I could use to achieve this, but I am just trying my hands on custom made views.
I have encountered couple of problems:
I have a class which extends SeekBar, and I have implemented onDraw and onMeasure methods as well, but I am not able to view that in layout editor in eclipse, here is the code for the custom view class:
package com.custom.android.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SeekBar;
import android.widget.Toast;
public class CustomSeekBar extends SeekBar {
public CustomSeekBar(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public CustomSeekBar(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);
}
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Here is my layout xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.custom.android.views.CustomSeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar"/>
</RelativeLayout>
If I use canvas class to draw an arc or any shape, would that be a good starting point?
What exactly is wrong with the eclipse adt and how could I use the onDraw method to give shape to that seekbar?
Drawing a ProgressBar with any shape, is pretty easy. With the SeekBar you have some complexity, since you have to achieve 3 diferent things:
Draw the line
Draw the draggable thumb, if you want.
Handle the user interaction
You have to think of it as an arc that is draw inside a rectangle. So point 3 could be easy: just let the user move the finger in a horizontal line, or exactly over the arc, but considering only the x coordinate of the touch event. What does this mean, in short? ok, good news: you dont have to do anything, since thats the normal behavior of the base SeekBar.
For the second point, you can choose an image for the handler, and write it in the corresponding position with a little maths. Or you can forget the handler for know, and just draw the seek bar as a line representing the full track, and another line over it representing the progress. When you have this working, if you want you can add the handler.
And for the first point, this is the main one, but its not hard to achieve. You can use this code:
UPDATE: I made some improvements in the code
public class ArcSeekBar extends SeekBar {
public ArcSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ArcSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private Paint mBasePaint;
private Paint mProgressPaint;
private RectF mOval = new RectF(5, 5, 550, 550);
private int defaultmax = 180;
private int startAngle=180;
private int strokeWidth=10;
private int trackColor=0xFF000000;
private int progressColor=0xFFFF0000;
public void setOval(RectF mOval) {
this.mOval = mOval;
}
public void setStartAngle(int startAngle) {
this.startAngle = startAngle;
}
public void setStrokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
}
public void setTrackColor(int trackColor) {
this.trackColor = trackColor;
}
public void setProgressColor(int progressColor) {
this.progressColor = progressColor;
}
public ArcSeekBar(Context context) {
super(context);
mBasePaint = new Paint();
mBasePaint.setAntiAlias(true);
mBasePaint.setColor(trackColor);
mBasePaint.setStrokeWidth(strokeWidth);
mBasePaint.setStyle(Paint.Style.STROKE);
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setColor(progressColor);
mProgressPaint.setStrokeWidth(strokeWidth);
mProgressPaint.setStyle(Paint.Style.STROKE);
setMax(defaultmax);// degrees
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawArc(mOval, startAngle, getMax(), false, mBasePaint);
canvas.drawArc(mOval, startAngle, getProgress(), false, mProgressPaint);
invalidate();
//Log.i("ARC", getProgress()+"/"+getMax());
}
}
Of course, you can and you should make everything configurable, be means of the contructor, or with some setters for the start and end angles, dimensions of the containing rectangle, stroke widths, colors, etc.
Also, note that the arc is drawn from 0 to getProgress, being this number an angle relative to the x axis, growing clocwise, so, if it go from 0 to 90 degrees, it will be something like:
Of course you can change this: canvas.drawArc get any number as an angle, and it is NOT treated as module 360, but you can do the maths and have it starting and ending in any point you want.
In my example the beggining is in the 9 of a clock, and it takes 180 degrees, to the 3 in the clock.
UPDATE
I uploaded a running example to github

Drawing using canvas

Hi Im new to Android Development and I wish to get my head around drawing with canvas.
I have several questions which the develop.android.com website doesn't make clear:
-Can you use a simple canvas for different views or do you have to create a new canvas each time you wish to add a new view?
-Are bitmaps the only image type "things" that can be drawn onto a canvas? for example in my code below I have a drawable called swirls however I cannot use this, do I have to convert it to a bitmap? And how do I do that?
package ple.kon15;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.ImageView;
public class DrawBG extends View {
Bitmap swirls = BitmapFactory.decodeResource(getResources(),R.drawable.swirls);
public DrawBG(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawBitmap(swirls, swirls.getWidth(), swirls.getHeight(),null);
}
public DrawBG(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public DrawBG(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Yes, each View has its own Canvas.
You can convert drawable resources to Bitmaps using:
BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_resource);
1- yes, every view has its own canvas but you can also have complex views that has one canvas,
2- you should decode it with BitmapFactory.decodeResource(your_context.getResources(), R.drawable.swirls); to use. And no not only bitmap, you can draw lines, vertex, rectangles, rounded rectangles, paths with canvas, and if you want to do more complex things like a game or a complex animation, you can also use SurfaceView but you should think really carefully since it's a bit more advanced and give all the handling of surcace to you

Animating the drawing of a canvas path on Android

I'd like to animate the drawing of a path, i.e. to have it progressively appear on the screen. I am using the canvas and my best guess so far is to use an ObjectAnimator to take care of the animation. However, I cannot figure out how to actually draw the corresponding segment of the path in the onDraw() method. Is there a method that would allow to do this? Would I need to involve path effects for that?
Edit: Using a DashPathEffect and setting its "on" and "off" intervals in the animation to cover the part of the path we want to draw for that step seems to work here, but it requires allocating a new DashPathEffect for every step of the animation. I will leave the question open in case there is a better way.
Answering my own question, as I figured out a satisfying way to do that.
The trick is to use an ObjectAnimator to progressively change the current length of the stroke, and a DashPathEffect to control the length of the current stroke. The DashPathEffect will have its dashes parameter initially set to the following:
float[] dashes = { 0.0f, Float.MAX_VALUE };
First float is the length of the visible stroke, second length of non-visible part. Second length is chosen to be extremely high. Initial settings thus correspond to a totally invisible stroke.
Then everytime the object animator updates the stroke length value, a new DashPathEffect is created with the new visible part and set to the Painter object, and the view is invalidated:
dashes[0] = newValue;
mPaint.setPathEffect(new DashPathEffect(dashes, 0));
invalidate();
Finally, the onDraw() method uses this painter to draw the path, which will only comprise the portion we want:
canvas.drawPath(path, mPaint);
The only drawback I see is that we must create a new DashPathEffect at every animation step (as they cannot be reused), but globally this is satisfying - the animation is nice and smooth.
package com.nexoslav.dashlineanimatedcanvasdemo;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
public class CustomView extends View {
float[] dashes = {30, 20};
Paint mPaint;
private Path mPath;
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(10f);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setPathEffect(new DashPathEffect(dashes, 0));
mPath = new Path();
mPath.moveTo(200, 200);
mPath.lineTo(300, 100);
mPath.lineTo(400, 400);
mPath.lineTo(1000, 200);
mPath.lineTo(1000, 1000);
mPath.lineTo(200, 400);
ValueAnimator animation = ValueAnimator.ofInt(0, 100);
animation.setInterpolator(new LinearInterpolator());
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Log.d("bla", "bla: " + valueAnimator.getAnimatedValue());
mPaint.setPathEffect(new DashPathEffect(dashes, (Integer) valueAnimator.getAnimatedValue()));
invalidate();
}
});
animation.setDuration(1000);
animation.setRepeatMode(ValueAnimator.RESTART);
animation.setRepeatCount(ValueAnimator.INFINITE);
animation.start();
}
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
}
}
As far as I know, the only way is to start with an empty path and have a runnable that appends points to the path at defined intervals, until it is completed.

Is it possible to change the RatingBar direction?

Does anyone knows if its possible to make the RatingBar go from right-to-Left instead of left to right, or have an idea how to do it?
You can use android:rotation="180" in your Ratingbar to rotate it
i used this way on my read-only Ratingbars, i dont know if this will effect interacting with the Ratingbar.
Use attribute android:scaleX="-1"
OR
package com.example.selection;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RatingBar;
public class InvertRatingBar extends RatingBar {
public InvertRatingBar(Context context) {
super(context);
}
public InvertRatingBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InvertRatingBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int width = this.getMeasuredWidth();
event.setLocation(width - event.getX(), event.getY());
return super.onTouchEvent(event);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
int width = this.getMeasuredWidth();
Matrix matrix = canvas.getMatrix();
matrix.preTranslate(width, 0);
matrix.preScale(-1.f, 1);
canvas.setMatrix(matrix);
super.onDraw(canvas);
}
}
You have to customize the RatingBar..
However you just have to play tricks with displaying RatingBar Images & then some sort of reverse calculations.
Trick will be displaying reverse Images in RatingBar.. swap selected and unselected images in RatingBar.
Allow user to rate on RatingBar. You have to swap selected and unselected calculations. I mean you have to do reverse calculations.
Complete Working example of custom RatingBar on StackOverflow answered by me only.
Download RatingBar icons from here : http://developer.android.com/guide/practices/ui_guidelines/icon_design.html

Simple Custom View Problem

edit 4:33pm 022111
I forgot to include my xml code. I'm so stupid.
Now I tried attrs to the super class and it still didn't help...
I tried searching for everything but no avail.
My code is very very simple.
The following includes everything I changed from the initial code I get from creating a project.
Thumbknightview.java
package com.google.www.Thumbknight;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
public class ThumbKnightView extends View{
public ThumbKnightView(Context context) {
super(context);
}
public ThumbKnightView(Context context, AttributeSet attrs) {
super(context, attrs); // edit 4:33pm 022111
}}
YoAndroid.java
package com.google.www.Thumbknight;
import android.app.Activity;
import android.os.Bundle;
public class YoAndroid extends Activity {
/** Called when the activity is first created. */
//private ThumbKnightView tnv;
float x;
float y;
float lastx;
float lasty;
float maxspeed = 8;
int ipi = -1;
int firstfinger;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}}
main.xml
I know it might look stupid for experts but for me, it gave me 3+ hours of searching and reading. Please go easy on me.
You need to specify more details - what is your problem? Also, past relevant section of your layout XML.
The code, as such, looks mostly harmless. The only thing to remember is that when you chain the constructor call to super class, pass AttributeSet as well. You will have to also pass this AttributeSet to constructors of your child views.
For. e.g: if you are creating a Button you would say new Button(activityContext,attrs);
public ThumbKnightView(Context context, AttributeSet attrs) {
super(context,attrs);
}}

Categories

Resources