Android偏移動畫(hua)-轉(zhuan)圈的Button
以前做項目(mu)碰到(dao)過一個(ge)(ge)需求,就(jiu)是有(you)5個(ge)(ge)button,這(zhe)5個(ge)(ge)button要圍繞一個(ge)(ge)點不(bu)停的轉動(dong),而且點擊不(bu)同(tong)的button會進行相應(ying)的邏輯,比如activity的跳轉等.
就類(lei)似于gallery,但是又有(you)所不同
有人(ren)會首先想到用偏(pian)(pian)移(yi)動畫(hua),但(dan)(dan)是(shi)(shi)(shi)android的偏(pian)(pian)移(yi)動畫(hua)只是(shi)(shi)(shi)動畫(hua),也(ye)就是(shi)(shi)(shi)說你從(cong)A點移(yi)動B點,看上(shang)去是(shi)(shi)(shi)移(yi)動過去了(le),但(dan)(dan)是(shi)(shi)(shi)點擊的事件觸發(fa)卻還是(shi)(shi)(shi)在A點,實際上(shang)沒有真正(zheng)的
偏移過去,只是欺(qi)騙(pian)眼睛罷了,但是在android2.2以后api提(ti)供了這樣的(de)一(yi)(yi)個方(fang)(fang)法setPosition(),這個方(fang)(fang)法的(de)好處是你(ni)可以監聽動(dong)畫,假如一(yi)(yi)段動(dong)畫完成了你(ni)可以動(dong)態(tai)的(de)
得到(dao)現(xian)在button的(de)(de)(de)位置,然(ran)后set進(jin)去(qu),這樣也(ye)可(ke)以完成操作,但是簡單的(de)(de)(de)運動還(huan)行,如果是向我上門說的(de)(de)(de)5個button不(bu)停的(de)(de)(de)轉(zhuan)動,然(ran)后每個button的(de)(de)(de)動畫不(bu)斷的(de)(de)(de)監(jian)聽然(ran)后set
會很費事,而且2.2以下的pad或者手機就沒有辦(ban)法安裝(zhuang)了
其實(shi)如果是(shi)在ios上(shang)這樣(yang)是(shi)非常容易的,ios的偏移動畫很像我們(men)做(zuo)flash一(yi)樣(yang),定制起始點,再定制一(yi)個終點,然后創建補間動畫即(ji)可,幾行代碼就可以搞定,但是(shi)android上(shang)就不行
所以(yi)(yi)基于以(yi)(yi)上,我就(jiu)只能是(shi)這個思路(lu),我就(jiu)實(shi)現真(zhen)正的偏(pian)移好了,對,就(jiu)是(shi)用開發游戲(xi)的思路(lu)實(shi)現真(zhen)正的偏(pian)移
下(xia)面我直接(jie)上代碼了,以下(xia)是舞臺的代碼
我(wo)的button就相當于演員(yuan),演員(yuan)要表演只能去(qu)舞(wu)臺(tai)
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();
}
}
舞臺中的演(yan)員:RotateImageView 這個也是我(wo)自己定義的一(yi)個類,就(jiu)相當于button,你(ni)要(yao)在舞臺上添加幾個演(yan)員你(ni)就(jiu)new幾個好了(le),我(wo)沒有好的圖片,所以(yi)就(jiu)拿默認的icon了(le),講究(jiu)看吧
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;
}
}
這個(ge)是我的演員(button)代碼
其(qi)實細心(xin)的(de)朋(peng)友就已經發現(xian)了我是(shi)不斷重(zhong)繪每一個(ge)view的(de)坐標,實現(xian)這種(zhong)轉圈(quan)的(de)效果,所以這個(ge)demo最關(guan)鍵(jian)的(de)就是(shi)怎(zen)么(me)來(lai)算(suan)出每個(ge)button下一幀的(de)位(wei)置
如(ru)果你是(shi)想讓一個button轉圈(quan),這個非常好實現,但是(shi)如(ru)果想讓5個button沿著同(tong)一軌跡來運(yun)動畫(hua)圈(quan)不那么容易,至少我是(shi)費了很(hen)多時(shi)間的(de)
因為(wei)表演的(de)演員的(de)職責(ze),所(suo)以(yi)這樣(yang)的(de)邏輯(ji)就在它(ta)自身了
xPix = RotateStage.POINTX + Math.cos(angle*ANGLENUM)*RotateStage.RADIUS;
yPix = RotateStage.POINTY + Math.sin(angle*ANGLENUM)*RotateStage.RADIUS;
angle += 1;
這里面的(de)(de)RotateStage.POINTX 是我計算的(de)(de)屏(ping)幕(mu)中心(xin)點的(de)(de)位置,RotateStage.RADIUS是所(suo)繞圈的(de)(de)半徑大小
ANGLENUM也可(ke)以看到,實(shi)際上就(jiu)是Math.PI/180
每次運動(dong)繪制angle會(hui)+1,就會(hui)相應的(de)計算(suan)出新(xin)的(de)坐標,然(ran)后舞臺調用它們各自的(de)moveImageButton方(fang)法(fa)即可
還有值得(de)注意的(de)是舞臺中的(de)代碼:
也就是我自定(ding)義(yi)的演(yan)員(yuan)的構造方法
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);
不一樣的也(ye)就是0 , 72 了,這個我沒有進(jin)行封(feng)裝,如果你(ni)們需要自己改改就可以了,為(wei)什么(me)是0和72呢,因為(wei)我是5個button,每一等分(fen)就是72,所(suo)以
也就(jiu)是起(qi)始的角度(du),比如你(ni)是0度(du),你(ni)就(jiu)在最右邊,你(ni)是72度(du),你(ni)就(jiu)在72度(du)角的方向上
我這么說你們是不是已經(jing)很明白了(le)?
再來說說怎么樣觸發點(dian)擊事件吧,其實也要借(jie)鑒(jian)游(you)戲里的(de)思路了,就是計算你當前toutch的(de)坐標(biao)是否在button內,在哪一個(ge)button內
這樣做也有很多的算法,比如(ru)Region碰撞(zhuang)檢測還有計算矩形(xing)的碰撞(zhuang)
我用的是后一種
@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);
}
其實這(zhe)樣做并不好,就是(shi)(shi)每次在點(dian)擊的時候都有點(dian)卡,但是(shi)(shi)要解決這(zhe)個問(wen)題也(ye)很好辦(ban),但是(shi)(shi)剩(sheng)下的我(wo)就不說了,自己去(qu)嘗試(shi)吧
附上(shang)我項目中(zhong)的一張截圖:
另(ling)外如果誰(shui)還有更好的一些辦法請告訴我,一定(ding)感激(ji)不(bu)盡(jin)!

