旗下导航:搜·么
当前位置:网站首页 > XML教程 > 正文

在XML布局里给View设置点击事宜的案例分享【XML教程】,XML,View,点击事件

作者:搜教程发布时间:2019-11-28分类:XML教程浏览:86评论:0


导读:给一个View设置监听点击事宜是再一般不过的事变,比方view.setOnClickListener(onClickListener);别的一种做法是直接在XM...
给一个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是怎样触发还调要领的。


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;
    @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'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设置点击事宜的案例分享的细致内容,更多请关注ki4网别的相干文章!

标签:XMLView点击事件


欢迎 发表评论: