編輯:關於Android編程
先來看看效果圖
跳動的小球做這個動畫,需掌握:
1、屬性動畫
2、Path類、Canvas類
3、貝塞爾曲線
4、SurfaceView用法
5、自定義attr屬性
6 、架構: 狀態模式,控制器
7 、自由落體,拋物線等概念
不多說了,直接上碼
1.DancingView.java
public class DancingView extends SurfaceView implements SurfaceHolder.Callback { public static final int STATE_DOWN = 1;//向下狀態 public static final int STATE_UP = 2;//向上狀態 public static final int DEFAULT_POINT_RADIUS = 10; public static final int DEFAULT_BALL_RADIUS = 13; public static final int DEFAULT_LINE_WIDTH = 200; public static final int DEFAULT_LINE_HEIGHT = 2; public static final int DEFAULT_LINE_COLOR = Color.parseColor("#FF9800"); public static final int DEFAULT_POINT_COLOR = Color.parseColor("#9C27B0"); public static final int DEFAULT_BALL_COLOR = Color.parseColor("#FF4081"); public static final int DEFAULT_DOWN_DURATION = 600;//ms public static final int DEFAULT_UP_DURATION = 600;//ms public static final int DEFAULT_FREEDOWN_DURATION = 1000;//ms public static final int MAX_OFFSET_Y = 50;//水平下降最大偏移距離 public int PONIT_RADIUS = DEFAULT_POINT_RADIUS;//小球半徑 public int BALL_RADIUS = DEFAULT_BALL_RADIUS;//小球半徑 private Paint mPaint; private Path mPath; private int mLineColor; private int mPonitColor; private int mBallColor; private int mLineWidth; private int mLineHeight; private float mDownDistance; private float mUpDistance; private float freeBallDistance; private ValueAnimator mDownController;//下落控制器 private ValueAnimator mUpController;//上彈控制器 private ValueAnimator mFreeDownController;//自由落體控制器 private AnimatorSet animatorSet; private int state; private boolean ismUpControllerDied = false; private boolean isAnimationShowing = false; private boolean isBounced = false; private boolean isBallFreeUp = false; public DancingView(Context context) { super(context); init(context, null); } public DancingView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public DancingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { initAttributes(context, attrs); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(mLineHeight); mPaint.setStrokeCap(Paint.Cap.ROUND); mPath = new Path(); getHolder().addCallback(this); initController(); } private void initAttributes(Context context, AttributeSet attrs) { TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.DancingView); mLineColor = typeArray.getColor(R.styleable.DancingView_lineColor, DEFAULT_LINE_COLOR); mLineWidth = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineWidth, DEFAULT_LINE_WIDTH); mLineHeight = typeArray.getDimensionPixelOffset(R.styleable.DancingView_lineHeight, DEFAULT_LINE_HEIGHT); mPonitColor = typeArray.getColor(R.styleable.DancingView_pointColor, DEFAULT_POINT_COLOR); mBallColor = typeArray.getColor(R.styleable.DancingView_ballColor, DEFAULT_BALL_COLOR); typeArray.recycle(); } private void initController() { mDownController = ValueAnimator.ofFloat(0, 1); mDownController.setDuration(DEFAULT_DOWN_DURATION); mDownController.setInterpolator(new DecelerateInterpolator()); mDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDownDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue(); postInvalidate(); } }); mDownController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { state = STATE_DOWN; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mUpController = ValueAnimator.ofFloat(0, 1); mUpController.setDuration(DEFAULT_UP_DURATION); mUpController.setInterpolator(new DancingInterpolator()); mUpController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mUpDistance = MAX_OFFSET_Y * (float) animation.getAnimatedValue(); if (mUpDistance >= MAX_OFFSET_Y) { //進入自由落體狀態 isBounced = true; if (!mFreeDownController.isRunning() && !mFreeDownController.isStarted() && !isBallFreeUp) { mFreeDownController.start(); } } postInvalidate(); } }); mUpController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { state = STATE_UP; } @Override public void onAnimationEnd(Animator animation) { ismUpControllerDied = true; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mFreeDownController = ValueAnimator.ofFloat(0, 8f); mFreeDownController.setDuration(DEFAULT_FREEDOWN_DURATION); mFreeDownController.setInterpolator(new DecelerateInterpolator()); mFreeDownController.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //該公式解決上升減速 和 下降加速 float t = (float) animation.getAnimatedValue(); freeBallDistance = 40 * t - 5 * t * t; if (ismUpControllerDied) {//往上拋,到臨界點 postInvalidate(); } } }); mFreeDownController.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isBallFreeUp = true; } @Override public void onAnimationEnd(Animator animation) { isAnimationShowing = false; //循環第二次 startAnimations(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animatorSet = new AnimatorSet(); animatorSet.play(mDownController).before(mUpController); animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isAnimationShowing = true; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } /** * 啟動動畫,外部調用 */ public void startAnimations() { if (isAnimationShowing) { return; } if (animatorSet.isRunning()) { animatorSet.end(); animatorSet.cancel(); } isBounced = false; isBallFreeUp = false; ismUpControllerDied = false; animatorSet.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 一條繩子用左右兩部分的二階貝塞爾曲線組成 mPaint.setColor(mLineColor); mPath.reset(); //起始點 mPath.moveTo(getWidth() / 2 - mLineWidth / 2, getHeight() / 2); if (state == STATE_DOWN) {//下落 /**************繪制繩子開始*************/ //左部分 的貝塞爾 mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2, getHeight() / 2 + mDownDistance); //右部分 的貝塞爾 mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + mDownDistance, getWidth() / 2 + mLineWidth / 2, getHeight() / 2); mPaint.setStyle(Paint.Style.STROKE); canvas.drawPath(mPath, mPaint); /**************繪制繩子結束*************/ /**************繪制彈跳小球開始*************/ mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mBallColor); canvas.drawCircle(getWidth() / 2, getHeight() / 2 + mDownDistance - BALL_RADIUS, BALL_RADIUS, mPaint); /**************繪制彈跳小球結束*************/ } else if (state == STATE_UP) { //向上彈 /**************繪制繩子開始*************/ //左部分的貝塞爾 mPath.quadTo((float) (getWidth() / 2 - mLineWidth / 2 + mLineWidth * 0.375), getHeight() / 2 + 50 - mUpDistance, getWidth() / 2, getHeight() / 2 + (50 - mUpDistance)); //右部分的貝塞爾 mPath.quadTo((float) (getWidth() / 2 + mLineWidth / 2 - mLineWidth * 0.375), getHeight() / 2 + 50 - mUpDistance, getWidth() / 2 + mLineWidth / 2, getHeight() / 2); mPaint.setStyle(Paint.Style.STROKE); canvas.drawPath(mPath, mPaint); /**************繪制繩子結束*************/ mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mBallColor); //彈性小球,自由落體 if (!isBounced) { //上升 canvas.drawCircle(getWidth() / 2, getHeight() / 2 + (MAX_OFFSET_Y - mUpDistance) - BALL_RADIUS, BALL_RADIUS, mPaint); } else { //自由落體 canvas.drawCircle(getWidth() / 2, getHeight() / 2 - freeBallDistance - BALL_RADIUS, BALL_RADIUS, mPaint); } } mPaint.setColor(mPonitColor); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(getWidth() / 2 - mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint); canvas.drawCircle(getWidth() / 2 + mLineWidth / 2, getHeight() / 2, PONIT_RADIUS, mPaint); } @Override public void surfaceCreated(SurfaceHolder holder) { Canvas canvas = holder.lockCanvas();//鎖定整個SurfaceView對象,獲取該Surface上的Canvas. draw(canvas); holder.unlockCanvasAndPost(canvas);//釋放畫布,提交修改 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }
2.DancingInterpolator.java
public class DancingInterpolator implements Interpolator { @Override public float getInterpolation(float input) { return (float) (1 - Math.exp(-3 * input) * Math.cos(10 * input)); } }
3.自定義屬性 styles.xml
<declare-styleable name="DancingView"> <attr name="lineWidth" format="dimension" /> <attr name="lineHeight" format="dimension" /> <attr name="pointColor" format="reference|color" /> <attr name="lineColor" format="reference|color" /> <attr name="ballColor" format="reference|color" /> </declare-styleable>
注意:顏色、尺寸、參數可以自己測試,調整。
以上就是本文的全部內容,希望對大家的學習和工作能有所幫助哦。
android給我們提供了一個spinner控件,這個控件主要就是一個列表,那麼我們就來說說這個控件吧,這個控件在以前的也看見過,但今天還是從新介紹一遍吧。Spinner
概述Android的消息機制主要值得就是Handler的運行機制,Handler的運行需要底層的MessageQueue和Looper的支撐。MessageQueue即為
主要思想:將一個view設計成多層:背景層,含中獎信息等;遮蓋層,用於刮獎,使用關聯一個Bitmap的Canvas在該Bitmap上,使用它的canvas.drawPat
本文實例為大家分享了打飛機游戲BOSS以及勝利失敗頁面設計的Android代碼,具體內容如下修改子彈類:public class Bullet { //子彈圖片資源 pu