I want to develop my application with a simple animation. I have used a lot of source from: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/Arcs.html
My code:
public class Animation extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private static class AnimView extends View {
private Paint myPaint;
private Paint myFramePaint;
private RectF bigOval;
private float myStart;
private float mySweep;
private static final float SWEEP_INC = 1;
public AnimView(Context context) {
super(context);
init();
}
public AnimView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
myPaint = new Paint();
myPaint.setAntiAlias(true);
myPaint.setStyle(Paint.Style.FILL);
myPaint.setColor(Color.RED);
bigOval = new RectF(40, 10, 280, 250);
myFramePaint = new Paint();
myFramePaint.setAntiAlias(true);
myFramePaint.setColor(Color.BLACK);
}
private void drawArcs(Canvas canvas, RectF oval, boolean useCenter, Paint paint) {
canvas.drawRect(oval, myFramePaint);
canvas.drawArc(oval, myStart, mySweep, useCenter, paint);
}
#Override
protected void onDraw(Canvas canvas) {
drawArcs(canvas, bigOval, true, myPaint);
myStart = -90;
mySweep -= SWEEP_INC;
invalidate();
}
}}
I'm using my view in this way in my xml file:
<view
class="go.android.Animation$AnimView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
It works properly. I know that it isn't custom animation. But is this possible to set speed of this animation (in ms or seconds)? Or to stop this animation (for example in the OnClick or OnTouch Listener), and get to know when this animation has finished?
I also want to get first whole circle, and on the end of animation - lack of circle. Simply to change direction of this animation. Is this possible?
I don't want to use frame-by-frame animation. I want to get continuous animation. Is there any possibilities to get similar animation (with setting speed, etc...)
I also want to animate not only a color but rather a round drawable.
Thank you in advance. Sorry for my English skill.
There might be better and more precise ways to control your animation than this (you could look into using OpenGL), but you can change the speed and direction of the animation relatively easily given your existing code.
The speed is controlled by the SWEEP_INC field (which stands for Sweep Increment I would guess). Every time the onDraw() method is called, it increases the size of the wedge by 1 degree. If you want the animation to go 5 times as fast, set SWEEP_INC to 5 instead of 1. If you want the animation to go half as fast, set SWEEP_INC to 0.5.
You can also just set a flag to reverse the animation each time it finishes. I've modified your code to reverse each time it reaches the end, using the addToCircle boolean.
public class Animation extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private static class AnimView extends View {
private Paint myPaint;
private Paint myFramePaint;
private RectF bigOval;
private float myStart;
private float mySweep;
private float SWEEP_INC = 1;
//Use this flag to control the direction of the arc's movement
private boolean addToCircle = true;
public AnimView(Context context) {
super(context);
init();
}
public AnimView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
myPaint = new Paint();
myPaint.setAntiAlias(true);
myPaint.setStyle(Paint.Style.FILL);
myPaint.setColor(Color.RED);
bigOval = new RectF(40, 10, 280, 250);
myFramePaint = new Paint();
myFramePaint.setAntiAlias(true);
myFramePaint.setColor(Color.BLACK);
}
private void drawArcs(Canvas canvas, RectF oval, boolean useCenter, Paint paint) {
canvas.drawRect(oval, myFramePaint);
canvas.drawArc(oval, myStart, mySweep, useCenter, paint);
}
public void setIncrement(float newIncrement)
{
SWEEP_INC = newIncrement;
}
#Override
protected void onDraw(Canvas canvas) {
drawArcs(canvas, bigOval, true, myPaint);
myStart = -90;
//If the arc is currently getting bigger, decrease the value of mySweep
if(addToCircle)
{
mySweep -= SWEEP_INC;
}
//If the arc is currently getting smaller, increase the value of mySweep
else
{
mySweep += SWEEP_INC;
}
//If the animation has reached the end, reverse it
if(mySweep%360 == 0)
{
addToCircle = !addToCircle;
}
invalidate();
}
}
}
EDIT
If you remove the final and static modifiers from SWEEP_INC you can change the speed of the animation at runtime using the setIncrement(float newIncrement) function I've added.
You can stop the animation by calling setIncrement(0).
However, I don't think there is a constant rate at which onDraw is called. So I don't know of any way to assign a duration in seconds to this animation. For that you may want to look into more sophisticated methods of animation.
Related
I'm trying to make a small pong game, using a custom view that draws the platforms on each side and the ball as a circle. However when i run the app to see if the drawing is done correctly, while the platforms are drawn correctly the circle wont appear. so my question is, why is the ball not drawn at all?
This is the custom view:
public class GameView extends SurfaceView implements Runnable {
private Context mContext; // a private instance of the context
Thread gameThread=null; //the thread on which the game will run
Ball ball; //a ball
Platform lPlat,rPlat; // a platform for the left side and the right
//tools that will draw the view and the objects
Canvas canvas;
Paint paint;
Color color;
SurfaceHolder mHolder;
//variable for frame management and objects movement
long lastFrame;
long fps;
volatile boolean playing;
public GameView(Context context){
super(context);
this.mContext=context;
mHolder=getHolder();
color=new Color();
paint=new Paint();
gameThread=new Thread(this);
lPlat=new Platform(2592,620,50,200);
rPlat=new Platform(120,620,50,200);
ball=new Ball(2400,620,20);
setWillNotDraw(false);
init();
}
public void init() {
render();
playing=true;
gameThread.start();
}
//the game Thread
#Override
public void run(){
while (playing){
long currentTime=System.currentTimeMillis();
render();
lastFrame=System.currentTimeMillis()-currentTime;
//dividing the time difference between the start of the count and the end of the render (which is a frame) by 1000 to get an approximation of the fps;
if (lastFrame>=1)
fps=1000/lastFrame;
}
}
//renders the view, calling update method, and draw method afterwards
public void render(){
update();
draw();
}
public void update(){
ball.updateBallPosition();
rPlat.updatePlatformPosition();
lPlat.updatePlatformPosition();
}
#Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
Log.i("#CanvasStatus","Canvas onDraw");
canvas.drawColor(Color.argb(255, 100, 120, 70));
//the drawRect methods work properly
canvas.drawRect(lPlat.shape,paint);
canvas.drawRect(rPlat.shape,paint);
canvas.drawCircle(ball.x,ball.y,ball.radius,paint); //this is where I try to draw the circle
}
public void draw(){
if (mHolder.getSurface().isValid()) {
Log.i("#CanvasStatus","valid");
canvas=mHolder.lockCanvas();
paint.setColor(Color.argb(255,10,200,157));
paint.setStyle(Paint.Style.FILL);
this.draw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
else Log.i("#CanvasStatus","invalid");
}
}
and this is the ball class:
public class Ball {
float x;
float y;
float radius;
double yspeed, xspeed;
public Ball(float x, float y, float radius) {
this.x = x;
this.y = y;
this.radius = radius;
yspeed = 200;
xspeed = 200;
}
public void updateBallPosition() {
x+=xspeed;
y+=yspeed;
//checks for wall collisions
if(x>=GameActivity.screenCoordinates.x) {
x = GameActivity.screenCoordinates.x;
xspeed=-xspeed;
}
else if (x<=0){
x=0;
xspeed=-xspeed;
}
}
}
Not everything is implemented in regards to the game, i'm just trying to get the canvas to draw the objects correctly at the moment.
Thanks in advance
You don't need to override onDraw method and call draw(canvas) in your draw method, all the work with canvas should be done there. Remove onDraw method and change draw method to look like this:
public void draw(){
if (mHolder.getSurface().isValid()) {
Log.i("#CanvasStatus","valid");
canvas=mHolder.lockCanvas();
paint.setColor(Color.argb(255,10,200,157));
paint.setStyle(Paint.Style.FILL);
canvas.drawColor(Color.argb(255, 100, 120, 70));
//the drawRect methods work properly
canvas.drawRect(lPlat.shape,paint);
canvas.drawRect(rPlat.shape,paint);
canvas.drawCircle(ball.x,ball.y,ball.radius,paint);
mHolder.unlockCanvasAndPost(canvas);
}
else Log.i("#CanvasStatus","invalid");
}
By the way, you've set up boundaries for x coordinate only, you should add it for y as well. Besides that, you will possibly need to add some delay to canvas drawing (with Thread.sleep in a simplest case) or else your view will re-draw too often.
I am trying to draw a single line in Android using canvas
My class :
public class LineDrawer extends View {
public LineDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
float left = 20;
float top = 20;
float right = 50;
float bottom = 100;
canvas.drawLine(left, top, right, bottom, paint);
}
}
My Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LineDrawer lineDrawer = new LineDrawer(this);
setContentView(R.layout.activity_Main);
}
}
I cannot find where is the problem , I try all the solutions in the internet but nothing happen , still a blank activity..
Should I import some code ?
lineDrawer is created but not added anywhere. Just creating a view is not enough, you need to add it to the current displayed views to be taken into account and drawn. You have two options:
Add it to your XML layout. You will have to add the following constructor to your custom view.
public LineDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
Use addView(). Anyway, given how simple is your example, I'll use first (common) method.
As an additional comment, the Paint paint object should be created on view initialization, as is a costly operation. See in the original documentation for more information about this.
In my application,I am setting an image in my imageview. I just need to place a marker on that imageview. I am doing it with onDraw function in my custom image view class.The problem is,for example if I take x position and y position as 40 respectively.The marker position shown above image in my mobile is different when compared with running same application on tablet.
I want a solution such that when I give coordinates then the position of marker on image in mobile and tablet appears same.
Here is the code my main activity:
public class PointOnImageActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_point_on_image);
CustomImag ev= new CustomImag(this);
ev.setBackgroundResource(R.drawable.stad);// set background
setContentView(ev);
}
}
Here is the code of my custom imageview class:
public class CustomImag extends ImageView
{
public CustomImag(Context context)
{
super(context);
}
#Override
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
canvas.drawCircle(40,40,10,mPaint);
}
}
that's because 40 is pixel value, you actually need 40dp instead. so you can convert 40dp to px value dynamically, like this:
int pxValue1 = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, context.getResources()
.getDisplayMetrics());
int pxValue2 = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources()
.getDisplayMetrics());
canvas.drawCircle(pxValue,pxValue,pxValue2,mPaint);
You have to position your custom View according to the screen dimensions. This way, no matter what size screen displays your app, your positioning will be relative.
In your onDraw(Canvas) method, you can get the screen dimensions and draw the circle using them. In the following example(which is mostly your code), I place the red circle at screen_width / 4 and screen_height / 4:
public class MyCustomImageViewActivity extends Activity{
CustomImag ev;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ev = new CustomImag(this);
ev.setBackgroundResource(R.drawable.stad);// set background
setContentView(ev);
}
public class CustomImag extends ImageView {
Paint mPaint;
public CustomImag(Context context) {
super(context);
mPaint = new Paint();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
mPaint.setColor(Color.RED);
canvas.drawCircle(point.x / 4, point.y / 4, 10, mPaint);
}
}
}
OnDraw function for my custom View is being called infinitely and is looping !! What could be possible reason??
Here is my custom view:-
public class Balls extends View{
private static final String TAG = "BallsView";
private int mMode = READY;
public static final int PAUSE = 0;
public static final int READY = 1;
public static final int RUNNING = 2;
public static final int LOSE = 3;
private final Paint mPaint = new Paint();
private double mUx = 0.1;
private double mUy = 2;
private double mVy;
private double mVx;
private double mSx;
private double mSy;
private double mRange;
private float mX1;
private float mY1;
private int mX2;
private int mY2;
private int mDx;
private int mDy;
Time t;
float mAngle;
private final double mGravity = -9.8;
private long mLastTime;
private double mT;
private Canvas mCanvas = null;
public Balls(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
setFocusable(true);
setWillNotDraw(false);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(10);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Cap.ROUND);
//mPaint.setColor(0xff00ffff);
mPaint.setARGB(255, 0, 255, 0);
mLastTime = System.currentTimeMillis();
}
public Balls(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setWillNotDraw(false);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(10);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Cap.ROUND);
mPaint.setColor(0xff00ffff);
}
#Override
public void onDraw(Canvas canvas) {
Log.w(this.getClass().getName(),"onDraw of Balls called");
super.onDraw(canvas);
mCanvas = canvas;
if(mCanvas!= null)
Log.w(this.getClass().getName(),"Canvas is not null");
}
This view is inflated as follows in another activity:-
mBalls = (Balls) findViewById(R.id.balls);
This view is placed in xml file inside a relative view and the relative view is the child of horizontal scroll view.
onDraw(Canvas) called too often like in an infinite loop, is not normal.
normally it should be called 1 to 3 times, if there is no following invalidate or layout changes.
reasons for infinite loop maybe:
1, you called invalidate or postInvalidate some where.
2, parent or sibling layout is changing all the time.
3, View.LAYER_TYPE_SOFTWARE is used, es. setLayerType(View.LAYER_TYPE_SOFTWARE, null).
it is notable that LAYER_TYPE_SOFTWARE will cause onDraw() to be called, like in a loop.
There isn't an infinite loop here. What is going on is the OS is redrawing your activity as fast as possible. When your activity gets redrawn it redraws all of its children views. As your code has very little computation, from what I can see here, it is running very fast and is probably redrawing at a rate >30 FPS. Your log message makes it appear as if there is an infinite loop when there isn't. In fact there isn't even a loop inside your onDraw method.
To illustrate what is going on try this. Add a protected member drawCount to your Balls class and set it to 0:
protect int drawCount = 0;
Then append drawCount to the end of your onDraw log message
public void onDraw(Canvas canvas){
drawCount++;
Log.w(this.getClass().getName(),"onDraw of Balls called. Total draws:" + Integer.toString(drawCount));
...
}
What you should see is each log message will display a different drawCount.
If you want to get fancy and calculate the framerate of your app you could measure the time since the first draw and then divide the drawCount by how much time has passed which would give you a estimate of your activities framerate.
onDraw() get's call at invalidate. Invalidate() get's called when the view or it's parent feel the need to change and have to change it's state.
For me that was happening because i had some code that was modifying UI inside overridden DispatchDraw..
I have the following class:
public class MainActivity extends Activity {
/** Called when the activity is first created. */
String value = "0";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new GaugeAnimation(this));
}
}
My GuageAnimation class is as follows:
public class GaugeAnimation extends View{
private Path p;
private Paint cPaint = new Paint();
private int width = 200;
private int angleStart = 135;
private int sweep = 90;
private int value=0;
Bitmap bottom = BitmapFactory.decodeResource(getResources(), R.drawable.dashboard_rpm_bottom);
Bitmap top= BitmapFactory.decodeResource(getResources(), R.drawable.dashboard_rpm_active);
public GaugeAnimation(Context context){
super (context);
//Arc Equations & etc....
}
#Override
public void onDraw(Canvas c){
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setColor(Color.BLUE);
c.translate(55,320);
//Draw bottom image on canvas
c.save();
p.addArc(new RectF(0,0,width,width), angleStart, sweep);
p.lineTo(width/2, width/2);
c.clipPath(p);
//draw Image on top
c.restore();
invalidate()
}
}
so basically this crops a circular image one piece at a time based on arc equations. I want to show an animation like the circle's pieces all being filled in (so showing each clipped path which demonstrates a circle being built). So i was thinking of doing a for loop like:
for (int i=0; i<100; i++) {
sweep=i;
p.addArc(new RectF(0,0,width,width), angleStart, sweep);
p.lineTo(width/2, width/2);
c.clipPath(p);
invalidate();
}
but this doesn't work it just draws the end result when i=100, anyone have an idea as to how i can do this?
Currently your loop is performed on the main thread and keeps it busy, so it will not actually update the UI until you finish.
You should do the loop in a background thread (using Timer or AsyncTask) and perform the paint on main thread. Note that without a short sleep, it will probably would look like an animation too much, it will be pretty fast.