在XML布局里给View设置点击事件
给一个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设置点击事件相关推荐
- DialogFragment中通过dataBinding绑定View,设置点击事件无效,通过getWindow设置dialog位置和大小无效。
1.问题描述:DialogFragment中通过dataBinding绑定View,设置点击事件无效. private val binding: CoreDialogEditContentBindin ...
- view.performClick()触发点击事件
1.主要作用 自动触发控件的点击事件 2.界面的布局文件 activity_main.xml <RelativeLayout xmlns:android="http://schema ...
- Android Span富文本设置点击事件/超链接 - URLSpan/ClickableSpan
######为文字实现很丰富的特殊效果,下面来实现 点击事件/超链接 操作 so... 直接上效果: ##使用前提条件: textView.movementMethod = LinkMovementM ...
- android点击linearlayout没反应,LinearLayout设置点击事件的问题
我给LinearLayout设置点击事件,LinearLayout中有一个ImageButton和一个TextView,为什么我点击Imagebutton无法触发点击事件,点击TextView就会触发 ...
- Android实现侧滑抽屉菜单,android studio自带的抽屉侧滑菜单怎么设置点击事件?还头一回遇到,汗!...
在你的activity,你需要inflate 你的menu folder@Override public boolean onCreateOptionsMenu(Menu menu) { MenuIn ...
- Android之BaseQuickAdapter(3.0.4版本)给子view添加点击事件(helper.addOnClickListener(view))的函数没了
1.问题 用BaseQuickAdapter给子view添加点击事件,以前都是在适配器里面这么写的helper.addOnClickListener(view),现在这个函数没了 2.解决办法 直接在 ...
- 判断自定义异形view的点击事件是否在绘制区域内
本文是摘抄自 这篇文章,此处作为记录,以备后期查阅使用.在此感谢原文作者的贡献. 使用recyclerview显示如下所示的列表,其中item是一个自定义六边形view,点击事件会有重叠情况,所以需要 ...
- 给View 添加手势,点击无反应 如何给View添加点击事件,手势方法
项目中有很多地方需要添加点击事件,重复代码很多,所以做了一个UIView的分类,专门做点击事件使用. 项目地址:UIView-Tap 代码很简单,主要有一点就是注意分类不能直接添加属性,需要用到运行时 ...
- 给EditText的drawableRight属性的图片设置点击事件
这个方法是通用的,不仅仅适用于EditText,也适用于TextView.AutoCompleteTextView等控件. Google官方API并没有给出一个直接的方法用来设置右边图片的点击事件,所 ...
最新文章
- Memcpy, blockcopy的进一步理解
- Gartner预测:SD-WAN将取代路由
- python ljust 库_Python知识精解:str ljust()方法
- orderd mysql_Oracle入门教程:leading vs ordered hint
- 创业的两种方式,你适合哪一种?
- vue中的$event
- 2.学习管理系统项目学习一
- CSS Flex-box示例代码
- rownum的用法oracle
- DevExpress GridControl使用方法
- 【转载】【SQL】Sql Server Management Studio 18 打开闪退问题
- MVC中code first方式开发,数据库的生成与更新
- Python之编写登录接口
- BACnet/IP之BACnet4j学习java代码例子属性读写同网段跨主机02
- matlab怎样编程形成软件_Matlab编程笔记之GUI程序转exe
- 手游客户端开发招聘要求
- java中的画图公式_GitHub - oneSaber/Compiler-java: 函数绘图语言java实现
- 癌症来临,应该怎么办?
- echarts K线图
- 由Asset中的double free引发的Android系统及APP崩溃问题分析