Saturday, June 7, 2014

Animation follow touch path forward and backward

Further modify the example of "Change speed of Animation follow touch path" to add feature of animation direction, forward and backward.


AnimationView.java
package com.example.androidanimationalongpath;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class AnimationView extends View {
 
 Paint paint;
 
 Bitmap bm;
 int bm_offsetX, bm_offsetY;
 
 Path animPath;
 PathMeasure pathMeasure;
 float pathLength;
 
 float step;   //distance each step
 float distance;  //distance moved
 float curX, curY;
  
 float curAngle;  //current angle
 float targetAngle; //target angle
 float stepAngle; //angle each step

 float[] pos;
 float[] tan;
 
 Matrix matrix;
 
 Path touchPath;
 
 static int FORWARD = 1;
 static int BACKWARD = -1;
 int direction;

 public AnimationView(Context context) {
  super(context);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initMyView();
 }

 public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  initMyView();
 }
 
 public void initMyView(){
  paint = new Paint();
  paint.setColor(Color.BLUE);
  paint.setStrokeWidth(1);
  paint.setStyle(Paint.Style.STROKE);
    
  bm = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
  bm_offsetX = bm.getWidth()/2;
  bm_offsetY = bm.getHeight()/2;
  
  animPath = new Path();
  
  pos = new float[2];
  tan = new float[2];
  
  matrix = new Matrix();
  
  touchPath = new Path();
  
  step = 1;  //default
  stepAngle = 1; //default
  
  direction = FORWARD; //default
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if(animPath.isEmpty()){
   return;
  }
  
  canvas.drawPath(animPath, paint);
  
  matrix.reset();
  
  if((targetAngle-curAngle)>stepAngle){
   curAngle += stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else if((curAngle-targetAngle)>stepAngle){
   curAngle -= stepAngle;
   matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
   matrix.postTranslate(curX, curY);
   canvas.drawBitmap(bm, matrix, null);
   
   invalidate();
  }else{
   curAngle=targetAngle;
   
   if((direction==FORWARD && distance < pathLength)
    ||(direction==BACKWARD && distance > 0)){
    
    pathMeasure.getPosTan(distance, pos, tan);

    targetAngle = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    
    curX = pos[0]-bm_offsetX;
    curY = pos[1]-bm_offsetY;
    matrix.postTranslate(curX, curY);
    
    canvas.drawBitmap(bm, matrix, null);
    
    distance += step * direction;
    
    invalidate();
   }else{
    matrix.postRotate(curAngle, bm_offsetX, bm_offsetY);
    matrix.postTranslate(curX, curY);
    canvas.drawBitmap(bm, matrix, null);
   }
  }

 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  
  int action = event.getAction();
  
  switch(action){
  case MotionEvent.ACTION_DOWN:
   touchPath.reset();
   touchPath.moveTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_MOVE:
   touchPath.lineTo(event.getX(), event.getY());
   break;
  case MotionEvent.ACTION_UP:
   touchPath.lineTo(event.getX(), event.getY());
   animPath = new Path(touchPath);
   
   pathMeasure = new PathMeasure(animPath, false);
   pathLength = pathMeasure.getLength();
   
   //step = 1;
   distance = 0;
   curX = 0;
   curY = 0;
   
   //stepAngle = 1; 
   curAngle = 0;
   targetAngle = 0;
   
   direction = FORWARD;
   
   invalidate();
   
   break;
    
  }
  
  return true;
 }
 
 public void setSpeed(int sp){
  step = sp;
  stepAngle = sp;
 }
 
 public void replayForward(){
  direction = FORWARD;
  invalidate();
 }
 
 public void replayBackward(){
  direction = BACKWARD;
  invalidate();
 }

}

/res/layout/activity_main.xml
<LinearLayout 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:orientation="vertical"
    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="com.example.androidanimationalongpath.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        
        <Button 
            android:id="@+id/replayfor"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:text="Replay/Forward" />
        <Button 
            android:id="@+id/replayback"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:text="Replay/Backward" />
        
    </LinearLayout>

    <SeekBar
        android:id="@+id/speedbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >
        
        <com.example.androidanimationalongpath.AnimationView
            android:id="@+id/animationview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            android:background="@android:color/darker_gray" />

    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidanimationalongpath;

import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity {
 
 AnimationView animationView;
 SeekBar speedBar;
 Button btnForward, btnbackward;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  animationView = (AnimationView)findViewById(R.id.animationview);
  speedBar = (SeekBar)findViewById(R.id.speedbar);
  speedBar.setOnSeekBarChangeListener(speedBarOnSeekBarChangeListener);
  animationView.setSpeed(speedBar.getProgress()); //set default speed
  
  btnForward = (Button)findViewById(R.id.replayfor);
  btnForward.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    animationView.replayForward();
   }});
  
  btnbackward = (Button)findViewById(R.id.replayback);
  btnbackward.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    animationView.replayBackward();
   }});
 }
 
 OnSeekBarChangeListener speedBarOnSeekBarChangeListener = 
  new OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
     boolean fromUser) {
    //offset from SeekBar 0~19 to step 1~10
    animationView.setSpeed(progress);
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {}

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {}
  };

}


download filesDownload the files.

More example of Drawing Path on canvas of custom View.


No comments: