Pressed狀態和clickable,duplicateParentState的關系

??? 做Android開發的人都用過Selector,可以方便的實現View在不同狀態下的背景。不過,相信大部分開發者遇到過和我一樣的問題,本文會從源碼角度,解釋這些問題。


??????? 首先,這里簡單描述一下,我遇到的問題:

界面上有個全屏的LinearLayout A,A中有一個TextView B和Button C,其中,A的clickable=true,并設置了pressed時,背景色為灰色,B設置了pressed時,背景色為藍色

當手指觸摸C下方的空白區域時,看到了這樣的效果:

??????? 在這里看到,在沒有觸摸B的情況下,B的pressed = true,而C的pressed = false。 C的狀態暫且不討論,按照Android消息傳遞的原則,因為touch的point不在B內部,所以,touch消息應該不會交給B處理,那為什么B的pressed = true?

???????? 下面開始一步一步分析(本文分析的Android源碼為4.2.2)。

Pressed狀態的設定

??????? 從View.onTouchEvent函數看起(frameworks/base/core/java/android/view/View.java):

[java] view plain copy
  1. /**?
  2. ?*?Implement?this?method?to?handle?touch?screen?motion?events.?
  3. ?*?
  4. ?*?@param?event?The?motion?event.?
  5. ?*?@return?True?if?the?event?was?handled,?false?otherwise.?
  6. ?*/??
  7. public?boolean?onTouchEvent(MotionEvent?event)?{??
  8. ????......??
  9. ????if?(((viewFlags?&?CLICKABLE)?==?CLICKABLE?||?//這里是為什么設置A的clickable為true的原因,否則,press?A的時候,沒有界面元素處理touch?event,最終會由Activity的onTouchEvent函數處理??
  10. ????????????(viewFlags?&?LONG_CLICKABLE)?==?LONG_CLICKABLE))?{??
  11. ????????switch?(event.getAction())?{??
  12. ????????????case?MotionEvent.ACTION_UP:??
  13. ????????????????......??
  14. ????????????????break;??
  15. ??
  16. ????????????case?MotionEvent.ACTION_DOWN:??
  17. ????????????????mHasPerformedLongPress?=?false;??
  18. ????????????????......??
  19. ????????????????//?Walk?up?the?hierarchy?to?determine?if?we're?inside?a?scrolling?container.??
  20. ????????????????boolean?isInScrollingContainer?=?isInScrollingContainer();//A已經是頂層元素了,沒有ScrollView之類的控件存在,所以,isInScrollingContainer?=?false??
  21. ??
  22. ????????????????//?For?views?inside?a?scrolling?container,?delay?the?pressed?feedback?for??
  23. ????????????????//?a?short?period?in?case?this?is?a?scroll.??
  24. ????????????????if?(isInScrollingContainer)?{??
  25. ????????????????????mPrivateFlags?|=?PFLAG_PREPRESSED;??
  26. ????????????????????if?(mPendingCheckForTap?==?null)?{??
  27. ????????????????????????mPendingCheckForTap?=?new?CheckForTap();??
  28. ????????????????????}??
  29. ????????????????????postDelayed(mPendingCheckForTap,?ViewConfiguration.getTapTimeout());??
  30. ????????????????}?else?{??
  31. ????????????????????//?Not?inside?a?scrolling?container,?so?show?the?feedback?right?away??
  32. ????????????????????setPressed(true);//A設置pressed?=?true??
  33. ????????????????????checkForLongClick(0);??
  34. ????????????????}??
  35. ????????????????break;??
  36. ??
  37. ????????????case?MotionEvent.ACTION_CANCEL:??
  38. ????????????????......??
  39. ????????????????break;??
  40. ??
  41. ????????????case?MotionEvent.ACTION_MOVE:??
  42. ????????????????......??
  43. ????????????????break;??
  44. ????????}??
  45. ????????return?true;??
  46. ????}??
  47. ??
  48. ????return?false;??
  49. }??

???????

??????? 從上面的代碼我們知道,當手指觸摸A的時候,A的pressed被設置為true。

Pressed狀態的傳遞

