Clickable - Enable - Focusable 的
[转自]
从源码的角度分析Android中setClickable()和setEnable()的区别
android中setClickable,setEnabled,setFocusable的含义及区别
setClickable
设置为true
时,表明控件可以点击,如果为false
,就不能点击;“点击”适用于鼠标、键盘按键、遥控器等;
注意,setOnClickListener
方法会默认把控件的setClickable
设置为true
。
setEnabled
使能控件,如果设置为false
,该控件永远不会活动,不管设置为什么属性,都无效;
设置为true
,表明激活该控件,控件处于活动状态,处于活动状态,就能响应事件了,比如触摸、点击、按键事件等;
setEnabled
就相当于总开关一样,只有总开关打开了,才能使用其他事件。
setFocusable
使能控件获得焦点,设置为true
时,并不是说立刻获得焦点,要想立刻获得焦点,得用requestFocus
;
使能获得焦点,就是说具备获得焦点的机会、能力,当有焦点在控件之间移动时,控件就有这个机会、能力得到焦点。
1:用Button测试
Clickable的解析:
Clickable = false
<Button
android:id="@+id/click_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="Test Clickable"
android:textAllCaps="false" />
onTouchEvent(...)
源码中OnClickListner
触发的过程:
//...省略
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//...
if (!post(mPerformClick)) {
performClickInternal();
}
//...
break;
}
//...省略
可见,如果clickable
为false
了,点击事件就无法执行了
但是,但是,但是:
如果我们给这个Button
又设置了 ClickListener
,那么clickable
标志位,将被重置为true
View 源码
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
Enable的解析:
Enable = false
无法点击
Enable
标志位,是一个总开关,
在View的源码里:dispatchTouchEvent(...)
有如下代码:
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
前面的if (onFilterTouchEventForSecurity(event))
进不去,下面的(mViewFlags & ENABLED_MASK) == ENABLED
也不会走,只能进入:onTouchEvent(event)
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return clickable;
}
...省略...
}
ENABLE_MASK = DISABLE
后,return clickable
,但是,要注意,此处clickable
是true
(因为这个clicable
是由CLICKABLE
,LONG_CLICKABLE
,CONTEXT_CLICKABLE
生成的),表示,如果View
是Disable
的,但是如果View
是Clickable
,那么仍消耗这个touch
事件,
但是,onTouchEvent(...)
方法,就此结束,下面的performClick(...)
不会响应,
也就是说OnClickListener
不会有回调
上面所述要好好理解一下,比较绕
2 :用EditText 测试 Focusable
Focusable
这个标志位,与Enable
和Clickable
的交集不是很广
Focusable
set为true,是使控件能获得焦点,但不是立即获得焦点,得用requestFocus()
来请求获得焦点,
也就是说: setFocusable(true)
是使控件具备获取焦点的机会能力,当有焦点在控件之间移动式,控件才能得到焦点
<EditText
android:id="@+id/focusable_false_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:hint="focusable - false" />
<EditText
android:id="@+id/focusable_true_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="focusable - true" />
从效果我们可以看到,在xml里设置android:focusable="false"
会使EditText
无法获取焦点,
进而,无法调出输入键盘
Focusable = false
点击事件 仍有效
class MainActivity : AppCompatActivity() {
val TAG = "C_E_F"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "click btn isClickAble-1: ${click_btn.isClickable}")
// click_btn.setOnClickListener {
// Log.d(TAG, "click btn isClickAble-2: ${click_btn.isClickable}")
// Log.d(TAG, "click btn")
// }
enable_btn.setOnClickListener {
Log.d(TAG, "enable btn")
}
focusable_btn.setOnClickListener {
Log.d(TAG, "focusable btn")
}
focusable_false_et.setOnClickListener {
Log.d(TAG, "focusable false et")
}
focusable_true_et.setOnClickListener {
Log.d(TAG, "focusable true et")
}
}
}
_focuse_enable_test_181210 D/C_E_F: focusable false et
_focuse_enable_test_181210 D/C_E_F: focusable false et
_focuse_enable_test_181210 D/C_E_F: focusable true et
_focuse_enable_test_181210 D/C_E_F: focusable true et
_focuse_enable_test_181210 D/C_E_F: focusable true et
_focuse_enable_test_181210 D/C_E_F: focusable true et
_focuse_enable_test_181210 D/C_E_F: focusable false et
这里再附上关于requestFocuse(int direction)
的翻译:
/**
* Call this to try to give focus to a specific view or to one of its
* descendants and give it a hint about what direction focus is heading.
* 调用此方法以尝试将焦点放在特定视图或特定视图的子View上
* 并给出一个关于焦点方向的暗示。
*
* A view will not actually take focus if it is not focusable ({@link #isFocusable} returns
* false), or if it is focusable and it is not focusable in touch mode
* ({@link #isFocusableInTouchMode}) while the device is in touch mode.
*
* 如果一个View是无法聚焦的(即: `isFoucusable()`方法返回的是false),或者这个View在触摸模式(touch
* mode)下无法聚焦(即: `isFocusableInTouchMode`返回false),那么这个View不会持有焦点,
*
* See also {@link #focusSearch(int)}, which is what you call to say that you
* have focus, and you want your parent to look for the next one.
*
* 参见: `focusSearch(int direction)`方法,此方法作用是: 当前view持有焦点,然后,你打算寻找下一个可持有焦点的View
* This is equivalent to calling {@link #requestFocus(int, Rect)} with
* <code>null</code> set for the previously focused rectangle.
* 调用`requestFocus(int direction)`和调用`requestFocus(int direction, Rect previouslyFocusedRect)`方法,
* 并给rect传入null,是等价的
*
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
* @return Whether this view or one of its descendants actually took focus.
* 返回: 这个View或其子View 能否 持有焦点
*/
public final boolean requestFocus(int direction) {
return requestFocus(direction, null);
}
笔者粗略浏览了一遍View的源码,发现:FOCUSABLE
这个标志位,并没有与dispatchTouchEvent(...)
及onTouchEvent(...)
方法
发生交集,因此,本篇不再继续深入挖掘.只需了解即可.
--End--