標籤:
先看View中是如何定義clickable和onClick的:
case com.android.internal.R.styleable.View_clickable: if (a.getBoolean(attr, false)) { viewFlagValues |= CLICKABLE; viewFlagMasks |= CLICKABLE; } break;
上面這段代碼是View源碼中對clickable屬性的定義,預設值為false。
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) { setOnClickListener(new OnClickListener() { private Method mHandler; public void onClick(View v) { if (mHandler == null) { try { mHandler = getContext().getClass().getMethod(handlerName, View.class); } catch (NoSuchMethodException e) { int id = getId(); String idText = id == NO_ID ? "" : " with id ‘" + getContext().getResources().getResourceEntryName( id) + "‘"; throw new IllegalStateException("Could not find a method " + handlerName + "(View) in the activity " + getContext().getClass() + " for onClick handler" + " on view " + View.this.getClass() + idText, e); } } try { mHandler.invoke(getContext(), View.this); } catch (IllegalAccessException e) { throw new IllegalStateException("Could not execute non " + "public method of the activity", e); } catch (InvocationTargetException e) { throw new IllegalStateException("Could not execute " + "method of the activity", e); } } }); } break;
上面是onClick屬性在View源碼中的定義。可以看到它調用了setOnClickListener()方法對點擊事件進行註冊,下面看下setOnClickListener()方法的源碼:
/** * Register a callback to be invoked when this view is clicked. If this view is not * clickable, it becomes clickable. * * @param l The callback that will run * * @see #setClickable(boolean) */ public void setOnClickListener(OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
如果clickable屬性是false那麼在調用此方法後就會變為true。
繼承自View的Button、ImageView控制項在xml裡使用onClick屬性時,通過列印日誌可以看到clickable由預設的false變為了true,可是同樣繼承自View的TextView依舊是false。為什麼會出現這種情況呢?
那就要瞭解Android載入xml檔案的過程。
以View為例進行說明。我們使用setOnContentView()方法載入布局檔案時。布局檔案中的View元素會調用下面的這個構造器來建立一個View對象
/** * Constructor that is called when inflating a view from XML. This is called * when a view is being constructed from an XML file, supplying attributes * that were specified in the XML file. This version uses a default style of * 0, so the only attribute values applied are those in the Context‘s Theme * and the given AttributeSet. * * <p> * The method onFinishInflate() will be called after all children have been * added. * * @param context The Context the view is running in, through which it can * access the current theme, resources, etc. * @param attrs The attributes of the XML tag that is inflating the view. * @see #View(Context, AttributeSet, int) */ public View(Context context, AttributeSet attrs) { this(context, attrs, 0); }View Code
this(context, attrs, 0);又會調用 public View(Context context, AttributeSet attrs, int defStyleAttr){}構造器。可以看到onClick屬性在定義時調用的setOnClickListener()方法是在對象被建立時調用的。
下面再來看TextView控制項,他在構造器中對clickable又進行了一些處理
boolean focusable = mMovement != null || getKeyListener() != null; boolean clickable = focusable; boolean longClickable = focusable; n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case com.android.internal.R.styleable.View_focusable: focusable = a.getBoolean(attr, focusable); break; case com.android.internal.R.styleable.View_clickable: clickable = a.getBoolean(attr, clickable); break; case com.android.internal.R.styleable.View_longClickable: longClickable = a.getBoolean(attr, longClickable); break; } }
從代碼中可以看到clickable的預設值變成了focuseable的值,TextView在調用構造器創造新的對象的時候會預設調用父類的構造器,也就是說經過setOnClickListener()處理過的clickable值是變成了true,可是在TextView的構造器中又被賦予了新的值。這也可以解釋說為什麼TextView控制項在xml檔案中用了onClick屬性後clickable的值依舊是false,而在代碼中調用setOnClickListener()方法後,clickable值就變成了true,那是因為在代碼中調用的setOnClickListener()方法是在TextView對象被建立之後。
TextView在xml檔案中加入onClick屬性後,clickable值依舊是false的原因。