??????? 接著,我們看看setPressed函數的實現:

[java] view plain copy
  1. /**?
  2. ?*?Sets?the?pressed?state?for?this?view.?
  3. ?*?
  4. ?*?@param?pressed?Pass?true?to?set?the?View's?internal?state?to?"pressed",?or?false?to?reverts?
  5. ?*????????????????the?View's?internal?state?from?a?previously?set?"pressed"?state.?
  6. ?*?@see?#isClickable()?
  7. ?*?@see?#setClickable(boolean)?
  8. ?*/??
  9. public?void?setPressed(boolean?pressed)?{??
  10. ????final?boolean?needsRefresh?=?pressed?!=?((mPrivateFlags?&?PFLAG_PRESSED)?==?PFLAG_PRESSED);??
  11. ??
  12. ????if?(pressed)?{??
  13. ????????mPrivateFlags?|=?PFLAG_PRESSED;??
  14. ????}?else?{??
  15. ????????mPrivateFlags?&=?~PFLAG_PRESSED;??
  16. ????}??
  17. ??
  18. ????if?(needsRefresh)?{??
  19. ????????refreshDrawableState();//切換背景圖片??
  20. ????}??
  21. ????dispatchSetPressed(pressed);??
  22. }??

??????? setPressed函數內部調用了dispatchSetPressed函數,這個讓人很在意(frameworks/base/core/java/android/view/ViewGroup.java):

[java] view plain copy
  1. @Override??
  2. protected?void?dispatchSetPressed(boolean?pressed)?{??
  3. ????final?View[]?children?=?mChildren;??
  4. ????final?int?count?=?mChildrenCount;??
  5. ????for?(int?i?=?0;?i?<?count;?i++)?{??
  6. ????????final?View?child?=?children[i];??
  7. ????????//?Children?that?are?clickable?on?their?own?should?not??
  8. ????????//?show?a?pressed?state?when?their?parent?view?does.??
  9. ????????//?Clearing?a?pressed?state?always?propagates.??
  10. ????????if?(!pressed?||?(!child.isClickable()?&&?!child.isLongClickable()))?{??
  11. ????????????child.setPressed(pressed);??
  12. ????????}??
  13. ????}??
  14. }??

??????? 原來,dispatchSetPressed函數會把pressed狀態傳遞給所有clickable=false并且longclickable=false的子元素。

??????? 到這里,前面的現象就可以解釋了,因為C是button類,clickable=true,而B的clickable=false。所以,當A被觸摸時,B的pressed=true,而C的pressed=false。那么,可以回答下面幾個小問題了:

  1. 如果不希望A的pressed=true時,B的pressed = true,該如何修改?
    1. 設置B的clickable=true
  2. 如果希望A的pressed = true時,C的pressed = true,那又該如何修改?
    1. 設置C的clickable = false. 但是,這里可能又存在問題了,設置C的clickable=false,會導致button按鈕的onclicklistener無法工作,這是個嚴重的副作用。那么可以不修改clickable,而設置android:duplicateParentState為true。

?????? 那么duplicateParentState做了些什么呢?View.setDuplicateParentStateEnabled:

[java] view plain copy
  1. public?void?setDuplicateParentStateEnabled(boolean?enabled)?{??
  2. ????setFlags(enabled???DUPLICATE_PARENT_STATE?:?0,?DUPLICATE_PARENT_STATE);??
  3. }??

????? 再看看View.onCreateDrawableState()

[java] view plain copy
  1. /**?
  2. ????*?Generate?the?new?{@link?android.graphics.drawable.Drawable}?state?for?
  3. ????*?this?view.?This?is?called?by?the?view?
  4. ????*?system?when?the?cached?Drawable?state?is?determined?to?be?invalid.??To?
  5. ????*?retrieve?the?current?state,?you?should?use?{@link?#getDrawableState}.?
  6. ????*?
  7. ????*?@param?extraSpace?if?non-zero,?this?is?the?number?of?extra?entries?you?
  8. ????*???????????????????would?like?in?the?returned?array?in?which?you?can?place?your?own?
  9. ????*???????????????????states.?
  10. ????*?@return?Returns?an?array?holding?the?current?{@link?Drawable}?state?of?
  11. ????*?the?view.?
  12. ????*?@see?#mergeDrawableStates(int[],?int[])?
  13. ????*/??
  14. ???protected?int[]?onCreateDrawableState(int?extraSpace)?{??
  15. ???????if?((mViewFlags?&?DUPLICATE_PARENT_STATE)?==?DUPLICATE_PARENT_STATE?&&??
  16. ???????????????mParent?instanceof?View)?{??
  17. ???????????return?((View)?mParent).onCreateDrawableState(extraSpace);??
  18. ???????}??
  19. ??
  20. ???????......??
  21. ???}??

??????? 從上面的代碼,可以看到,當設置duplicateParentState為true時,View的DrawableState直接使用了其parent的。所以,他的drawable狀態會一直保持與其parent同步。

題外,為什么當ListView中包含focusable為true的控件時,OnItemClickListener不會觸發

??????? 因為ListView未重載onTouchEvent事件,所以,需要看其父類的AbsListView.onTouchEvent(frameworks/base/core/java/android/widget/AbsListView):

[java] view plain copy
  1. @Override??
  2. public?boolean?onTouchEvent(MotionEvent?ev)?{??
  3. ????......??
  4. ????switch?(action?&?MotionEvent.ACTION_MASK)?{??
  5. ????case?MotionEvent.ACTION_DOWN:?{??
  6. ????????......??
  7. ????????break;??
  8. ????}??
  9. ??
  10. ????case?MotionEvent.ACTION_MOVE:?{??
  11. ????????......??
  12. ????????break;??
  13. ????}??
  14. ??
  15. ????case?MotionEvent.ACTION_UP:?{??
  16. ????????switch?(mTouchMode)?{??
  17. ????????case?TOUCH_MODE_DOWN:??
  18. ????????case?TOUCH_MODE_TAP:??
  19. ????????case?TOUCH_MODE_DONE_WAITING:??
  20. ????????????final?int?motionPosition?=?mMotionPosition;??
  21. ????????????final?View?child?=?getChildAt(motionPosition?-?mFirstPosition);??
  22. ??
  23. ????????????final?float?x?=?ev.getX();??
  24. ????????????final?boolean?inList?=?x?>?mListPadding.left?&&?x?<?getWidth()?-?mListPadding.right;??
  25. ??
  26. ????????????if?(child?!=?null?&&?!child.hasFocusable()?&&?inList)?{??
  27. ????????????????if?(mTouchMode?!=?TOUCH_MODE_DOWN)?{??
  28. ????????????????????child.setPressed(false);??
  29. ????????????????}??
  30. ??
  31. ????????????????if?(mPerformClick?==?null)?{??
  32. ????????????????????mPerformClick?=?new?PerformClick();??
  33. ????????????????}??
  34. ??
  35. ????????????????......??
  36. ????????????????performClick.run();??
  37. ????????????????......??
  38. ????????????}??
  39. ????????????......??
  40. ????????case?TOUCH_MODE_SCROLL:??
  41. ????????????......??
  42. ????????????break;??
  43. ??
  44. ????????case?TOUCH_MODE_OVERSCROLL:??
  45. ????????......??
  46. ????????break;??
  47. ????}??
  48. ????......??
  49. ????case?MotionEvent.ACTION_CANCEL:?{??
  50. ????????......??
  51. ????????break;??
  52. ????}??
  53. ??
  54. ????case?MotionEvent.ACTION_POINTER_UP:?{??
  55. ????????......??
  56. ????????break;??
  57. ????}??
  58. ??
  59. ????case?MotionEvent.ACTION_POINTER_DOWN:?{??
  60. ????????......??
  61. ????????break;??
  62. ????}??
  63. ????}??
  64. ??
  65. ????return?true;??
  66. }??

???????? 僅在child.hasFocusable()=false時, PerformClick對象才會執行ViewGroup.hasFocusable:

[java] view plain copy
  1. /**?
  2. ?*?{@inheritDoc}?
  3. ?*/??
  4. @Override??
  5. public?boolean?hasFocusable()?{??
  6. ????if?((mViewFlags?&?VISIBILITY_MASK)?!=?VISIBLE)?{??
  7. ????????return?false;??
  8. ????}??
  9. ??
  10. ????if?(isFocusable())?{??
  11. ????????return?true;??
  12. ????}??
  13. ??
  14. ????final?int?descendantFocusability?=?getDescendantFocusability();??
  15. ????if?(descendantFocusability?!=?FOCUS_BLOCK_DESCENDANTS)?{??
  16. ????????final?int?count?=?mChildrenCount;??
  17. ????????final?View[]?children?=?mChildren;??
  18. ??
  19. ????????for?(int?i?=?0;?i?<?count;?i++)?{??
  20. ????????????final?View?child?=?children[i];??
  21. ????????????if?(child.hasFocusable())?{??
  22. ????????????????return?true;??
  23. ????????????}??
  24. ????????}??
  25. ????}??
  26. ??
  27. ????return?false;??
  28. }??

??????? 僅在所有的clild的hasFocusable為false時,ListView才會執行performClick(AbsListView.PerformClick):

[java] view plain copy
  1. private?class?PerformClick?extends?WindowRunnnable?implements?Runnable?{??
  2. ????int?mClickMotionPosition;??
  3. ??
  4. ????public?void?run()?{??
  5. ????????//?The?data?has?changed?since?we?posted?this?action?in?the?event?queue,??
  6. ????????//?bail?out?before?bad?things?happen??
  7. ????????if?(mDataChanged)?return;??
  8. ??
  9. ????????final?ListAdapter?adapter?=?mAdapter;??
  10. ????????final?int?motionPosition?=?mClickMotionPosition;??
  11. ????????if?(adapter?!=?null?&&?mItemCount?>?0?&&??
  12. ????????????????motionPosition?!=?INVALID_POSITION?&&??
  13. ????????????????motionPosition?<?adapter.getCount()?&&?sameWindow())?{??
  14. ????????????final?View?view?=?getChildAt(motionPosition?-?mFirstPosition);??
  15. ????????????//?If?there?is?no?view,?something?bad?happened?(the?view?scrolled?off?the??
  16. ????????????//?screen,?etc.)?and?we?should?cancel?the?click??
  17. ????????????if?(view?!=?null)?{??
  18. ????????????????performItemClick(view,?motionPosition,?adapter.getItemId(motionPosition));??
  19. ????????????}??
  20. ????????}??
  21. ????}??
  22. }??

??????? 而PerformClick會調用performItemClick(AdsListView.performItemClick):

[java] view plain copy
  1. @Override??
  2. ???public?boolean?performItemClick(View?view,?int?position,?long?id)?{??
  3. ???????boolean?handled?=?false;??
  4. ???????boolean?dispatchItemClick?=?true;??
  5. ??
  6. ???????......??
  7. ??
  8. ???????if?(dispatchItemClick)?{??
  9. ???????????handled?|=?super.performItemClick(view,?position,?id);??
  10. ???????}??
  11. ??
  12. ???????return?handled;??
  13. ???}??

?????? AdapterView.preformItemClick:

[java] view plain copy
  1. /**?
  2. ?*?Call?the?OnItemClickListener,?if?it?is?defined.?
  3. ?*?
  4. ?*?@param?view?The?view?within?the?AdapterView?that?was?clicked.?
  5. ?*?@param?position?The?position?of?the?view?in?the?adapter.?
  6. ?*?@param?id?The?row?id?of?the?item?that?was?clicked.?
  7. ?*?@return?True?if?there?was?an?assigned?OnItemClickListener?that?was?
  8. ?*?????????called,?false?otherwise?is?returned.?
  9. ?*/??
  10. public?boolean?performItemClick(View?view,?int?position,?long?id)?{??
  11. ????if?(mOnItemClickListener?!=?null)?{??
  12. ????????playSoundEffect(SoundEffectConstants.CLICK);??
  13. ????????if?(view?!=?null)?{??
  14. ????????????view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);??
  15. ????????}??
  16. ????????mOnItemClickListener.onItemClick(this,?view,?position,?id);??
  17. ????????return?true;??
  18. ????}??
  19. ??
  20. ????return?false;??
  21. }??


??????? 所以,如果ListView item中包含focusable為true的控件(例如:button, radiobutton),會導致ItemClickListener失效。解決方案兩個:

設置特定的控件focusable = false

不使用onItemClickListener,而直接在Item上設置onClickListener監聽點擊事件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284397.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284397.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284397.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Hbase筆記4 java操作Hbase

暫無轉載于:https://www.cnblogs.com/mrxiaohe/p/6512481.html

【招聘(南京)】 慧咨環球南京研發中心 .NET和Blazor 前端

主要的亮點快速增長的、產品導向型的全球性科技公司設計和開發市場領先的軟件解決方案WLB — 工作生活相平衡澳洲排名前五的軟件公司混合辦公 — 3天在家辦公&#xff0c;2天在辦公室辦公在C#和.NET開發&#xff0c;企業級系統研發&#xff0c;軟件工程方面有長期的優秀實踐和技…

用Python+Django在Eclipse環境下開發web網站【轉】

一、創建一個項目如果這是你第一次使用Django&#xff0c;那么你必須進行一些初始設置。也就是通過自動生成代碼來建立一個Django項目--一個Django項目的設置集&#xff0c;包含了數據庫配置、Django詳細選項設置和應用 特性配置&#xff0c;具體操作步驟如下所示。 1.新建Djan…

[轉]數據結構KMP算法配圖詳解(超詳細)

KMP算法配圖詳解 前言 KMP算法是我們數據結構串中最難也是最重要的算法。難是因為KMP算法的代碼很優美簡潔干練&#xff0c;但里面包含著非常深的思維。真正理解代碼的人可以說對KMP算法的了解已經相當深入了。而且這個算法的不少東西的確不容易講懂&#xff0c;很多正規的書本…

BGP-MED-2

BGP-MED-2如圖&#xff1a;當AS100去往AS300的60、10的網絡時&#xff0c;60走R3&#xff0c;10走R1!使用MED屬性影響選路&#xff01; R2的配置 bgp 200peer 1.1.1.1 as-number 100 peer 1.1.1.1 ebgp-max-hop 255 peer 1.1.1.1 connect-interface LoopBack0peer 4.4.4.4 as-n…

WPF 實現 Gitee 氣泡菜單(一)

WPF 實現 Gitee 氣泡菜單&#xff08;一&#xff09;氣泡菜單&#xff08;一&#xff09;作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開…

[轉]LVS負載均衡(LVS簡介、三種工作模式、十種調度算法)

一、LVS簡介 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虛擬服務器&#xff0c;是由章文嵩博士主導的開源負載均衡項目&#xff0c;目前LVS已經被集成到Linux內核模塊中。該項目在Linux內核中實現了基于IP的數據請求負載均衡調度方案&#xff0c;其體系結構如圖1…

一張圖看懂微軟Power BI系列組件

一、Power BI簡介 Power BI是微軟最新的商業智能&#xff08;BI&#xff09;概念&#xff0c;它包含了一系列的組件和工具。話不多說&#xff0c;直接上圖吧&#xff1a; Power BI的核心理念就是讓我們用戶不需要強大的技術背景&#xff0c;只需要掌握Excel這樣簡單的工具就能快…

互聯網項目總結

2019獨角獸企業重金招聘Python工程師標準>>> 從去年年底開始專門被分配到互聯網小組做項目&#xff0c;一直想做個總結&#xff0c;但是苦于太貪玩。好吧&#xff0c;借著小組技術交流來一發。這里只對自己新學習的技術或者一些小技巧做簡要概述&#xff0c;不做深究…

【ArcGIS微課1000例】0036:分式標注案例教程

【拓展閱讀】:【ArcGIS Pro微課1000例】0015:ArcGIS Pro中屬性字段分式標注案例教程 文章目錄 1. 符號化2. 分式標注1. 符號化 右鍵數據圖層→符號系統,打開符號系統對話框,住符號系統選擇【唯一值】,字段1選擇NAME。 唯一值標注效果: 2. 分式標注 雙擊打開圖層屬性,切…

