以前做項目碰到過一個需求,就是有5個button,這5個button要圍繞一個點不停的轉動,而且點擊不同的button會進行相應的邏輯,比如activity的跳轉等.
就類似於gallery,但是又有所不同
有人會首先想到用偏移動畫,但是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); }
其實這樣做並不好,就是每次在點擊的時候都有點卡,但是要解決這個問題也很好辦,但是剩下的我就不說了,自己去嘗試吧
附上我項目中的一張截圖:
另外如果誰還有更好的一些辦法請告訴我,一定感激不盡!