在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,原因請自行百度(手動滑稽)....
每日語錄:
我其實並不孤僻,甚至可以說開朗活潑,但大多時候我很懶,懶得經營一個關係。還有一些時候就是愛自由,覺得任何一種關係都會束縛自己,當然最主要的還是知音難尋,我老覺得自己跟大多數人交往總是隻能拿出自己的一個維度,很難找到一個像我一樣的人。
—— 劉瑜《送你一顆子彈》
單曲循環《笑中有淚》