【轉】 ConstraintLayout 完全解析 快來優化你的布局吧

轉自&#xff1a; http://blog.csdn.net/lmj623565791/article/details/78011599 本文出自張鴻洋的博客 一、概述 ConstraintLayout出現有一段時間了&#xff0c;不過一直沒有特別去關注&#xff0c;也多多少少看了一些文字介紹&#xff0c;多數都是對使用可視化布局拖拽&#…

IoTDB 的C# 客戶端發布 0.13.0.7

IoTDB C# Client 0.13.0.7 已經發布&#xff0c; 此版本更新的內容為筆者為Apache-IoTDB-Client-CSharp實現了Ado.Net的兼容層&#xff0c;降低了對IoTDB的使用門檻。于此同時&#xff0c; IoTSharp也開始支持了IoTDB的數據入庫&#xff0c;隨著晚些時候IoTSharp 2.7 版本的發布…

[轉]Docker超詳細基礎教程,快速入門docker

一、docker概述 1.什么是docker Docker 是一個開源的應用容器引擎&#xff0c;基于 Go 語言 并遵從 Apache2.0 協議開源。 Docker 可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中&#xff0c;然后發布到任何流行的 Linux 機器上&#xff0c;也可以實現虛擬…

【Zookeeper】源碼分析之服務器(一)

一、前言 前面已經介紹了Zookeeper中Leader選舉的具體流程&#xff0c;接著來學習Zookeeper中的各種服務器。 二、總體框架圖 對于服務器&#xff0c;其框架圖如下圖所示 說明&#xff1a; ZooKeeperServer&#xff0c;為所有服務器的父類&#xff0c;其請求處理鏈為PrepReques…

linux下配置samba服務器(以CentOS6.7為例)

一、簡介&#xff08;百度百科&#xff09;Samba是在Linux和UNIX系統上實現SMB協議的一個免費軟件&#xff0c;由服務器及客戶端程序構成。SMB&#xff08;Server Messages Block&#xff0c;信息服務塊&#xff09;是一種在局域網上共享文件和打印機的一種通信協議&#xff0c…

【ArcGIS微課1000例】0037:上下標標注記案例教程

在利用ArcGIS進行制圖時&#xff0c;進行標注(Label) 或注記(Annolation) 是必不可少的。但是除了常規的標注和注記以外&#xff0c;還時常需要一些特殊的標注或注記&#xff0c;比如上標、下標等。 文章目錄一、上標標注方法二、下標標注方法一、上標標注方法 上下標代碼模板…

Redis——緩存擊穿、穿透、雪崩

1、緩存穿透&#xff1a; &#xff08;1&#xff09;問題描述&#xff1a;key對應的數據并不存在&#xff0c;每次請求訪問key時&#xff0c;緩存中查找不到&#xff0c;請求都會直接訪問到數據庫中去&#xff0c;請求量超出數據庫時&#xff0c;便會導致數據庫崩潰。如一個用…

數據庫性能系列之子查詢

前言說起數據庫&#xff0c;想必一些朋友會認為&#xff0c;數據庫不就是天天CRUD嗎&#xff1f;只要我掌握了這幾招&#xff0c;根本不在話下。是的&#xff0c;其實我也很贊同這個觀點&#xff0c;對于大多數應用程序來說&#xff0c;只掌握這些內容&#xff0c;是可以勝任日…

shell printf命令:格式化輸出語句

shell printf命令&#xff1a;格式化輸出語句注意&#xff1a;使用printf的腳本比使用echo移植性好。如同echo命令&#xff0c;printf命令可以輸出簡單的字符串&#xff1a;[rootmaster ~]#printf "Hello, Shell\n"Hello, Shellprintf不像echo那樣會自動提供一個換行…

UVa 10905 孩子們的游戲

https://vjudge.net/problem/UVA-10905 題意&#xff1a; 給定n個正整數&#xff0c;把它們連接成一個最大的整數。 思路&#xff1a; 實在是沒想到直接用string來排序了。 1 #include<iostream> 2 #include<algorithm>3 #include<string>4 using namespace …