自定義 MyView 繪製小圖標,設置 setOnClickListener 後,**點擊螢幕任意位置都會觸發**,而非僅限圖標區域。
View 預設佔滿父容器(FrameLayout)整個區域。onDraw 僅在 (830,880) 繪製圖標,**但 View 邊界未裁剪**,點擊事件分發給整個 View 矩形區域。onTouchEvent 判斷觸摸點是否在圖標實際繪製區域內。onTouchEvent 精準判斷(推薦)class MyView extends View {
private Rect iconRect = new Rect(830, 880, 890, 980); // 圖標實際區域
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
float x = event.getX(), y = event.getY();
if (iconRect.contains((int)x, (int)y)) {
performClick(); // 觸發標準點擊流程(含 OnClickListener)
return true;
}
}
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.point);
canvas.drawBitmap(bitmap, null, iconRect, null);
}
}
在 onMeasure 中指定 View 大小為圖標尺寸,父容器用 layout_gravity 定位:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(60, 100); // 圖標寬高
}
佈局 XML:
<FrameLayout ...>
<com.example.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:layout_marginLeft="830dp"
android:layout_marginTop="880dp" />
</FrameLayout>
// 在 onClick 或 performClick 內啟動
ObjectAnimator animator = ObjectAnimator.ofFloat(myView, "translationY", 0, -20, 0);
animator.setDuration(300);
animator.setInterpolator(new BounceInterpolator());
animator.start();
// 或縮放動畫
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myView, "scaleX", 1f, 1.3f, 1f),
ObjectAnimator.ofFloat(myView, "scaleY", 1f, 1.3f, 1f)
);
set.setDuration(200).start();
onDraw 實際繪製區域。dispatchTouchEvent → onInterceptTouchEvent → onTouchEvent,重寫 onTouchEvent 返回 true 表示消費事件。OnClickListener、無障礙服務、狀態變更,優於直接調用回調。setTouchDelegate 可在不改變視覺大小前提下擴大響應區,適合小圖標。掌握觸摸分發與幾何判斷,自定義 View 交互不再「全屏生效」。