需求:在自定義 View 的 onDraw() 中繪製兩張圖片,並實現從屏幕底部移動到頂部的動畫效果。
float imgY1, imgY2)。onDraw() 中使用變量繪製:canvas.drawBitmap(bmp, x, imgY, paint)。postInvalidate() 觸發重繪。public class MovingImageView extends View {
private Bitmap bitmap1, bitmap2;
private float y1, y2;
private ValueAnimator animator;
public MovingImageView(Context context) {
super(context);
init();
}
private void init() {
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.btnpre);
// 初始位置:屏幕底部(需在 onSizeChanged 獲取高度後設置)
y1 = y2 = 0; // 稍後在 onSizeChanged 修正
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
y1 = y2 = h; // 從底部開始
startAnimation(h);
}
private void startAnimation(int screenHeight) {
animator = ValueAnimator.ofFloat(screenHeight, -bitmap1.getHeight());
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator()); // 或 AccelerateDecelerateInterpolator
animator.addUpdateListener(animation -> {
float animatedY = (float) animation.getAnimatedValue();
y1 = animatedY;
y2 = animatedY + 200; // 錯開位置
postInvalidate(); // 關鍵:請求重繪
});
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.CYAN);
canvas.drawBitmap(bitmap1, 10, y1, null);
canvas.drawBitmap(bitmap2, 100, y2, null);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (animator != null) animator.cancel();
bitmap1.recycle(); bitmap2.recycle();
}
}
| 插值器 | 效果 | 適用場景 |
|---|---|---|
| LinearInterpolator | 勻速 | 簡單移動 |
| AccelerateDecelerateInterpolator | 漸入漸出 | 自然過渡(推薦) |
| OvershootInterpolator | 超出回彈 | 彈性感 |
| PathInterpolator | 自定義貝塞爾曲線 | 複雜軌跡 |
onDraw 中解碼。onDraw 盡量不 new 對象(Paint、Rect 等提升為成員)。setLayerType(LAYER_TYPE_HARDWARE, null)。掌握「狀態驅動重繪」模式,實現流暢自定義動畫。