Android 4.4TextView 在xml裏面設置onClick點擊事件沒有響應

在Android4.4也就是sdk19的手機上。TextView的OnClick點擊事件沒有響應

xml:
<TextView
        android:id="@+id/tv_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="testClick"
        android:text="@string/hello_blank_fragment" />
java:
    public void testClick(View view) {
        Log.e("zmm", "testClick--->");
    }

百思不得其解,查了大量資料,發現,TextView的clickable默認爲false,但是我們會發現之前,我們也沒有手動將clickable設置爲true,但是確可以點擊,那是因爲,我們通過setOnClickListener,會被設置成可點擊:

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

有人又說了,我也是通過 android:onClick="testClick"來設置點擊事件的,一樣可以響應。是的,在4.4之後android:onClick也會做一個操作,就是將不可點擊的修改爲可點擊的。但是4.4的時候,需要你手動寫上:android:clickable="true",寫在xml裏面的點擊事件纔可以響應。又雙有人說了。我的設備也是4.4,點擊事件設置也是在xml裏面,也沒有手動寫上android:clickable="true",點擊事件一樣響應了,那是因爲你的Activity繼承的是AppCompatActivity,看源碼,可以發現AppCompatActivity裏面會將TextView轉換成AppCompatTextView。

從AppCompatActivity.java的setContentView得到AppCompatDelegateImpl,然後調用setContentView
    public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
    }

 @NonNull
    public AppCompatDelegate getDelegate() {
        if (this.mDelegate == null) {
            this.mDelegate = AppCompatDelegate.create(this, this);
        }

        return this.mDelegate;
    }

AppCompatDelegate.java的create方法
 public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, activity.getWindow(), callback);
    }

    public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(dialog.getContext(), dialog.getWindow(), callback);
    }

    public static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) {
        return new AppCompatDelegateImpl(context, window, callback);
    }

AppCompatDelegateImpl.java的setContentView()

public void setContentView(View v) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        contentParent.addView(v);
        this.mOriginalWindowCallback.onContentChanged();
    }

    public void setContentView(int resId) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);
        this.mOriginalWindowCallback.onContentChanged();
    }

這邊通過先添加DecorView,在找到名爲content的framelayout的id,通過在這個id上添加我們的佈局:

具體加載流程請參考:  Android activity如何加載佈局

主要是LayoutInflater.createViewFromTag();

 

 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        // Apply a theme wrapper, if allowed and one is specified.
        if (!ignoreThemeAttr) {
            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();
        }

        if (name.equals(TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }

        try {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } catch (InflateException e) {
            throw e;

        } catch (ClassNotFoundException e) {
            final InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;

        } catch (Exception e) {
            final InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + name, e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        }
    }

createViewFromTag是用來創建View的,先去判斷factory2、factory這兩個東西,而我們的AppCompatDelegateImpl實現了factory2接口,所以這裏的mFactory2.onCreateView其實回調到了AppCompatDelegateImpl的onCreateView裏面。我們跟進去再看下:代碼太多,挑一些重點:

final View createView(){
  switch(var12) {
        case 0:
            view = this.createTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 1:
            view = this.createImageView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 2:
            view = this.createButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 3:
            view = this.createEditText(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 4:
            view = this.createSpinner(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 5:
            view = this.createImageButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 6:
            view = this.createCheckBox(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 7:
            view = this.createRadioButton(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 8:
            view = this.createCheckedTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 9:
            view = this.createAutoCompleteTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 10:
            view = this.createMultiAutoCompleteTextView(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 11:
            view = this.createRatingBar(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        case 12:
            view = this.createSeekBar(context, attrs);
            this.verifyNotNull((View)view, name);
            break;
        default:
            view = this.createView(context, name, attrs);
        }

        if (view == null && originalContext != context) {
            view = this.createViewFromTag(context, name, attrs);
        }

        if (view != null) {
            this.checkOnClickListener((View)view, attrs);
        }

        return (View)view;

}
    @NonNull
    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
        return new AppCompatTextView(context, attrs);
    }

可以發現,如果是TextView,就直接返回V7包中的AppCompatTextView類,所以這就是爲什麼當我們使用AppCompatActivity的時候,TextView變成AppCompatTextView的原因.

所以在4.4上你的Activity繼承AppCompatActivity,那你仍可以在xml定義點事件,不需要手動寫上:android:clickable="true"

但是,你如果繼承Activity或者FragmentActivity,那你需要再xml裏面寫上:android:clickable="true"

總結:4.4以下的,TextView最好寫上android:clickable="true",或者你的Activity繼承AppCompatActivity。

另外,在Fragment的xml裏面如果是:android:onClick="testClick",則。testClick需要定義在Fragment所在的Activity裏面。不能定義在Fragment,原因請自行百度(手動滑稽)....

每日語錄:

我其實並不孤僻,甚至可以說開朗活潑,但大多時候我很懶,懶得經營一個關係。還有一些時候就是愛自由,覺得任何一種關係都會束縛自己,當然最主要的還是知音難尋,我老覺得自己跟大多數人交往總是隻能拿出自己的一個維度,很難找到一個像我一樣的人。
—— 劉瑜《送你一顆子彈》

單曲循環《笑中有淚》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章