在Android4.4也就是sdk19的手机上。TextView的OnClick点击事件没有响应
xml:
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
的原因.
那么问题来了,难道说AppCompatTextView源码是默认可点击的。我们可以来看下。
public class AppCompatTextView extends TextView implements TintableBackgroundView, AutoSizeableTextView {
private final AppCompatBackgroundHelper mBackgroundTintHelper;
private final AppCompatTextHelper mTextHelper;
@Nullable
private Future 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);
}
}
.....
}
其实可以看到AppCompatTextView继承自TextView,代码里面并没有关于Clickable的设置。那肯定就是在默认的样式文件里了。
首先看我们的样式:
values/styles.xml
这里面没有定义AutoCompleteTextView的样式。然后跟到父样式:
也没有定义。再跟进去:
我们可以看到autoCompleteTextViewStyle,这个其实就是AppCompatTextView的基本样式,点进去:
style路径:(android-sdk/platforms/android-28/data/res/values/styles.xml)
省略了部分style,我们可以看到AutoCompleteTextView的基本样式,是继承自EditText.而EditText的clickable 默认为true。故
AutoCompleteTextView默认也是可以点击的。。。
所以在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,原因请自行百度(手动滑稽)....
每日语录:
我其实并不孤僻,甚至可以说开朗活泼,但大多时候我很懒,懒得经营一个关系。还有一些时候就是爱自由,觉得任何一种关系都会束缚自己,当然最主要的还是知音难寻,我老觉得自己跟大多数人交往总是只能拿出自己的一个维度,很难找到一个像我一样的人。
—— 刘瑜《送你一颗子弹》
单曲循环《笑中有泪》