Slide conflict in ListView use case 1

Keywords: Android Java

Use of ListView

Before we learn to use recyclerview, we must be struggling, hesitant, melancholy, confused in the world of ListView, but we can also go through this mess and enter a new world when we are really packaged.

Below you will find some pits in ListView that you have encountered in my project. Of course, you may also encounter pits in ListView, and there are different ways of handling them. This is only for those who know it, but for those who don't.

First, I'll send you a wave of'crashes'of all kinds of sliding conflicts I've encountered:

1) ScrollView internal application ListView, GridView scrolling conflict events

Processing method: Rewrite the onMeasure() method of ListView, code as follows
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

//Resolve ScrollView Scroll Conflict Events Applying to ListView Externally
public class MyListView extends ListView{

    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //Set the current ListVIew's height to the overall height of all the ListView's contents, which should not exceed Integer.MAX_VALUE >>2 at most
        int heightSpec;
        if (getLayoutParams().height==LayoutParams.WRAP_CONTENT){
            heightSpec= MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        }else {
            heightSpec=heightMeasureSpec;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

2) Solve scrolling issues with ScrolView nested ViewPager

Processing method: Rewrite the onMeasure() method of ScrolView, code as follows
Note: This may be the use of SimpleOnGestureListener, which is a class of different gesture handling when users touch the screen. The next article will give you detailed instructions on how to use it. Of course, if you are an old driver, you can also Baidu it directly, which also has a lot of reasonable explanations and instructions for use - -)
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
import android.widget.ScrollView;
import android.widget.Scroller;

/**
 * Customize ScrollView to fix: ScrollView nests ViewPager, causing ViewPager to not slide
 */
public class CustomScrollView extends ScrollView {
    private GestureDetector mGestureDetector;
    private int Scroll_height = 0;
    private int view_height = 0;
    protected Field scrollView_mScroller;
    private static final String TAG = "CustomScrollView";

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new YScrollDetector());
        setFadingEdgeLength(0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            stopAnim();
        }
        boolean ret = super.onInterceptTouchEvent(ev);
        boolean ret2 = mGestureDetector.onTouchEvent(ev);
        return ret && ret2;
    }

    // Return false if we're scrolling in the x direction
    class YScrollDetector extends SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (Math.abs(distanceY) > Math.abs(distanceX)) {
                return true;
            }
            return false;
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        boolean stop = false;
        if (Scroll_height - view_height == t) {
            stop = true;
        }

        if (t == 0 || stop == true) {
            try {
                if (scrollView_mScroller == null) {
                    scrollView_mScroller = getDeclaredField(this, "mScroller");
                }

                Object ob = scrollView_mScroller.get(this);
                if (ob == null || !(ob instanceof Scroller)) {
                    return;
                }
                Scroller sc = (Scroller) ob;
                sc.abortAnimation();

            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
        }
        super.onScrollChanged(l, t, oldl, oldt);
    }

    private void stopAnim() {
        try {
            if (scrollView_mScroller == null) {
                scrollView_mScroller = getDeclaredField(this, "mScroller");
            }

            Object ob = scrollView_mScroller.get(this);
            if (ob == null) {
                return;
            }
            Method method = ob.getClass().getMethod("abortAnimation");
            method.invoke(ob);
        } catch (Exception ex) {
            Log.e(TAG, e.getMessage());
        }
    }

    @Override
    protected int computeVerticalScrollRange() {
        Scroll_height = super.computeVerticalScrollRange();
        return Scroll_height;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed == true) {
            view_height = b - t;
        }
    }

    @Override
    public void requestChildFocus(View child, View focused) {
        if (focused != null && focused instanceof WebView) {
            return;
        }
        super.requestChildFocus(child, focused);
    }

    /**
     * Gets the hidden property of an object and sets the property to public to allow direct access
     * 
     * @return {@link Field} Returns null if unreadable; returned Field s require the user to cache themselves; this method does not cache
     */
    public static Field getDeclaredField(Object object, String field_name) {
        Class<?> cla = object.getClass();
        Field field = null;
        for (; cla != Object.class; cla = cla.getSuperclass()) {
            try {
                field = cla.getDeclaredField(field_name);
                field.setAccessible(true);
                return field;
            } catch (Exception e) {

            }
        }
        return null;
    }
}

3) Horizontal sliding conflict when ViewPager is applied internally

Processing method: Override onTouchEvent() method in ViewPager which slides horizontally inside, code as follows
import android.content.Context;
import android.graphics.PointF;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * Customize ViewPager to solve the problem of not sliding when ViewPagger is nested.
 */
public class HorizontalInnerViewPager extends ViewPager {
    /** Points pressed when touching**/
    PointF downP = new PointF();
    /** Current point at touch**/
    PointF curP = new PointF();

    public HorizontalInnerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    }
    public HorizontalInnerViewPager(Context context) {
        super(context);

        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);//default
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //Record the current pressed coordinates for each onTouch event
        curP.x = ev.getX();
        curP.y = ev.getY();

        if(ev.getAction() == MotionEvent.ACTION_DOWN){
            //Record coordinates when pressed
            //Keep in mind that downP = curP is not available, so when you change curP, downP will also change
            downP.x = ev.getX();
            downP.y = ev.getY();
            //This code is to inform his parent ViewPager that this control is now operating and that there is no interference with my operation
            getParent().requestDisallowInterceptTouchEvent(true);
        }

        if(ev.getAction() == MotionEvent.ACTION_MOVE){
            //This code is to inform his parent ViewPager that this control is now operating and that there is no interference with my operation
                getParent().requestDisallowInterceptTouchEvent(true);
        }

        return super.onTouchEvent(ev);
    }

}

4) Nested ScrollView in ViewPager and another sliding conflict event in ScrollView

** Processing method:
1) Determine the nesting model: My nesting is ViewPager ->ScrollView ->ViewPager;
2) Horizontal scrolling of the innermost ViewPager always triggers the outermost ViewPager scrolling, which can be handled by customizing the internal ViewPager (using the same process in 3);
3) However, the outer ScrollView does not scroll when the innermost ViewPager scrolls vertically at this time (add the impleOnGestureListener gesture sliding listening method in 2).**
import android.content.Context;
import android.graphics.PointF;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;

/**
 * Customize ViewPager to solve the problem of not sliding when ViewPagger is nested.
 */
public class HorizontalInnerViewPager extends ViewPager {
    /** Points pressed when touching**/
    PointF downP = new PointF();
    /** Current point at touch**/
    PointF curP = new PointF();

    /** Custom Gestures**/
    private GestureDetector mGestureDetector;

    public HorizontalInnerViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    }
    public HorizontalInnerViewPager(Context context) {
        super(context);

        mGestureDetector = new GestureDetector(context, new XScrollDetector());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);//default
        //Return true when the intercept touch event reaches this location.
        //Describes the onTouchEvent that intercepts the onTouch to execute the control
//        return true;
        //Child control handles this event near horizontal sliding, otherwise it is handled by parent control
//        return mGestureDetector.onTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //Record the current pressed coordinates for each onTouch event
        curP.x = ev.getX();
        curP.y = ev.getY();

        if(ev.getAction() == MotionEvent.ACTION_DOWN){
            //Record coordinates when pressed
            //Keep in mind that downP = curP is not available, so when you change curP, downP will also change
            downP.x = ev.getX();
            downP.y = ev.getY();
            //This code is to inform his parent ViewPager that this control is now operating and that there is no interference with my operation
            getParent().requestDisallowInterceptTouchEvent(true);
        }

        if(ev.getAction() == MotionEvent.ACTION_MOVE){
            float distanceX = curP.x - downP.x;
            float distanceY = curP.y - downP.y;
            //Near horizontal sliding, the ViewPager control captures gestures and scrolls horizontally
            if(Math.abs(distanceX) > Math.abs(distanceY)){
                //This code is to inform his parent ViewPager that this control is now operating and that there is no interference with my operation
                getParent().requestDisallowInterceptTouchEvent(true);
            }else{
                //Slide close to vertical, handled by parent control
                getParent().requestDisallowInterceptTouchEvent(false);
            }
        }

        return super.onTouchEvent(ev);
    }

    private class XScrollDetector extends GestureDetector.SimpleOnGestureListener{
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//            return super.onScroll(e1, e2, distanceX, distanceY);

            //Child control handles this event near horizontal sliding, otherwise it is handled by parent control
            return (Math.abs(distanceX) > Math.abs(distanceY));
        }
    }

}

Not finished yet...

Posted by zz50 on Sat, 01 Jun 2019 10:07:27 -0700