9 宫格圖片點阵,手指滑動連線,經過的圖片縮放動畫、繪製圓點與連線,鬆開 1 秒後自動清除軌跡。核心技術:自定義 ViewGroup 佈局 + 手勢繪製 View 疊加。
ViewGroup,負責 9 張圖片均勻佈局(onLayout、onMeasure)View,疊加在 ViewGroup 上方,負責觸摸事件、Canvas 繪製連線/圓點、縮放動畫回調
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
int row = i / 3, col = i % 3;
View v = getChildAt(i);
int left = col * cellWidth + cellWidth / BASE_NUM;
int top = row * cellWidth + cellWidth / BASE_NUM;
int right = left + cellWidth - 2 * cellWidth / BASE_NUM;
int bottom = top + cellWidth - 2 * cellWidth / BASE_NUM;
v.layout(left, top, right, bottom);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentPoint = getPointAt(event.getX(), event.getY());
if (currentPoint != null) {
selectPoint(currentPoint);
drawCircle(currentPoint);
}
break;
case MotionEvent.ACTION_MOVE:
clearAndRedraw(); // 重繪已確認軌跡
LYJGesturePoint next = getPointAt(event.getX(), event.getY());
if (next != null && next != currentPoint && !next.isSelected()) {
// 移動到新點:畫線、標記、動畫
drawLine(currentPoint, next);
selectPoint(next);
animationCallback.startAnimation(next.getIndex());
currentPoint = next;
} else {
// 移動到空白/同點:畫臨時線到手指位置
drawTempLine(currentPoint, event.getX(), event.getY());
}
break;
case MotionEvent.ACTION_UP:
clearAndRedraw(); // 移除臨時線
new Handler().postDelayed(this::clearAll, 1000); // 1秒後清空
break;
}
invalidate();
return true;
}
// 建構子建立 Bitmap + Canvas
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas();
canvas.setBitmap(bitmap);
// 繪製邏輯寫在 onTouchEvent,僅操作離屏 Canvas
// onDraw 僅負責將 Bitmap 畫到螢幕
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
public interface OnAnimationCallback {
void startAnimationImage(int index);
}
// ViewGroup 實作
@Override
public void startAnimationImage(int index) {
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.scale_anim);
getChildAt(index).startAnimation(anim);
}
| 技術點 | 實作要點 |
|---|---|
| 點擊判定 | 遍歷 9 個矩形區域,x in [left,right) && y in [top,bottom) |
| 軌跡保存 | 兩個列表:lineList (PaircirclePoints |
| 重繪策略 | 每次 canvas.drawColor(CLEAR) 後遍歷列表重畫 |
| 離屏緩衝 | Bitmap + Canvas 避免 onDraw 重複計算,效能大幅提升 |
| 動畫解耦 | 介面回調讓 ViewGroup 控制子 View 動畫 |
cellWidth)Vibrator)Path 替代逐段 drawLine,支援平滑曲線百度紅包界面看似複雜,實則是「佈局容器 + 手勢繪製層 + 狀態機」的經典組合。掌握 ViewGroup.onLayout、Canvas 雙緩衝、觸摸事件分發 三大核心,即可舉一反三實現手勢密碼、連連看、軌跡繪圖等多種互動效果。