目标
在Android中到处可见接口回调机制,尤其是UI事件处理方面。熟悉回调机制之后,我们就可以利用这个机制为自定义组件创建我们自己的事件监听接口和回调方法。
例子
举一个最常见的例子button点击事件,我们通常调用button.setOnClickListener(View.OnClickListener)并在接口View.OnClickListener中实现方法onClick(),我们知道onclick()是一个回调方法,当用户点击button时,系统框架就执行这个方法。
下面我贴出了为button注册click监听事件的代码:
public class SSSS extends Activity {
private Button button;
private OnClickListener clickListener = new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
button = (Button)findViewById(R.id.button1);
button.setOnClickListener(clickListener);
}
}
onclick()回调过程
下面我们分析一下当button被点击时,系统framework找到onclick()回调方法并执行的。
首先,我们得知道button.setOnClickListener()方法其实继承自View超类,相关类图如下:
在View类中有如下源码:
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
可见:接口OnClickListener的实现类的实例clickListener被赋值给了变量getListenerInfo().mOnClickListener。
在View类中还定义了内部接口OnClickListener,源码如下:
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
此外,View类的中还定义了很多供系统framework调用的方法来响应屏幕触摸事件、键盘事件等等,例如这些方法:
public boolean onTouchEvent(MotionEvent event){}
public boolean onKeyDown(int keyCode, KeyEvent event){}
由于button的点击属于屏幕触摸事件,所以我们来分析下 onTouchEvent()的源码:
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true);
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
···············
···············
break;
case MotionEvent.ACTION_DOWN:
···········
···········
break;
case MotionEvent.ACTION_CANCEL:
·······················
break;
case MotionEvent.ACTION_MOVE:
·····················
·····················
break;
}
return true;
}
return false;
}
我们知道,一般在用户松开button时,button的响应才开始执行,从以上源码的case MotionEvent.ACTION_UP代码片段中我们能够看到如下代码片段:
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
可以推测,button的click响应应该在 performClick()方法中执行。 performClick()方法也View类中定义,源码如下:
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
return true;
}
return false;
}
前面我们提到我们自定义的OnClickListener的实现类的实例变量clickListener被赋值给了变量mOnClickListener,因此上面代码片段中的
li.mOnClickListener.onClick(this);
实际上是调用的是
clickListener.onClick(this);
也就是接口OnClickListener中的onClick()方法:
private OnClickListener clickListener = new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
};
总结
button按钮的点击事件回调过程可以简单地用如下流程表示: