亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android 中ListView setOnItemClickListener點擊無效原因分析

2020-04-11 11:02:15
字體:
來源:轉載
供稿:網友

前言

最近在做項目的過程中,在使用listview的時候遇到了設置item監聽事件的時候在沒有回調onItemClick 方法的問題。我的情況是在item中有一個Button按鈕。所以不會回調。上百度找到了解決辦法有兩種,如下:

1、在checkbox、button對應的view處加android:focusable=”false”

復制代碼 代碼如下:

android:clickable=”false” android:focusableInTouchMode=”false”

2、在item最外層添加屬性 android:descendantFocusability=”blocksDescendants”

網上大多數帖子的理由是:當listview中包含button,checkbox等控件的時候,android會默認將focus給了這些控件,也就是說listview的item根本就獲取不到focus,所以導致onitemclick時間不能觸發。

由于自己想去驗證一下,所有有了這篇文章。好了下面開始

我們為ListView設置的onItemClickListener是在何處回調的?

要搞清楚這個問題,我們先從 android事件分發機制開始說起,事件分發機制網上有大神寫了一些特別詳細和優秀的文章,在這里就只做簡要介紹了:

事件分發重要的三個方法

復制代碼 代碼如下:

public boolean dispatchTouchEvent(MotionEvent ev)

該方法用來進行事件分發,在事件傳遞到當前View的時候調用,返回結果受到當前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響。

復制代碼 代碼如下:

public boolean onInterceptTouchEvent(MotionEvent ev)

該方法在上一個方法dispatchTouchEvent中調用,返回結果表示是否攔截當前事件,默認返回false,也就是不攔截。

復制代碼 代碼如下:

public void onTouchEvent(MotionEvent event)

在 dispatchTouchEvent方法中調用,該方法用來處理點擊事件,返回結果表示是否消耗當前事件。

當點擊事件觸發之后的流程

了解事件分發機制之后,我們在setOnItemClick之后肯定需要進行事件處理,上面說到事件攔截默認是不攔截,所以我們猜想會到ListView的onTouchEvent方法中去處理ItemClick事件。去找你會發現ListView沒有onTouchEvent方法。那我們再去他的父類AbsListView去找。還真有:

@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (!isEnabled()) {// A disabled view that is clickable still consumes the touch// events, it just doesn't respond to them.return isClickable() || isLongClickable();}if (mPositionScroller != null) {mPositionScroller.stop();}if (mIsDetaching || !isAttachedToWindow()) {// Something isn't right.// Since we rely on being attached to get data set change notifications,// don't risk doing anything where we might try to resync and find things// in a bogus state.return false;}startNestedScroll(SCROLL_AXIS_VERTICAL);if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) {return true;}initVelocityTrackerIfNotExists();final MotionEvent vtev = MotionEvent.obtain(ev);final int actionMasked = ev.getActionMasked();if (actionMasked == MotionEvent.ACTION_DOWN) {mNestedYOffset = 0;}vtev.offsetLocation(0, mNestedYOffset);switch (actionMasked) {case MotionEvent.ACTION_DOWN: {onTouchDown(ev);break;}case MotionEvent.ACTION_MOVE: {onTouchMove(ev, vtev);break;}case MotionEvent.ACTION_UP: {onTouchUp(ev);break;}case MotionEvent.ACTION_CANCEL: {onTouchCancel();break;}case MotionEvent.ACTION_POINTER_UP: {onSecondaryPointerUp(ev);final int x = mMotionX;final int y = mMotionY;final int motionPosition = pointToPosition(x, y);if (motionPosition >= 0) {// Remember where the motion event startedfinal View child = getChildAt(motionPosition - mFirstPosition);mMotionViewOriginalTop = child.getTop();mMotionPosition = motionPosition;}mLastY = y;break;}case MotionEvent.ACTION_POINTER_DOWN: {// New pointers take over dragging dutiesfinal int index = ev.getActionIndex();final int id = ev.getPointerId(index);final int x = (int) ev.getX(index);final int y = (int) ev.getY(index);mMotionCorrection = 0;mActivePointerId = id;mMotionX = x;mMotionY = y;final int motionPosition = pointToPosition(x, y);if (motionPosition >= 0) {// Remember where the motion event startedfinal View child = getChildAt(motionPosition - mFirstPosition);mMotionViewOriginalTop = child.getTop();mMotionPosition = motionPosition;}mLastY = y;break;}}if (mVelocityTracker != null) {mVelocityTracker.addMovement(vtev);}vtev.recycle();return true;}

代碼比較長,我們主要看46行 MotionEvent.ACTION_UP的情況,因為onItemClick事件的觸發是在我們的手指從屏幕抬起的那一刻,在MotionEvent.ACTION_UP的情況下執行了onTouchUp(ev);那么我們可以想到問題發生的原因應該就是在這個方法了里了。

private void onTouchUp(MotionEvent ev) {switch (mTouchMode) {case TOUCH_MODE_DOWN:case TOUCH_MODE_TAP:case TOUCH_MODE_DONE_WAITING:final int motionPosition = mMotionPosition;final View child = getChildAt(motionPosition - mFirstPosition);if (child != null) {if (mTouchMode != TOUCH_MODE_DOWN) {child.setPressed(false);}final float x = ev.getX();final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;if (inList && !child.hasFocusable()) {if (mPerformClick == null) {mPerformClick = new PerformClick();}final AbsListView.PerformClick performClick = mPerformClick;performClick.mClickMotionPosition = motionPosition;performClick.rememberWindowAttachCount();mResurrectToPosition = motionPosition;if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?mPendingCheckForTap : mPendingCheckForLongPress);mLayoutMode = LAYOUT_NORMAL;if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {mTouchMode = TOUCH_MODE_TAP;setSelectedPositionInt(mMotionPosition);layoutChildren();child.setPressed(true);positionSelector(mMotionPosition, child);setPressed(true);if (mSelector != null) {Drawable d = mSelector.getCurrent();if (d != null && d instanceof TransitionDrawable) {((TransitionDrawable) d).resetTransition();}mSelector.setHotspot(x, ev.getY());}if (mTouchModeReset != null) {removeCallbacks(mTouchModeReset);}mTouchModeReset = new Runnable() {@Overridepublic void run() {mTouchModeReset = null;mTouchMode = TOUCH_MODE_REST;child.setPressed(false);setPressed(false);if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {performClick.run();}}};postDelayed(mTouchModeReset,ViewConfiguration.getPressedStateDuration());} else {mTouchMode = TOUCH_MODE_REST;updateSelectorState();}return;} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {performClick.run();}}}mTouchMode = TOUCH_MODE_REST;updateSelectorState();break;}

這里主要看7行到18行,拿到了我們item的View,并且在15行代碼里判斷了item的View是否在范圍是否獲取焦點(hasFocusable()),這里對hasFocusable()取反判斷,也就是說,必需要我們的itemView的hasFocusable() 方法返回false, 才會執行一下的方法,以下的方法就是點擊事件的方法。那么我們來看看是不是mPerformClick真的就是執行我們的itemClick事件。

PerformClick以及相關代碼如下:

private class PerformClick extends WindowRunnnable implements Runnable {int mClickMotionPosition;@Overridepublic void run() {// The data has changed since we posted this action in the event queue,// bail out before bad things happenif (mDataChanged) return;final ListAdapter adapter = mAdapter;final int motionPosition = mClickMotionPosition;if (adapter != null && mItemCount > 0 &&motionPosition != INVALID_POSITION &&motionPosition < adapter.getCount() && sameWindow()) {final View view = getChildAt(motionPosition - mFirstPosition);// If there is no view, something bad happened (the view scrolled off the// screen, etc.) and we should cancel the clickif (view != null) {performItemClick(view, motionPosition, adapter.getItemId(motionPosition));}}}}

第18行代碼拿到了我們點擊的item View,并且調用了performItemClick方法。我們再來看absListView的performItemClick方法:

@Overridepublic boolean performItemClick(View view, int position, long id) {boolean handled = false;boolean dispatchItemClick = true;if (mChoiceMode != CHOICE_MODE_NONE) {handled = true;boolean checkedStateChanged = false;if (mChoiceMode == CHOICE_MODE_MULTIPLE ||(mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {boolean checked = !mCheckStates.get(position, false);mCheckStates.put(position, checked);if (mCheckedIdStates != null && mAdapter.hasStableIds()) {if (checked) {mCheckedIdStates.put(mAdapter.getItemId(position), position);} else {mCheckedIdStates.delete(mAdapter.getItemId(position));}}if (checked) {mCheckedItemCount++;} else {mCheckedItemCount--;}if (mChoiceActionMode != null) {mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,position, id, checked);dispatchItemClick = false;}checkedStateChanged = true;} else if (mChoiceMode == CHOICE_MODE_SINGLE) {boolean checked = !mCheckStates.get(position, false);if (checked) {mCheckStates.clear();mCheckStates.put(position, true);if (mCheckedIdStates != null && mAdapter.hasStableIds()) {mCheckedIdStates.clear();mCheckedIdStates.put(mAdapter.getItemId(position), position);}mCheckedItemCount = 1;} else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {mCheckedItemCount = 0;}checkedStateChanged = true;}if (checkedStateChanged) {updateOnScreenCheckedViews();}}if (dispatchItemClick) {handled |= super.performItemClick(view, position, id);}return handled;}

看第54行調用了父類的performItemClick方法:

public boolean performItemClick(View view, int position, long id) {final boolean result;if (mOnItemClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);mOnItemClickListener.onItemClick(this, view, position, id);result = true;} else {result = false;}if (view != null) {view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);}return result;}

好了,搞了半天,終于到點上了。第3

行代碼很明顯了,就是如果有ItemClickListener,就執行他的onItemClick方法,最終回調到我們常見的那個方法。

到這里,相信大家已經知道,關鍵代碼就是剛才上面我們分析的那一個if判斷

if (inList && !child.hasFocusable()) {if (mPerformClick == null) {mPerformClick = new PerformClick();}.....}

也就是只有item的View hasFocusable( )方法返回false,才會執行onItemClick。

View 和 ViewGroup 的 hasFocusable

ViewGroup的hasFocusable

@Overridepublic boolean hasFocusable() {if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {return false;}if (isFocusable()) {return true;}final int descendantFocusability = getDescendantFocusability();if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {final int count = mChildrenCount;final View[] children = mChildren;for (int i = 0; i < count; i++) {final View child = children[i];if (child.hasFocusable()) {return true;}}}return false;}

看源碼我們可以知道:

如果 ViewGroup visiable 和 focusable 都為 true,就算能夠獲取焦點, 返回 true。
如果我們給ViewGroup設置了descendantFocusability屬性,并且等于FOCUS_BLOCK_DESCENDANTS的情況下,返回false。不能獲取焦點。
如果沒有設置descendantFocusability屬性的話,只要一個子View hasFocusable返回了true,ViewGroup的hasFocusable就返回。

再來看View的hasFocusable

ViewGroup的hasFocusable

public boolean hasFocusable() {if (!isFocusableInTouchMode()) {for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {final ViewGroup g = (ViewGroup) p;if (g.shouldBlockFocusForTouchscreen()) {return false;}}}return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();}

在觸摸模式下如果不可獲取焦點,先遍歷 View 的所有父節點,如果有一個父節點設置了阻塞子 View 獲取焦點,那么該 View 就不可能獲取焦點
在觸摸模式下如果不可獲取焦點,并且沒有父節點設置阻塞子 View 獲取焦點,和在觸摸模式下如果可以獲取焦點,那么才判斷 View 自身的 visiable 和 focusable 屬性,來決定是否可以獲取焦點,只有 visiable 和 focusable 同時為 true,該View 才可能獲取焦點。

好了,分析到這里我們再回過頭去看兩個解決辦法。

在checkbox、button對應的view處加android:focusable=”false”

復制代碼 代碼如下:

android:clickable=”false” android:focusableInTouchMode=”false”

在item最外層添加屬性 android:descendantFocusability=”blocksDescendants”

第一種情況,item沒有設置descendantFocusability=”blocksDescendants”,遍歷了所有子View,由于所有的子view都不可獲得焦點,所有item也沒有獲取焦點,那么上面說到回調至性的條件判斷也就的代碼:

if (inList && !child.hasFocusable()) {if (mPerformClick == null) {mPerformClick = new PerformClick();}.....}

if條件成立,所有執行了回調。

第二種情況,item,設置了descendantFocusability=”blocksDescendants”,所有沒有遍歷子 View,child.hasFocusable()直接返回false了。

以上所述是本文給大家分享的Android 中ListView setOnItemClickListener點擊無效原因分析,希望大家喜歡。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产香蕉精品视频一区二区三区| 国产激情视频一区| 在线视频免费一区二区| 欧美电影免费观看电视剧大全| 亚洲成人亚洲激情| 精品久久久久久久久久久久久| 精品国产成人在线| 亚洲欧美成人网| 日韩av网站导航| 久久国内精品一国内精品| 亚洲精品一区二三区不卡| 欧美日韩亚洲成人| 九九热这里只有精品免费看| 亚洲午夜久久久影院| 久久精品99国产精品酒店日本| 69久久夜色精品国产7777| 欧美激情视频在线免费观看 欧美视频免费一| 国产精品a久久久久久| 欧美日韩国产一中文字不卡| 欧美激情亚洲自拍| 日韩精品视频中文在线观看| 国产日韩精品入口| 国产第一区电影| 国产精品www色诱视频| 久久综合免费视频| 黄网站色欧美视频| 欧美激情亚洲自拍| 成人黄色av播放免费| 精品视频在线导航| 国模极品一区二区三区| 中文字幕一区二区精品| 91po在线观看91精品国产性色| 亚洲国产成人精品一区二区| 亚洲视频精品在线| 中文字幕日韩免费视频| 日韩欧美高清在线视频| 亚洲免费一级电影| 国产91精品黑色丝袜高跟鞋| 高清欧美性猛交xxxx| 精品欧美aⅴ在线网站| 日韩精品免费一线在线观看| 欧美日韩精品在线观看| 午夜精品久久久99热福利| 国产ts一区二区| 日韩国产在线播放| 亚洲在线观看视频网站| 性色av香蕉一区二区| 8090成年在线看片午夜| 欧美中文在线字幕| 久久综合免费视频影院| 亚洲欧美国产一本综合首页| 精品久久久久人成| 性欧美长视频免费观看不卡| 亚州国产精品久久久| 精品国产网站地址| 欧美一区二区.| 国产欧美一区二区三区在线看| 日韩中文字幕免费视频| 国产精品嫩草视频| 欧美xxxx14xxxxx性爽| 久久艳片www.17c.com| 亚洲小视频在线| 日韩欧美在线中文字幕| 国内精品模特av私拍在线观看| 成人综合国产精品| 欧美高清激情视频| 国产精品va在线播放| 亚洲人成人99网站| 97视频免费在线观看| 成人激情视频网| 欧美亚洲第一页| 欧美极品少妇xxxxⅹ喷水| 中文欧美在线视频| 成人国产在线激情| 热久久99这里有精品| 久久69精品久久久久久国产越南| 欧美成人精品一区二区| 日韩精品欧美国产精品忘忧草| 日韩精品在线视频美女| 91国产美女视频| 性欧美视频videos6一9| 一区二区在线视频| 国产va免费精品高清在线观看| 欧美又大又粗又长| 欧美精品18videos性欧美| 亚洲黄色有码视频| 日韩电影在线观看免费| 日韩欧美成人精品| 色av吧综合网| 久久精品色欧美aⅴ一区二区| 亚洲人成在线播放| 国产成人+综合亚洲+天堂| 国产精品黄色av| 国产suv精品一区二区| 亚洲精品国产精品自产a区红杏吧| 久久成人在线视频| 黑人极品videos精品欧美裸| 欧美在线激情网| 奇米一区二区三区四区久久| 亚洲视频视频在线| 国产香蕉一区二区三区在线视频| 久久精品亚洲一区| 亚洲视频在线观看视频| 亚洲国产欧美一区二区丝袜黑人| 日韩大陆毛片av| 欧美国产一区二区三区| 国产欧美日韩免费看aⅴ视频| 国产精品久久视频| 国产成人自拍视频在线观看| 欧美成人免费观看| 国产精品欧美久久久| 91精品成人久久| 欧美另类极品videosbestfree| 亚洲经典中文字幕| 国产91精品久久久久久久| 午夜精品一区二区三区在线视| 美女少妇精品视频| 久久久国产精彩视频美女艺术照福利| 中文字幕亚洲综合| 成人av资源在线播放| 日韩av在线网站| 精品国产一区久久久| 久热精品视频在线免费观看| 综合欧美国产视频二区| 日韩在线观看免费全集电视剧网站| 日韩精品在线播放| 中文字幕亚洲专区| 久久精品小视频| 国产玖玖精品视频| 91久久夜色精品国产网站| 5566日本婷婷色中文字幕97| 国产视频观看一区| 亚洲第一福利在线观看| www.欧美视频| 亚洲二区中文字幕| 亚洲自拍偷拍一区| 欧美视频在线观看免费| 国产精品美女主播在线观看纯欲| 亚洲欧美综合图区| 日韩激情av在线播放| 亚洲欧美国产精品| 日本国产一区二区三区| 中文字幕亚洲一区二区三区| 97视频免费在线看| 中文字幕在线看视频国产欧美| 亚洲视频在线免费看| 亚洲成人精品在线| 精品国产一区二区三区久久狼黑人| 欧美与欧洲交xxxx免费观看| 日韩在线观看成人| 色综合久久久久久中文网| 热99精品里视频精品| 日韩av中文在线| 丝袜情趣国产精品| 中文字幕自拍vr一区二区三区| 俺去啦;欧美日韩| 大胆人体色综合| 亚洲一区二区久久久| www.午夜精品| 国产精品福利在线观看网址| 在线视频亚洲欧美| 欧美日韩爱爱视频| 亚洲精品一二区| 欧美在线免费视频|