这几天一直在看《android开发艺术探索》和《android群英传》中关于自定义view的章节,结合着网上大神的一些心得分享,感觉自定义view这一块受益匪浅,这里做个心得笔记,主要记录一下view与用户交互的一些知识。
自定义view与用户交互用的最多的就是单击事件,其次的还有双击事件、长按事件、滑动事件等,所以就需要做好view的事件监听。
如果我们继承了View,也绘制好了控件,但是不重写onTouchEvent()方法的话,设置点击事件一般也是没用的,但也不是一定没用,下面先介绍一种比较简单的点击事件实现方式,也是从张鸿洋大神的一篇博客中看到的。
设置view的点击事件实现方式一:
在View的构造方法中直接setOnClickListener,如下代码:
public TouchEventTest(Context context) {
this(context, null);
}
public TouchEventTest(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchEventTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Logger.e("view内部设置OnClickListener","");
}
});
}
如上代码即可实现该自定义view的点击监听,但是有很大的局限性,首先这样设置监听很死板,不能供外部调用,其次经过测试,如果你这样写了代码,并且又重写的该View的OnTouchEvent()方法,那么不管你的onTouchEvent方法中是直接return super.onTouchEvent()还是做了任何其他逻辑,都不能触发这个监听事件,也就是说在这种情况下这种方式设置监听根本没用了,而这直接影响到了我们自定义View的扩展性,所以建议别这么用,虽然简单,但是麻烦事也多,不过这种方式也有一种适用情况,就是如果自定义View真的只需要自己内部处理逻辑,不需要外部的参与,那么这样设置也行,不过真的不推荐大家使用这种方式除非业务逻辑允许。
下面就介绍第二种给自定义view设置监听的方式,也是个人认为比较正常且通用的一种方式。
即重写onTouchEvent方法,然后判断所点击的坐标是否处于自定义view内部。如果处于自定义view内部,则回调点击监听接口,否则不予理会。
在说这种方式之前,先普及一下各种坐标知识,毕竟一会要用到。
如上面的图示:清楚的体现了各个坐标的含义。
接着,我们需要重写setOnClickListener()方法,代码如下:
@Override
public void setOnClickListener(OnClickListener l) {
mListener = l;
}
public void setOnViewClick(onViewClick click) {
this.mViewClick = click;
}
public interface onViewClick {
/**
* @param scrollX 从按下到抬起,X轴方向移动的距离
* @param scrollY 从按下到抬起,Y轴方向移动的距离
*/
void onClick(float scrollX, float scrollY);
}
然后我们重写onTouchEvent方法,如下是一个通用的设置点击事件模板方法,
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (x + getLeft() < getRight() && y + getTop() < getBottom()) {
mListener.onClick(this);
mViewClick.onClick(x - startX, y - startY);
}
break;
}
return true;
}
另外再说明一点:处理点击事件一般都是在ACTION_UP时进行,而处理滑动之类的一般都在ACTION_MOVE时进行!!!
下面我们在代码中验证一下是否有效,代码如下(布局文件就不贴了):
public class TouchEventTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_touch_event_test);
TouchEventTest testView = (TouchEventTest) findViewById(R.id.TouchEventTest);
testView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Logger.e("setOnClickListener,view.getClass():" + v.getClass() + "");
}
});
testView.setOnViewClick(new TouchEventTest.onViewClick() {
@Override
public void onClick(float scrollX, float scrollY) {
Logger.e("x轴移动了:" + scrollX + " px,y轴移动了:" + scrollY + " px", "");
}
});
}
}
最后贴出完整代码,让大家更好理解:
package com.lanma.customviewproject.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 作者 qiang_xi on 2016/8/23 15:05.
*/
public class TouchEventTest extends View {
private OnClickListener mListener;
private onViewClick mViewClick;
private int startRawX;
private int startRawY;
public TouchEventTest(Context context) {
this(context, null);
}
public TouchEventTest(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchEventTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// this.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// Logger.e("view内部设置OnClickListener", "");
// }
// });
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startRawX = rawX;
startRawY = rawY;
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (x + getLeft() < getRight() && y + getTop() < getBottom()) {
mListener.onClick(this);
mViewClick.onClick(rawX - startRawX, rawY - startRawY);
}
break;
}
return true;
}
@Override
public void setOnClickListener(OnClickListener l) {
mListener = l;
}
public void setOnViewClick(onViewClick click) {
this.mViewClick = click;
}
public interface onViewClick {
/**
* @param scrollX 从按下到抬起,X轴方向移动的距离
* @param scrollY 从按下到抬起,Y轴方向移动的距离
*/
void onClick(float scrollX, float scrollY);
}
}