zoukankan      html  css  js  c++  java
  • Android偏移动画转圈的Button

    以前做项目碰到过一个需求,就是有5个button,这5个button要围绕一个点不停的转动,而且点击不同的button会进行相应的逻辑,比如activity的跳转等.

    就类似于gallery,但是又有所不同

    QQ截图20120422211515

    有人会首先想到用偏移动画,但是android的偏移动画只是动画,也就是说你从A点移动B点,看上去是移动过去了,但是点击的事件触发却还是在A点,实际上没有真正的

    偏移过去,只是欺骗眼睛罢了,但是在android2.2以后api提供了这样的一个方法setPosition(),这个方法的好处是你可以监听动画,假如一段动画完成了你可以动态的

    得到现在button的位置,然后set进去,这样也可以完成操作,但是简单的运动还行,如果是向我上门说的5个button不停的转动,然后每个button的动画不断的监听然后set

    会很费事,而且2.2以下的pad或者手机就没有办法安装了

    其实如果是在ios上这样是非常容易的,ios的偏移动画很像我们做flash一样,定制起始点,再定制一个终点,然后创建补间动画即可,几行代码就可以搞定,但是android上就不行

    所以基于以上,我就只能是这个思路,我就实现真正的偏移好了,对,就是用开发游戏的思路实现真正的偏移

    下面我直接上代码了,以下是舞台的代码

    我的button就相当于演员,演员要表演只能去舞台

    package com.yp.rotatebutton;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.WindowManager;
    
    public class RotateStage extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    	
    	public static final String 	TAG	= "RotateStage";
    	
    	private Context context ;
    	private SurfaceHolder holder ;
    	private Thread mThread ;
    	private Canvas mCanvas;
    	
    	private boolean isMovie = false;
    	
    	private RotateImageView mOneImageView ,mTwoImageView, mThreeImageView,mFourthImageView,mFiveImageView;
    	
    	public static double POINTX = DisplayMetrics.DENSITY_LOW ;
    	public static double POINTY = DisplayMetrics.DENSITY_HIGH ;
    	public static final  double RADIUS = 100;
    	
    	public RotateStage(Context context) {
    		super(context);
    		
    		this.context = context;
    		this.holder = this.getHolder();
    		this.holder.addCallback(this);
    		
    		DisplayMetrics dm = new DisplayMetrics();
    		((Activity) this.context).getWindowManager().getDefaultDisplay().getMetrics(dm);
    		POINTX = dm.widthPixels / 2 -  BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher).getWidth() / 2;
    		POINTY = dm.heightPixels / 2 - BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher).getHeight() / 2 ;
    		
    		Init();
    	}
    	
    	private void Init(){
    		mOneImageView = new RotateImageView(this.context,
    				R.drawable.ic_launcher, POINTX
    						+ Math.cos(0 * RotateImageView.ANGLENUM) * RADIUS,
    				POINTY + Math.sin(0 * RotateImageView.ANGLENUM) * RADIUS, 0);
    
    		mTwoImageView = new RotateImageView(this.context,
    				R.drawable.ic_launcher, POINTX
    						+ Math.cos(72 * RotateImageView.ANGLENUM) * RADIUS,
    				POINTY + Math.sin(72 * RotateImageView.ANGLENUM) * RADIUS, 72);
    
    		mThreeImageView = new RotateImageView(this.context,
    				R.drawable.ic_launcher, POINTX
    						+ Math.cos(144 * RotateImageView.ANGLENUM) * RADIUS,
    				POINTY + Math.sin(144 * RotateImageView.ANGLENUM) * RADIUS, 144);
    
    		mFourthImageView = new RotateImageView(this.context,
    				R.drawable.ic_launcher, POINTX
    						+ Math.cos(216 * RotateImageView.ANGLENUM) * RADIUS,
    				POINTY + Math.sin(216 * RotateImageView.ANGLENUM) * RADIUS, 216);
    
    		mFiveImageView = new RotateImageView(this.context,
    				R.drawable.ic_launcher, POINTX
    						+ Math.cos(288 * RotateImageView.ANGLENUM) * RADIUS,
    				POINTY + Math.sin(288 * RotateImageView.ANGLENUM) * RADIUS, 288);
    		
    	}
    	
    	@Override
    	public void surfaceCreated(SurfaceHolder holder) {
    		// TODO Auto-generated method stub
    		mThread = new Thread(this);
    		mThread.start();
    	}
    
    	@Override
    	public void surfaceChanged(SurfaceHolder holder, int format, int width,
    			int height) {
    		
    		isMovie = true ;
    		
    	}
    
    	@Override
    	public void surfaceDestroyed(SurfaceHolder holder) {
    		// TODO Auto-generated method stub
    		isMovie = false;
    	}
    
    	@Override
    	public void run() {
    		
    		while (isMovie) {
    			
    			long start = System.currentTimeMillis();
    			draw();
    			logic();
    			long end = System.currentTimeMillis();
    			if (end - start < 5) {
    				try {
    					Thread.sleep (5 - (end - start)) ;
    //					Thread.sleep(5);
    				} catch (InterruptedException e) {
    					Log.v(TAG, "run thread sleep error : " + e.toString());
    				}
    			}
    		}
    		
    	}
    	
    	
    	public void draw(){
    		try{
    			mCanvas = holder.lockCanvas() ;
    			mCanvas.drawColor(Color.RED);
    			
    			mCanvas.drawBitmap(mOneImageView.getmBgBitmap(), (float)mOneImageView.getxPix(), (float)mOneImageView.getyPix(), null);
    			mCanvas.drawBitmap(mTwoImageView.getmBgBitmap(), (float)mTwoImageView.getxPix(), (float)mTwoImageView.getyPix(), null);
    			mCanvas.drawBitmap(mThreeImageView.getmBgBitmap(), (float)mThreeImageView.getxPix(), (float)mThreeImageView.getyPix(), null);
    			mCanvas.drawBitmap(mFourthImageView.getmBgBitmap(), (float)mFourthImageView.getxPix(), (float)mFourthImageView.getyPix(), null);
    			mCanvas.drawBitmap(mFiveImageView.getmBgBitmap(), (float)mFiveImageView.getxPix(), (float)mFiveImageView.getyPix(), null);
    			
    		}catch (Exception e) {
    			
    			Log.v(TAG	, "draw error: " + e.toString());
    			
    		}finally{
    			if (mCanvas != null) holder.unlockCanvasAndPost(mCanvas) ;
    		}
    	}
    	
    	private void logic() {
    		mOneImageView.moveImageButton();
    		mTwoImageView.moveImageButton();
    		mThreeImageView.moveImageButton();
    		mFourthImageView.moveImageButton();
    		mFiveImageView.moveImageButton();
    	}
    	
    }

    舞台中的演员:RotateImageView  这个也是我自己定义的一个类,就相当于button,你要在舞台上添加几个演员你就new几个好了,我没有好的图片,所以就拿默认的icon了,讲究看吧

    package com.yp.rotatebutton;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.util.Log;
    /**
     * 
     * @author peng_yu
     *
     */
    public class RotateImageView {
    	
    	public static final String TAG = "RotateImageView";
    	
    	public static final double ANGLENUM = Math.PI/180;
    	
    	private Bitmap mBgBitmap;
    	private double angle ;
    	private double xPix;
    	private double yPix;
    	
    	public RotateImageView() {
    		super();
    	}
    	
    	public RotateImageView(Context content,int bgId,double XPix,double Ypix,double angle) {
    		BitmapFactory.Options opts = new BitmapFactory.Options();
    		opts.inJustDecodeBounds = true;
    		mBgBitmap = BitmapFactory.decodeResource(content.getResources(),bgId);
    		this.xPix = XPix;
    		this.yPix = Ypix;
    		this.angle = angle;
    		
    	}
    	
    	public double getAngle() {
    		return angle;
    	}
    
    	public void setAngle(double angle) {
    		this.angle = angle;
    	}
    	
    	public int getHeight() {
    		return mBgBitmap.getHeight();
    	}
    
    
    	public int getWidth() {
    		return mBgBitmap.getWidth();
    	}
    
    	public double getxPix() {
    		return xPix;
    	}
    
    	public void setxPix(double xPix) {
    		this.xPix = xPix;
    	}
    
    	public double getyPix() {
    		return yPix;
    	}
    
    	public void setyPix(double yPix) {
    		this.yPix = yPix;
    	}
    	
    	public Bitmap getmBgBitmap() {
    		return mBgBitmap;
    	}
    
    	public void setmBgBitmap(Bitmap mBgBitmap) {
    		this.mBgBitmap = mBgBitmap;
    	}
    	
    	public void moveImageButton(){
    		if(angle > 360){
    			angle = 0;
    		}
    		xPix = RotateStage.POINTX + Math.cos(angle*ANGLENUM)*RotateStage.RADIUS;
    		yPix = RotateStage.POINTY + Math.sin(angle*ANGLENUM)*RotateStage.RADIUS;
    		angle += 1;
    		
    	}
    
    }

    这个是我的演员(button)代码

    其实细心的朋友就已经发现了我是不断重绘每一个view的坐标,实现这种转圈的效果,所以这个demo最关键的就是怎么来算出每个button下一帧的位置

    如果你是想让一个button转圈,这个非常好实现,但是如果想让5个button沿着同一轨迹来运动画圈不那么容易,至少我是费了很多时间的

    因为表演的演员的职责,所以这样的逻辑就在它自身了

    xPix = RotateStage.POINTX + Math.cos(angle*ANGLENUM)*RotateStage.RADIUS;

    yPix = RotateStage.POINTY + Math.sin(angle*ANGLENUM)*RotateStage.RADIUS;

    angle += 1;

    这里面的RotateStage.POINTX 是我计算的屏幕中心点的位置,RotateStage.RADIUS是所绕圈的半径大小

    ANGLENUM也可以看到,实际上就是Math.PI/180

    每次运动绘制angle会+1,就会相应的计算出新的坐标,然后舞台调用它们各自的moveImageButton方法即可

    还有值得注意的是舞台中的代码:

    也就是我自定义的演员的构造方法

    mOneImageView = new RotateImageView(this.context,
                    R.drawable.ic_launcher, POINTX
                            + Math.cos(0 * RotateImageView.ANGLENUM) * RADIUS,
                    POINTY + Math.sin(0 * RotateImageView.ANGLENUM) * RADIUS, 0);

    mTwoImageView = new RotateImageView(this.context,
                    R.drawable.ic_launcher, POINTX
                            + Math.cos(72 * RotateImageView.ANGLENUM) * RADIUS,
                    POINTY + Math.sin(72 * RotateImageView.ANGLENUM) * RADIUS, 72);

    不一样的也就是0 , 72 了,这个我没有进行封装,如果你们需要自己改改就可以了,为什么是0和72呢,因为我是5个button,每一等分就是72,所以

    也就是起始的角度,比如你是0度,你就在最右边,你是72度,你就在72度角的方向上

    我这么说你们是不是已经很明白了?

    再来说说怎么样触发点击事件吧,其实也要借鉴游戏里的思路了,就是计算你当前toutch的坐标是否在button内,在哪一个button内

    这样做也有很多的算法,比如Region碰撞检测还有计算矩形的碰撞

    我用的是后一种

    @Override
    	public boolean onTouchEvent(MotionEvent event) {
    		
    		if(event.getX() > mOneImageView.getxPix() && event.getX() < mOneImageView.getxPix() + mOneImageView.getWidth()
    				  && event.getY() > mOneImageView.getyPix() && event.getY() < mOneImageView.getyPix() + mOneImageView.getHeight()	
    					){
    			
    			Toast.makeText(this.context,"mOneImageView", 1).show();
    			return false;
    		}
    		if(event.getX() > mTwoImageView.getxPix() && event.getX() < mTwoImageView.getxPix() + mTwoImageView.getWidth()
    				&& event.getY() > mTwoImageView.getyPix() && event.getY() < mTwoImageView.getyPix() + mTwoImageView.getHeight()	
    		){
    			Toast.makeText(this.context,"mTwoImageView", 1).show();
    			return false;
    		}
    		if(event.getX() > mThreeImageView.getxPix() && event.getX() < mThreeImageView.getxPix() + mThreeImageView.getWidth()
    				&& event.getY() > mThreeImageView.getyPix() && event.getY() < mThreeImageView.getyPix() + mThreeImageView.getHeight()	
    		){
    			
    			Toast.makeText(this.context,"mThreeImageView", 1).show();
    			return false;
    		}
    		if(event.getX() > mFourthImageView.getxPix() && event.getX() < mFourthImageView.getxPix() + mFourthImageView.getWidth()
    				&& event.getY() > mFourthImageView.getyPix() && event.getY() < mFourthImageView.getyPix() + mFourthImageView.getHeight()	
    		){
    			
    			Toast.makeText(this.context,"mFourthImageView", 1).show();
    			return false;
    		}
    		if(event.getX() > mFiveImageView.getxPix() && event.getX() < mFiveImageView.getxPix() + mFiveImageView.getWidth()
    				&& event.getY() > mFiveImageView.getyPix() && event.getY() < mFiveImageView.getyPix() + mFiveImageView.getHeight()	
    		){
    			Toast.makeText(this.context,"mFiveImageView", 1).show();
    			return false;
    		}
    		
    		return super.onTouchEvent(event);
    	}

    其实这样做并不好,就是每次在点击的时候都有点卡,但是要解决这个问题也很好办,但是剩下的我就不说了,自己去尝试吧

    附上我项目中的一张截图:

    image

    另外如果谁还有更好的一些办法请告诉我,一定感激不尽!

  • 相关阅读:
    python闭包&深浅拷贝&垃圾回收&with语句
    Python基本数据类型
    面向对象
    四则运算2
    周总结01
    软件工程个人作业01
    java web中乱码的种类和一些解决方式
    java web 增加信息课堂测试00
    课程00作业
    动手动脑07
  • 原文地址:https://www.cnblogs.com/jqyp/p/2465531.html
Copyright © 2011-2022 走看看