Android 4.4TextView setting onClick click event in xml does not respond

On Android4.4, that is, sdk19 mobile phones. TextView's OnClick click event does not respond

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--->");
    }

I was puzzled, checked a lot of information, and found that the clickable of TextView is false by default, but we will find that before, we did not manually set the clickable to true, but it can be clicked, that is because we pass setOnClickListener, will is set to be clickable:

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

Someone said again, I also set the click event through android:onClick="testClick", and it can also respond. Yes, android:onClick will also do an operation after 4.4, which is to modify the non-clickable to clickable. But in 4.4, you need to manually write: android:clickable="true", the click event written in xml can respond. It was said again. My device is also 4.4, and the click event setting is also in xml, and I didn't manually write android:clickable="true", and the click event responds the same, that's because your Activity inherits AppCompatActivity, look at the source code, you can find AppCompatActivity It will convert TextView to 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;
    }

arrive

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);
    }

setContentView() of AppCompatDelegateImpl.java

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();
    }

Here, by adding DecorView first, find the id of the framelayout named content, and add our layout on this id:

For the specific loading process, please refer to : How Android activity loads layout

Mainly 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;
        }
    }

createViewFromTagIt is used to create View, first judge the two things factory2 and factory, and our AppCompatDelegateImpl implements the factory2 interface, so the mFactory2.onCreateView here is actually called back to the onCreateView of AppCompatDelegateImpl. Let's follow up and take a look: there are too many codes, so pick some key points:

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);
    }

It can be found that if it is TextView, it will directly return the class in the V7 package AppCompatTextView, so this is why when we use it AppCompatActivity, it TextViewbecomes AppCompatTextView.

So the question is, is itAppCompatTextView源码是默认可点击的。我们可以来看下。

public class AppCompatTextView extends TextView implements TintableBackgroundView, AutoSizeableTextView {
    private final AppCompatBackgroundHelper mBackgroundTintHelper;
    private final AppCompatTextHelper mTextHelper;
    @Nullable
    private Future<PrecomputedTextCompat> mPrecomputedTextFuture;

    public AppCompatTextView(Context context) {
        this(context, (AttributeSet)null);
    }

    public AppCompatTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 16842884);
    }

    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
        this.mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
        this.mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
        this.mTextHelper = new AppCompatTextHelper(this);
        this.mTextHelper.loadFromAttributes(attrs, defStyleAttr);
        this.mTextHelper.applyCompoundDrawablesTints();
    }

    public void setBackgroundResource(@DrawableRes int resId) {
        super.setBackgroundResource(resId);
        if (this.mBackgroundTintHelper != null) {
            this.mBackgroundTintHelper.onSetBackgroundResource(resId);
        }

    }

    public void setBackgroundDrawable(Drawable background) {
        super.setBackgroundDrawable(background);
        if (this.mBackgroundTintHelper != null) {
            this.mBackgroundTintHelper.onSetBackgroundDrawable(background);
        }

    }
.....
}

 In fact, you can see that AppCompatTextView inherits from TextView, and there is no setting about Clickable in the code. That must be in the default style file.

First look at our style:

values/styles.xml  
   <style name="AppTheme" parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
这里面没有定义AutoCompleteTextView的样式。然后跟到父样式:
<style name="Theme.AppCompat.Light" parent="Base.Theme.AppCompat.Light"/>
也没有定义。再跟进去:
<style name="Base.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">
 同上
<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
同上
<style name="Platform.AppCompat.Light" parent="android:Theme.Holo.Light">
同上:
<style name="Theme.Holo.Light" parent="Theme.Light">
终于找到祖先了。。。。
<style name="Theme.Light">
这个里面定义了很多基本样式。我只挑了部分样式。
<style name="Theme.Light">
  <!-- Button styles -->
        <item name="buttonStyle">@style/Widget.Button</item>
         <item name="textViewStyle">@style/Widget.TextView</item>
        <item name="autoCompleteTextViewStyle">@style/Widget.AutoCompleteTextView</item>
</style>

We can see autoCompleteTextViewStyle, which is actually the basic style of AppCompatTextView, click in:

style路径:(android-sdk/platforms/android-28/data/res/values/styles.xml)

 <style name="Widget.Button">
       ...
        <item name="focusable">true</item>
        <item name="clickable">true</item>
        .....
    </style>

<style name="Widget.TextView">
        <item name="textAppearance">?attr/textAppearanceSmall</item>
        <item name="textSelectHandleLeft">?attr/textSelectHandleLeft</item>
        <item name="textSelectHandleRight">?attr/textSelectHandleRight</item>
        <item name="textSelectHandle">?attr/textSelectHandle</item>
        <item name="textEditPasteWindowLayout">?attr/textEditPasteWindowLayout</item>
        <item name="textEditNoPasteWindowLayout">?attr/textEditNoPasteWindowLayout</item>
。。。。。
    </style>

<style name="Widget.EditText">
      ....
        <item name="clickable">true</item>
      .....
    </style>
    
<style name="Widget.AutoCompleteTextView" parent="Widget.EditText">
        <item name="completionHintView">@layout/simple_dropdown_hint</item>
        <item name="completionThreshold">2</item>
        <item name="dropDownSelector">@drawable/list_selector_background</item>
        <item name="popupBackground">@drawable/spinner_dropdown_background</item>
        <item name="dropDownVerticalOffset">-6dip</item>
        <item name="dropDownHorizontalOffset">0dip</item>
        <item name="dropDownWidth">wrap_content</item>
    </style>

 Omitting some styles, we can see that the basic style of AutoCompleteTextView is inherited from EditText. The clickable of EditText is true by default. so

AutoCompleteTextView is also clickable by default. . .

So on 4.4 your Activity inherits AppCompatActivity, then you can still define some events in xml, no need to manually write: android:clickable="true"

However, if you inherit Activity or FragmentActivity, then you need to write in xml: android:clickable="true"

Summary: Below 4.4, it is best to write android:clickable="true" on TextView, or your Activity inherits AppCompatActivity.

In addition, if it is in the xml of Fragment: android:onClick="testClick", then. testClick needs to be defined in the Activity where the Fragment is located. It cannot be defined in Fragment, the reason please Baidu (manually funny)....

Daily records:

I'm actually not withdrawn, and I can even be said to be cheerful and lively, but most of the time I am lazy and too lazy to manage a relationship. There are also times when I love freedom and feel that any kind of relationship will restrict me. Of course, the most important thing is that it is hard to find a soulmate. I always feel that I can only show one dimension of myself when interacting with most people, and it is difficult to find one. people like me.
—— Liu Yu "Send You a Bullet"

Single cycle "Tears in Laughter"

Guess you like

Origin blog.csdn.net/androidzmm/article/details/89213961