首页 后端开发 XML/RSS教程 在XML布局里给View设置点击事件的案例分享

在XML布局里给View设置点击事件的案例分享

Mar 17, 2017 pm 05:25 PM

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

    view.setOnClickListener(onClickListener);
登录后复制

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

    public void onClickView(View view){
        // do something
    }
登录后复制

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

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:onClick="onClickView" />
登录后复制

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

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

在XML布局里给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中有重写
在XML布局里给View设置点击事件的案例分享

public class ContextWrapper extends Context {
    Context mBase;
    @Override
    public boolean isRestricted() {
        return mBase.isRestricted();
    }
}

public class MockContext extends Context {
    @Override
    public 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;
        }

        @Override
        public 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);
            }
        }

        @NonNull
        private 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&#39;t search up the hierarchy, null out and fail.
                    context = null;
                }
            }

            final int id = mHostView.getId();
            final String idText = id == NO_ID ? "" : " with id &#39;"
                    + mHostView.getContext().getResources().getResourceEntryName(id) + "&#39;";
            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设置点击事件的案例分享的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

layui登陆页面怎么设置跳转 layui登陆页面怎么设置跳转 Apr 04, 2024 am 03:12 AM

layui 登录页面跳转设置步骤:添加跳转代码:在登录表单提交按钮点击事件中添加判断,成功登录后通过 window.location.href 跳转到指定页面。修改 form 配置:在 lay-filter="login" 的 form 元素中添加 hidden 输入字段,name 为 "redirect",value 为目标页面地址。

vue中图片怎么添加碰事件 vue中图片怎么添加碰事件 May 02, 2024 pm 10:21 PM

如何为 Vue 中的图片添加点击事件?导入 Vue 实例。创建 Vue 实例。在 HTML 模板中添加图片。使用 v-on:click 指令添加点击事件。在 Vue 实例中定义 handleClick 方法。

鸿蒙 HarmonyOS 与 Go 语言开发 鸿蒙 HarmonyOS 与 Go 语言开发 Apr 08, 2024 pm 04:48 PM

鸿蒙HarmonyOS与Go语言开发简介鸿蒙HarmonyOS是华为开发的分布式操作系统,而Go是一种现代化的编程语言,两者的结合为开发分布式应用提供了强大的解决方案。本文将介绍如何在HarmonyOS中使用Go语言进行开发,并通过实战案例加深理解。安装与设置要使用Go语言开发HarmonyOS应用,你需要首先安装GoSDK和HarmonyOSSDK。具体步骤如下:#安装GoSDKgogetgithub.com/golang/go#设置PATH

C++ 函数在并发编程中的事件驱动机制? C++ 函数在并发编程中的事件驱动机制? Apr 26, 2024 pm 02:15 PM

并发编程中的事件驱动机制通过在事件发生时执行回调函数来响应外部事件。在C++中,事件驱动机制可用函数指针实现:函数指针可以注册回调函数,在事件发生时执行。lambda表达式也可以实现事件回调,允许创建匿名函数对象。实战案例使用函数指针实现GUI按钮点击事件,在事件发生时调用回调函数并打印消息。

JavaScript 获取网页元素详解 JavaScript 获取网页元素详解 Apr 09, 2024 pm 12:45 PM

答案:JavaScript提供了多种获取网页元素的方法,包括使用id、标签名、类名和CSS选择器。详细描述:getElementById(id):根据唯一id获取元素。getElementsByTagName(tag):获取具有指定标签名的元素组。getElementsByClassName(class):获取具有指定类名的元素组。querySelector(selector):使用CSS选择器获取第一个匹配元素。querySelectorAll(selector):使用CSS选择器获取所有匹配

js中点击事件为什么不能重复执行 js中点击事件为什么不能重复执行 May 07, 2024 pm 06:36 PM

JavaScript 中的点击事件不能重复执行,原因在于事件冒泡机制。为了解决此问题,可以采取以下措施:使用事件捕获:指定事件侦听器在事件冒泡之前触发。移交事件:使用 event.stopPropagation() 阻止事件冒泡。使用计时器:在一段时间后再次触发事件侦听器。

如何使用 PHP 函数处理 XML 数据? 如何使用 PHP 函数处理 XML 数据? May 05, 2024 am 09:15 AM

使用PHPXML函数处理XML数据:解析XML数据:simplexml_load_file()和simplexml_load_string()加载XML文件或字符串。访问XML数据:利用SimpleXML对象的属性和方法获取元素名称、属性值和子元素。修改XML数据:使用addChild()和addAttribute()方法添加新元素和属性。序列化XML数据:asXML()方法将SimpleXML对象转换为XML字符串。实战案例:解析产品馈送XML,提取产品信息,转换并将其存储到数据库中。

css中div什么意思 css中div什么意思 Apr 28, 2024 pm 02:21 PM

CSS 中的 DIV 是一个文档分隔器或容器,用途包括:分组内容、创建布局、添加样式和交互性。在 HTML 中,DIV 元素使用语法 <div></div>,其中 div 表示元素,可以添加属性和内容。DIV 是一个块级元素,在浏览器中会占据一整行。

See all articles