Android solves sliding conflicts

Android solves sliding conflicts

Generation of sliding conflict

Before discussing the method of solving sliding conflicts, we need to know under which scenarios sliding conflicts will occur. Sliding conflicts occur between two components (or more), and these two components are in a containment relationship (parent control and child control), when the parent control and child control can intercept the sliding event, but we did not Their sliding conflicts are handled, then only one control can respond to the sliding event at this time, we usually call this situation a sliding conflict between the parent control and the child control. There are three main scenarios:

Sliding conflict scene one

The sliding direction of the parent control is different from the sliding direction of the child control. The solution to this scenario is relatively simple. For example, we usually use ViewPager+RecyclerView (assuming that viewPager slides left and right, while RecyclerView slides up and down). This is sliding and we slide Scenario 1 of the conflict, but we did not have a sliding conflict when using this architecture, because it helped us realize the method of resolving the sliding conflict inside the viewPager. And if we don't use ViewPager to use other parent controls that can slide and the interior is a RecyclerView or ListView list, a sliding conflict will occur at this time.

The specific solution to sliding conflict scenario 1 is the external interception method, which is described in detail below:

external intercept method

The so-called external interception method means that the entire event sequence must first be intercepted by the parent control. When we need this event, we will intercept it, and if we do not need it, we will hand it over to the child control for processing. (If you don't understand the event distribution mechanism, please learn about Android's event distribution mechanism). We only need to rewrite the onInterceptTouchEvent() method of our parent control to decide whether to intercept the event, assuming that the external is ViewPager (swipe left and right, and here we assume that ViewPager does not handle sliding conflicts, the actual ViewPager source code has solved the problem of sliding conflicts), The interior is RecyclerView (sliding up and down), then we rewrite the onInterceptTouchEvent() method of ViewPager. The specific code logic is as follows:

package com.it.test;

import android.content.Context;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.viewpager.widget.ViewPager;

public class MyViewPager extends ViewPager {
    
    

    private float startX;
    private float startY;
    //使用directionSign来作为拦截的标志,0代表没有初始化,1代表父控件拦截,其他情况代表子控件拦截
    private int directionSign = 0; //只有在滑动开始时需要进行判断由谁进行拦截,之后的这一次事件中无需判断

    public MyViewPager(@NonNull Context context) {
    
    
        super(context);
    }

    //onInterceptTouchEvent返回true表示该viewPager拦截事件(不再向子控件中进行分发),返回false代表不拦截,进行分发事件(交由子控件RecyclerView处理)
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    
    
        boolean sign = false; //该标志标示是否拦截事件
        switch (ev.getAction()){
    
    
            case MotionEvent.ACTION_DOWN:
                //按下屏幕事件
                sign = false; //此处必须为false,如果为true,则后期滑动的事件将默认会被viewPager拦截,且不在调用onInterceptTouchEvent()方法
                //进行记录当前的坐标
                startX = ev.getX();
                startY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //滑动屏幕事件,具体解决滑动冲突的思路在此
                if(checkFatherNeed(ev)){
    
     //检查父控件ViewPager是否需要拦截,如果需要则进行修改sign = true
                    sign = true;
                }else{
    
    
                    sign = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                //松开屏幕事件
                sign = false; //此处为true或false意义不大,一般置为false
                break;
        }
        return sign;
    }

    private boolean checkFatherNeed(MotionEvent event) {
    
    
        if(directionSign == 0){
    
    
            //没有初始化,对directionSign进行初始化
            float currentX = event.getX();
            float currentY = event.getY();
            if(Math.abs(currentX - startX) > Math.abs(currentY-startY)){
    
    
                //代表水平滑动内容多,进行拦截事件
                directionSign = 1;
                return true;
            }else{
    
    
                //代表水平滑动内容多,交给子控件拦截事件
                directionSign = 2;
                return false;
            }
        }else if(directionSign == 1){
    
    
            //父控件拦截
            return true;
        }else{
    
    
            //子控件拦截
            return false;
        }
    }
}

The general solution is to judge the direction of the finger sliding (you can compare the horizontal sliding distance and the vertical sliding distance) to determine who will intercept and process the event. (Swipe left and right to ViewPager, and slide up and down to RecyclerView)

Sliding conflict scene two

The second scenario of sliding conflict is that the external sliding direction is the same as the internal sliding direction, for example, a RecyclerView that slides up and down is nested inside a ScrollView that slides up and down. The specific solution to this scenario is the internal interception method.

internal intercept method

The internal interception method means that we need to rewrite the dispatcherTouchEvent distribution method of the child control, and we can judge the state of the event to distribute the processing right to the parent control or the child control. (For example, when in a certain state, we want the child control to handle events, and other states are handed over to the parent control).

The specific implementation idea is as follows:

We need to rewrite the dispatcherTouchEvent method of the child control, and rewrite the onInterceptTouchEvent method of the parent control. We need to ensure that when the finger is pressed for a moment, that is, when the ACTION_DOWN event is generated, we need to ensure that the parent control does not intercept it, and then the child control needs to be distributed. Perform parent.requestDisallowInterceptTouchEvent(true) to prevent the parent control from intercepting the event. When we call parent.requestDisallowInterceptTouchEvent(false), the parent control can intercept the event.

The specific code is as follows:

child control

package com.it.test;

import android.content.Context;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

public class MyRecyclerview extends RecyclerView {
    
    

    float x;
    float y;
    
    public MyRecyclerview(@NonNull Context context) {
    
    
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
    
        switch (ev.getAction()){
    
    
            case MotionEvent.ACTION_DOWN:
                float x = ev.getX();
                float y = ev.getY();
                getParent().requestDisallowInterceptTouchEvent(true); //父控件之后不再拦截事件
                break;
            case MotionEvent.ACTION_MOVE:
                if(父控件需要此类事件){
    
    
                    getParent().requestDisallowInterceptTouchEvent(false); //父控件可以进行拦截事件  
                } 
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
}


parent control

package com.it.test;

import android.content.Context;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.viewpager.widget.ViewPager;

public class MyViewPager extends ViewPager {
    
    

    public MyViewPager(@NonNull Context context) {
    
    
        super(context);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    
    
        if(ev.getAction() == MotionEvent.ACTION_DOWN){
    
    
            return false; //不拦截
        }else{
    
    
            return true; //除了按下事件其他进行全部拦截(前提是子控件的parent.requestDisallowInterceptTouchEvent(false))
        }
    }

}

Sliding conflict scene three

The third scenario of sliding conflict is the combination or simultaneous occurrence of the above two situations. The solution is to use the joint use of internal interception and external interception.

Guess you like

Origin blog.csdn.net/weixin_45927121/article/details/124873613