<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
前言:
一個touch事件序列包括:down、move、up(其中move事件會多次觸發,就是說如果手指在螢幕上多次滑動的時候會多次觸發move事件,可以利用這一點實現view 的移動)
ViewGroup:用來進行事件分發 View:用來對事件的處理
分發流程: Activity#dispatchTouchEvent -> PhoneWindow#superDispatchTouchEvent -> DecorView#superDispatchTouchEvent ->ViewGroup#dispatchTouchEvent -> View#dispatchTouchEvent ->View#OnTouchEvent
從下往上看,先看事件如何被處理的,先看一個例子
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("hover","onCLick"); } }); btn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.e("hover", "onTouch:" + event.getAction()); //return false;//return false的話兩個列印紀錄檔都有 return true;//只有onTouch紀錄檔會列印 } });
對同一個元件設定兩個監聽,此處有三個面試題:
帶著問題看看View的dispatchTouchEvent
從程式碼中可以看出,onTouch的優先順序要高於onTouchEvent,而onClick是在onTouchEvent中呼叫的,因此onClick的優先順序最低
注意以上三個方法可能都不會執行,因為三個方法都是在View的dispatchTouchEvent的執行的,如果連dispatchTouchEvent都不執行的話,那麼三個方法就都不會執行了
什麼情況下View的dispatchTouchEvent會不執行呢:父容器不分發事件給View,就不會執行,即父容器不會呼叫子View的dispatchTouchEvent方法
那什麼時候父容器不會分發事件給View呢?這就需要看看事件分發的過程了: Activity#dispatchTouchEvent -> PhoneWindow#superDispatchTouchEvent -> DecorView#superDispatchTouchEvent -> ViewGroup#dispatchTouchEvent
ViewGroup中的dispatchTouchEvent中的核心地方可以用兩句虛擬碼來闡述(摘自Android開發藝術探索):
如果,ViewGroup的onInterceptTouchEvent方法執行了,則表示ViewGroup攔截了當前事件,去執行自己的 onTouchEvent邏輯,否則將事件分發給子View去執行
ViewGroup的分發邏輯主要有三個部分:
第一部分:判斷是否攔截該事件:
第二部分:分發事件給View,看哪個子View處理事件(原始碼太多了,只粘了後半部分)
注意:當Move事件來的時候不會走第二部分程式碼!!!
第三部分:執行事件:單指操作還是多指操作
子View去執行事件邏輯:
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) handled = child.dispatchTouchEvent(event);
總結:
1、如果父View攔截了事件並消費了事件,則子View 的dispatchTouchEvent就不會執行 2、如果父View並沒有攔截事件,但是所有的子View都沒有消費此事件,則最後也是執行父View的dispatchTouchEvent 3、如果父View沒有攔截事件,且某個子View攔截了此事件消費了,事件就不會再向下個子View傳遞,如果沒有消費,則會繼續遍歷下一個子View(這段邏輯再第二部分的for迴圈中)
如果子View處理了就提前break
如何解決自定View 的滑動衝突呢:根據實際情況去分配事件
以內部攔截法做一個例子:ViewPager中巢狀ListView
public class SlideInflictFragment extends Fragment { private BasePager mPager; List<MyListView> mListViews = new ArrayList<>(); private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple", "Strawberry","Cherry","Mango","Apple","Banana","Orange","Watermelon","Pear","Grape", "Pineapple","Strawberry","Cherry","Mango"}; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.slide_inflict_view_layout, container, false); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mPager = view.findViewById(R.id.viewPager); initListViews(); mPager.setAdapter(new MyPagerAdapter(mListViews)); } private void initListViews(){ MyListView l1 = new MyListView(getContext()); MyListView l2 = new MyListView(getContext()); MyListView l3 = new MyListView(getContext()); ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),R.layout.slide_inflict_list_item,data); l1.setAdapter(adapter);l2.setAdapter(adapter);l3.setAdapter(adapter); mListViews.add(l1);mListViews.add(l2);mListViews.add(l3); } public class MyPagerAdapter extends PagerAdapter{ public List<MyListView> mListViews; public MyPagerAdapter(List<MyListView> mListViews) { this.mListViews = mListViews; } @Override public int getCount() { return mListViews.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { container.addView(mListViews.get(position)); return mListViews.get(position); } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView(mListViews.get(position)); } } }
public class MyListView extends ListView { public MyListView(Context context) { super(context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private int mLastX,mLastY; @Override public boolean dispatchTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true);//表示父容器不能攔截此事件 break; case MotionEvent.ACTION_MOVE: int deltaX = x-mLastX; int deltaY = x-mLastY; if(Math.abs(deltaX)>Math.abs(deltaY)){ getParent().requestDisallowInterceptTouchEvent(false);//表示可以攔截 } break; default: break; } mLastX = x; mLastY = y; return super.dispatchTouchEvent(event); } }
public class MyPager extends ViewPager { public MyPager(@NonNull Context context) { super(context); } public MyPager(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { super.onInterceptTouchEvent(ev); return false;//必須要在down事件return false,否則listView就接收不到down事件,也就無法處理touchevent } return true; } }
原理解釋:
首先down事件傳遞給MyPager, down事件來的時候ViewGroup會重置標誌位,而且onInterceptTouchEvent方法一定會執行,所以這裡一定要返回false,ListView才會收到Down事件,否則listView是否發下拉的
按照上述程式碼,此後ViewGroup應該會執行第二塊程式碼塊去分發事件,即listView去處理事件,在ListView中的down事件呼叫getParent().requestDisallowInterceptTouchEvent(true)方法,會改變ViewGroup中mGroupFlags標誌位,進而影響ViewPager中對後續事件的攔截回撥的執行與否
當Move事件到來的時候,由於ListView在Down事件的時候設定了不攔截事件,則ViewPager也不會攔截Move事件,所以此事件落到listView去處理,在ListView中根據手指滑動情況去設定ViewPager是否攔截move事件:
到此這篇關於View事件分發原理和ViewPager+ListView巢狀滑動衝突的文章就介紹到這了,更多相關View的事件分發 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45