一:安卓事件分发的常用总结语句:
ViewGroup,先要走分发流程,如果没有孩子处理事件,自己就再走处理流程(自己处理事件)
View,只能走处理事件流程.
二:事件分发流程(简洁):
(1) down--首先确定down事件属于谁的?: 属于父容器的?属于子view的?
1.先看是否拦截后自己处理(即不分发下去)
2.如果不拦截,分发下去:
排序
遍历分发
领取事件的子View 处理该事件
3.没孩子领取处理该事件,再看下自己是否处理事件。
①如果父容器拿到了down事件,那么后续事件(move up)怎么处理?有父容器说了算,或者拦截,自己处理事件,或者事件分发给子view处理。
②如果子Veiw拿到了down事件 那么后续的事件(move up)有谁处理,由子View 说了算 ,或者子view请求父容器要拦截事件,让父容器处理事件(view.getParent().requestDisallowInterceptTouchEvent(false)(请求系统不要禁用父容器的拦截事件);或者子View自己处理事件.
(2) move--处理事件
1.先看是否拦截后自己处理(即不分发下去) (子view可以请求不拦截)
2.分发下去:
直接由down事件确定的view处理
三:控件滑动冲突的解决方案:
1.外部拦截法
外部滑动方向与内部滑动方向不一致
解决方案:外部拦截法,当事件传递到父View时,父View需要处理此事件,就拦截,不处理此事件就不拦截
2.内部拦截法
外部滑动方向与内部滑动方向一致;
解决方案:内部拦截法,当事件传递到父View时,父View都传递给子View,如果子View需要处理此事件就消耗掉,否则就交给父View处理。但是这种方法和Android事件分发不一致,需要配合 requestDisallowInterceptTouchEvent 方法才能正常工作
事件冲突案例1: 父容器 BadViewPager 与子view MylistView的冲突
public class MainActivity extends AppCompatActivity {
private int[] iv = new int[]{R.mipmap.iv_0, R.mipmap.iv_1, R.mipmap.iv_2,
R.mipmap.iv_3, R.mipmap.iv_4, R.mipmap.iv_5,
R.mipmap.iv_6, R.mipmap.iv_7, R.mipmap.iv_8};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewpager);
BadViewPager pager = findViewById(R.id.viewpager);
List<Map<String, Integer>> strings = new ArrayList<>();
Map<String, Integer> map;
for (int i = 0; i < 20; i++) {
map = new HashMap<>();
map.put("key", iv[i % 9]);
strings.add(map);
}
MyPagerAdapter adapter = new MyPagerAdapter(this, strings);
pager.setAdapter(adapter);
}
}
public class MyPagerAdapter extends PagerAdapter {
private Context mContext;
private List<Map<String, Integer>> mData;
public MyPagerAdapter(Context context, List<Map<String, Integer>> list) {
mContext = context;
mData = list;
}
@Override
public int getCount() {
return 5;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = View.inflate(mContext, R.layout.item_list, null);
ListView list = view.findViewById(R.id.list);
list.setAdapter(new SimpleAdapter(container.getContext(), mData, R.layout.item_base, new String[]{"key"}, new int[]{R.id.iv}));
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
使用内部拦截法:让子view(MyListView)处理事件冲突 当你 上下滑动子view myListView的同时可以左右滑动ViewPager 子Veiw不可以抢父容器的事件
/**
内部拦截法:子view(MyListView)处理事件冲突
* */
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
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: {
//todo 当down时,父容器(BadViewPager)会把事件往下分发给子view(父容器拿不到该事件了) 让子view(MyListVIew)来处理事件(子view 拿到down事件 )
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
// todo 如果Math.abs(deltaX) > Math.abs(deltaY) 判断出是左右滑动,
// todo 那么 子view 把该事件 让给父容器(BadViewPager)去处理 让 父容器会拦截事件,父容器处理该事件 那么你就可以左右滑动父容器(BadViewPager)
if (Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
}
外部拦截法:父容器BadViewPager处理冲突事件
当你左右滑动父容器 BadViewPager的同时不可以上下滑动myListView 父容器可以抢子Veiw的事件
/**
外部拦截法:父容器处理冲突事件
* 父容器想要把事件分发给谁就分发给谁
* */
public class BadViewPager extends ViewPager {
private int mLastX, mLastY;
public BadViewPager(@NonNull Context context) {
super(context);
}
public BadViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// if (event.getAction() == MotionEvent.ACTION_DOWN){
// super.onInterceptTouchEvent(event);
// return false;
// }
// return true;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastX = (int) event.getX();
mLastY = (int) event.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
//todo 如果 onInterceptTouchEvent =false 父容器(BadViewPager)不拦截事件 , 分发给子view (MyListView ) 父容器(BadViewPager) 不处理事件(那么父容器就不能左右滑动了)
// return false;
//todo 如果 onInterceptTouchEvent =true 父容器(BadViewPager)拦截事件 ,不分发给子view (MyListView ) 父容器(BadViewPager) 自己处理事件(那么父容器就可以左右滑动了)
return true;
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
return super.onInterceptTouchEvent(event);
}
}
事件冲突案例2: 父容器 SwipeRefreshLayout 与子view Viewpager的冲突
/**
* 父容器CustomSRL2来处理这个冲突事件
* */
public class CustomSRL2 extends SwipeRefreshLayout {
public CustomSRL2(Context context) {
super(context);
}
public CustomSRL2(Context context, AttributeSet attrs) {
super(context, attrs);
}
private int mLastX, mLastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
//todo down 父容器拿到事件 下面 父容器如何处理这个事件呢?
case MotionEvent.ACTION_DOWN: {
mLastX = (int) ev.getX();
mLastY = (int) ev.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
//todo 如果 Math.abs(deltaY) > Math.abs(deltaX) 上下垂直滑动, onInterceptTouchEvent=true 父容器SwipeRefreshLayout(上拉刷新) 拦截事件 自己处理 。
if (Math.abs(deltaY) > Math.abs(deltaX)) {
return true ;
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
}
/**
* 子view ViewPager
*
* */
public class CustomVPInner extends ViewPager {
private float startX;
private float startY;
private float x;
private float y;
private float deltaX;
private float deltaY;
public CustomVPInner(Context context) {
super(context);
}
public CustomVPInner(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = ev.getX();
startY = ev.getY();
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
x = ev.getX();
y = ev.getY();
deltaX = Math.abs(x - startX);
deltaY = Math.abs(y - startY);
if (deltaX < deltaY) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return super.dispatchTouchEvent(ev);
}
}