直接如题,本例效果如下图,继承自ImageView,后面画了个圆,圆的大小在布局文件设置,点击圆内会有事件相应,圆外无响应。
先看自定义的MyView代码
package test.bg; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; public class MyView extends ImageView implements OnGlobalLayoutListener { private int radius = 0; private RectF rectF = null; private test.bg.OnTouchListener mOnTouchListener=null; public MyView(Context context) { super(context); TypedArray ta = context.obtainStyledAttributes(R.styleable.MyView); radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0); rectF = new RectF(0, 0, 2 * radius, 2 * radius); ta.recycle(); ViewTreeObserver vto = getViewTreeObserver(); vto.addOnGlobalLayoutListener(this); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView); radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0); rectF = new RectF(0, 0, 2 * radius, 2 * radius); ta.recycle(); ViewTreeObserver vto = getViewTreeObserver(); vto.addOnGlobalLayoutListener(this); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView); radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0); rectF = new RectF(0, 0, 2 * radius, 2 * radius); ta.recycle(); ViewTreeObserver vto = getViewTreeObserver(); vto.addOnGlobalLayoutListener(this); } public void setOnTouchListener(test.bg.OnTouchListener mOnTouchListener){ this.mOnTouchListener=mOnTouchListener; } @Override protected void onDraw(Canvas canvas) { Paint p = new Paint(); p.reset(); p.setARGB(255, 255, 0, 0); canvas.drawRoundRect(rectF, radius, radius, p); super.onDraw(canvas); } @Override public void onGlobalLayout() { LayoutParams lp = getLayoutParams(); lp.height = 2 * radius; lp.width = 2 * radius; setLayoutParams(lp); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); if ((x-radius)*(x-radius)+(y-radius)*(y-radius)<radius*radius) { Log.d("nei", ""+x+":"+y); return mOnTouchListener.onTouchEvent(this, event); } else { Log.d("wai", ""+x+":"+y); return false; } } }其中TypedArray ta = context.obtainStyledAttributes(R.styleable.MyView);
涉及到的代码事先在res/valus下新建attrs.xml并加入内容
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyView"> <attr name="radius" format="dimension" /> </declare-styleable> </resources>即定义了属性和属性类型,在布局文件设置属性时需要加入xmlns:gain="http://schemas.android.com/apk/res/test.bg",其中gain是属性的前缀,test.bg是包名,完整布局文件如下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:gain="http://schemas.android.com/apk/res/test.bg" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <test.bg.MyView android:id="@+id/mv" android:layout_width="wrap_content" android:layout_height="wrap_content" gain:radius="160dp" android:src="@drawable/ic_launcher" > </test.bg.MyView> </LinearLayout>接下就可以直接用MyView了,代码如下
package test.bg; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; public class TestBgActivity extends Activity implements OnTouchListener { MyView mv; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); MyView mv = (MyView) findViewById(R.id.mv); mv.setOnTouchListener(this); } @Override public boolean onTouchEvent(View v, MotionEvent event) { if (v.getId() == R.id.mv) { Toast.makeText(this, "haha:" + v.getId() + ":" + event.getX() + ":" + event.getY(), Toast.LENGTH_SHORT).show(); } return false; } }注意到这里的OnTouchListener并不是系统的OnTouchListener,而是我们自己定义的一个,只不过名称相同而已,定义如下
package test.bg; import android.view.MotionEvent; import android.view.View; public interface OnTouchListener { public boolean onTouchEvent(View v, MotionEvent event); }为什么要自己定义OnTouchListener?因为android的传统控件都是都是以矩形区域来响应事件的,而自定义的控件可以根据自己需要的形状来响应事件,比如本例的圆形事件区响应,而不在圆内,即使是在圆的外接矩形内也不能响应,因为我们在MyView里面实现了View的onTouchEvent(MotionEvent event)方法从而进行过滤,即代码
@Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); if ((x-radius)*(x-radius)+(y-radius)*(y-radius)<radius*radius) { Log.d("nei", ""+x+":"+y); return mOnTouchListener.onTouchEvent(this, event); } else { Log.d("wai", ""+x+":"+y); return false; } }
即事件发生在圆内就触发自定义方法,圆外的直接忽略。
That's all!nice day,isn't it?