给一个View设置监听点击事件是再普通不过的事情,比如

    view.setOnClickListener(onClickListener);

另外一种做法是直接在XML布局里面指定View点击时候的回调方法,首先需要在Activity中编写用于回调的方法,比如

    public void onClickView(View view){// do something}

然后在XML设置View的android:onClick属性

    <Viewandroid:layout_width="match_parent"android:layout_height="match_parent"android:onClick="onClickView" />

有的时候从XML布局里直接设定点击事件会比较方便(特别是在写DEMO项目的时候),这种做法平时用的人并不多,从使用方式上大致能猜出来,View应该是在运行的时候,使用反射的方式从Activity找到“onClickView”方法并调用,因为这种做法并没有用到任何接口。

接下来,我们可以从源码中分析出View是怎么触发回调方法的。


View有5个构造方法,第一个是内部使用的,平时在Java代码中直接创建View实例用的是第二种方法,而从XML布局渲染出来的View实例最后都是要调用第五种方法。

public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {this(context);final TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);for (int i = 0; i < N; i++) {int attr = a.getIndex(i);switch (attr) {……// 处理onClick属性case R.styleable.View_onClick:if (context.isRestricted()) {throw new IllegalStateException("The android:onClick attribute cannot "+ "be used within a restricted context");}final String handlerName = a.getString(attr);if (handlerName != null) {// 给当前View实例设置一个DeclaredOnClickListener监听器setOnClickListener(new DeclaredOnClickListener(this, handlerName));}break;}}
}

处理onClick属性的时候,先判断View的Context是否isRestricted,如果是就抛出一个IllegalStateException异常。看看isRestricted方法

    /*** Indicates whether this Context is restricted.** @return {@code true} if this Context is restricted, {@code false} otherwise.** @see #CONTEXT_RESTRICTED*/public boolean isRestricted() {return false;}

isRestricted是用于判断当前的Context实例是否出于被限制的状态,按照官方的解释,处限制状态的Context,会忽略某些特点的功能,比如XML的某些属性,很明显,我们在研究的android:onClick属性也会被忽略。

a restricted context may disable specific features. For instance, a View associated with a restricted context would ignore particular XML attributes.

不过isRestricted方法是Context中为数不多的有具体实现的方法(其余基本是抽象方法),这里直接返回false,而且这个方法只有在ContextWrapper和MockContext中有重写

public class ContextWrapper extends Context {Context mBase;@Overridepublic boolean isRestricted() {return mBase.isRestricted();}
}public class MockContext extends Context {@Overridepublic boolean isRestricted() {throw new UnsupportedOperationException();}
}

ContextWrapper中也只是代理调用mBase的isRestricted,而MockContext是写单元测试的时候才会用到,所以这里的isRestricted基本只会返回false,除非使用了自定义的ContextWrapper并重写了isRestricted。
回到View,接着的final String handlerName = a.getString(attr);其实就是拿到了android:onClick="onClickView"中的“onClickView”这个字符串,接着使用了当前View的实例和“onClickView”创建了一个DeclaredOnClickListener实例,并设置为当前View的点击监听器。

/*** An implementation of OnClickListener that attempts to lazily load a* named click handling method from a parent or ancestor context.*/
private static class DeclaredOnClickListener implements OnClickListener {private final View mHostView;private final String mMethodName;private Method mMethod;public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {mHostView = hostView;mMethodName = methodName;}@Overridepublic void onClick(@NonNull View v) {if (mMethod == null) {mMethod = resolveMethod(mHostView.getContext(), mMethodName);}try {mMethod.invoke(mHostView.getContext(), v);} catch (IllegalAccessException e) {throw new IllegalStateException("Could not execute non-public method for android:onClick", e);} catch (InvocationTargetException e) {throw new IllegalStateException("Could not execute method for android:onClick", e);}}@NonNullprivate Method resolveMethod(@Nullable Context context, @NonNull String name) {while (context != null) {try {if (!context.isRestricted()) {return context.getClass().getMethod(mMethodName, View.class);}} catch (NoSuchMethodException e) {// Failed to find method, keep searching up the hierarchy.}if (context instanceof ContextWrapper) {context = ((ContextWrapper) context).getBaseContext();} else {// Can't search up the hierarchy, null out and fail.context = null;}}final int id = mHostView.getId();final String idText = id == NO_ID ? "" : " with id '"+ mHostView.getContext().getResources().getResourceEntryName(id) + "'";throw new IllegalStateException("Could not find method " + mMethodName+ "(View) in a parent or ancestor Context for android:onClick "+ "attribute defined on view " + mHostView.getClass() + idText);}
}

到这里就清楚了,当点击View的时候,DeclaredOnClickListener实例的“onClick”方法会被调用,接着会调用“resolveMethod”方法,使用反射的方式从View的Context中找一个叫“onClickView”方法,这个方法有一个View类型的参数,最后再使用反射调用该方法。要注意的是,“onClickView”方法必须是public类型的,不然反射调用时会抛出IllegalAccessException异常。

同时从源码也能看出,使用android:onClick设置点击事件的方式是从Context里面查找回调方法的,所以如果对于在Fragment的XML里创建的View,是无法通过这种方式绑定Fragment中的回调方法的,因为Fragment自身并不是一个Context,这里的View的Context其实是FragmentActivity,这也意味着使用这种方式能够快速地从Fragment中回调到FragmentActivity。

此外,从DeclaredOnClickListener类的注释也能看出android:onClick的功能,主要是起到懒加载的作用,只有到点击View的时候,才会知道哪个方法是用于点击回调的。

最后,特别需要补充说明的是,使用android:onClick给View设置点击事件,就意味着要在Activity里添加一个非接口的public方法。现在Android的开发趋势是“不要把业务逻辑写在Activity类里面”,这样做有利于项目的维护,防止Activity爆炸,所以尽量不要在Activity里出现非接口、非生命周期的public方法。因此,贸然使用android:onClick可能会“污染”Activity。

在XML布局里给View设置点击事件相关推荐

  1. DialogFragment中通过dataBinding绑定View,设置点击事件无效,通过getWindow设置dialog位置和大小无效。

    1.问题描述:DialogFragment中通过dataBinding绑定View,设置点击事件无效. private val binding: CoreDialogEditContentBindin ...

  2. view.performClick()触发点击事件

    1.主要作用 自动触发控件的点击事件 2.界面的布局文件  activity_main.xml <RelativeLayout xmlns:android="http://schema ...

  3. Android Span富文本设置点击事件/超链接 - URLSpan/ClickableSpan

    ######为文字实现很丰富的特殊效果,下面来实现 点击事件/超链接 操作 so... 直接上效果: ##使用前提条件: textView.movementMethod = LinkMovementM ...

  4. android点击linearlayout没反应,LinearLayout设置点击事件的问题

    我给LinearLayout设置点击事件,LinearLayout中有一个ImageButton和一个TextView,为什么我点击Imagebutton无法触发点击事件,点击TextView就会触发 ...

  5. Android实现侧滑抽屉菜单,android studio自带的抽屉侧滑菜单怎么设置点击事件?还头一回遇到,汗!...

    在你的activity,你需要inflate 你的menu folder@Override public boolean onCreateOptionsMenu(Menu menu) { MenuIn ...

  6. Android之BaseQuickAdapter(3.0.4版本)给子view添加点击事件(helper.addOnClickListener(view))的函数没了

    1.问题 用BaseQuickAdapter给子view添加点击事件,以前都是在适配器里面这么写的helper.addOnClickListener(view),现在这个函数没了 2.解决办法 直接在 ...

  7. 判断自定义异形view的点击事件是否在绘制区域内

    本文是摘抄自 这篇文章,此处作为记录,以备后期查阅使用.在此感谢原文作者的贡献. 使用recyclerview显示如下所示的列表,其中item是一个自定义六边形view,点击事件会有重叠情况,所以需要 ...

  8. 给View 添加手势,点击无反应 如何给View添加点击事件,手势方法

    项目中有很多地方需要添加点击事件,重复代码很多,所以做了一个UIView的分类,专门做点击事件使用. 项目地址:UIView-Tap 代码很简单,主要有一点就是注意分类不能直接添加属性,需要用到运行时 ...

  9. 给EditText的drawableRight属性的图片设置点击事件

    这个方法是通用的,不仅仅适用于EditText,也适用于TextView.AutoCompleteTextView等控件. Google官方API并没有给出一个直接的方法用来设置右边图片的点击事件,所 ...

最新文章

  1. Memcpy, blockcopy的进一步理解
  2. Gartner预测:SD-WAN将取代路由
  3. python ljust 库_Python知识精解:str ljust()方法
  4. orderd mysql_Oracle入门教程:leading vs ordered hint
  5. 创业的两种方式,你适合哪一种?
  6. vue中的$event
  7. 2.学习管理系统项目学习一
  8. CSS Flex-box示例代码
  9. rownum的用法oracle
  10. DevExpress GridControl使用方法
  11. 【转载】【SQL】Sql Server Management Studio 18 打开闪退问题
  12. MVC中code first方式开发,数据库的生成与更新
  13. Python之编写登录接口
  14. BACnet/IP之BACnet4j学习java代码例子属性读写同网段跨主机02
  15. matlab怎样编程形成软件_Matlab编程笔记之GUI程序转exe
  16. 手游客户端开发招聘要求
  17. java中的画图公式_GitHub - oneSaber/Compiler-java: 函数绘图语言java实现
  18. 癌症来临,应该怎么办?
  19. echarts K线图
  20. 由Asset中的double free引发的Android系统及APP崩溃问题分析

热门文章

  1. 基于Composer Player 模型加载和相关属性设置
  2. javap(反汇编命令)详解【转】
  3. (asp.net)百度浏览器Cookie的神奇bug
  4. DevExpress Components16.2.6 Source Code 编译
  5. 十进制 转换为 二进制
  6. 于XAML导入命名空间的代码
  7. struts tags 学习
  8. 网络工程师考试部分技术要点
  9. [原创] Neo.Geo 视频帧浏览器开发日志
  10. shell截取字符串方法