直接如题,本例效果如下图,继承自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);
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
涉及到的代码事先在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?