The second series of smooth guide bars perfectly combined with ViewPager

introduction


proposed at the end of the previous

  • Cursor doesn't move when index slides
  • A complete swipe must be an index or a cursor, cannot coexist
  • Frequent and irregular sliding of ViewPager or guide bar will cause NullPointerException, because RecyclerView cannot find the inner View (so dispatchTouchEvent is rewritten)

These three flaws need to be fixed.

For the first point: the index slides because the RecyclerView will slide, and the cursor is added on the RelativeLayout, so the cursor will not slide in
response second point: Because a complete slide will be reset after the end state, which makes it impossible to switch the sliding object during the sliding process
. For the third point: due to the recycling and reuse mechanism of RecyclerView, the specific View of the desired item cannot be obtained.

Starting from these three questions, this time I used HorizontalScrollView and View to combine and encapsulate the control GuideSlideView.

A point that can be optimized immediately pops up here. The control is not reused. It is listed as a defect first, and subsequent improvements are made.

text


The layout of GuideSlideView is as follows

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <HorizontalScrollView
            android:id="@+id/horizontalscrollview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scrollbars="none">

            <LinearLayout
                android:id="@+id/linearlayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal" />
        </HorizontalScrollView>

        <View
            android:id="@+id/lineView"
            android:layout_width="10dp"
            android:layout_height="3dp"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="7dp"
            android:background="#444444" />
    </RelativeLayout>
</merge>

After all the Views in the index item are inflate, they are added to LinearLayout, and the linkage with ViewPager is carried out through scrollBy of HorizontalScrollView.

Key code analysis

        horizontalScrollView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (whichOne != 0) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            lastX = event.getX();
                            break;
                        case MotionEvent.ACTION_MOVE:
                            setScrollViewPager(event.getX() - lastX);
                            lastX = event.getX();
                            break;
                        case MotionEvent.ACTION_UP:
                        case MotionEvent.ACTION_CANCEL:
                            lastX = 0;
                            break;
                    }

                    return true;
                }

                return false;
            }
        });

    public ViewPager.OnPageChangeListener getOnPageChangeListener() {
        return new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                Log.i("onPageScrolled", "position-->" + position + "     positionOffset-->" + positionOffset + "     positionOffsetPixels-->" + positionOffsetPixels);
                setScroll(position, positionOffset);
            }
        };
    }

    private void setScroll(int position, float offset) {

        Log.i("setScroll", "position-->" + position + "     offset-->" + offset);

        if (!whetherOrNotToMove(position, offset)) {
            return;
        }

        if (!positiveDirection) {//根据方向设置偏移
            offset = offset - 1;
        }

        int x = calculateMoveLength(position, offset);

        move(x);
    }

    private boolean whetherOrNotToMove(int position, float offset) {
        if (position > currentPosition) {//第一次正向滑动触发
            positiveDirection = true;
            directionPrepare(position);
            return false;
        }
        if (position < currentPosition) {//第一次反向滑动触发
            positiveDirection = false;
            directionPrepare(position);
            return false;
        }
        if (position == 0 && offset == 0) {//滑到第一个 初始状态
            positiveDirection = true;
            directionPrepare(position);
            return false;
        }

        lineView.setVisibility(VISIBLE);
        ViewHolder viewHolder;
        if (positiveDirection) {
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
        } else {
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
            viewHolder.lineView.setVisibility(INVISIBLE);
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1));
        }
        viewHolder.lineView.setVisibility(INVISIBLE);

        if (offset == 0) {
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
            viewHolder.lineView.setVisibility(VISIBLE);
            lineView.setVisibility(INVISIBLE);
        }
        return true;
    }

    private void directionPrepare(int position) {
        resetState(position);
        if (whichOne != 1) {
            if (positiveDirection) {
                layoutLineView(position);
            } else {
                layoutLineView(position + 1);
            }
        }

        ViewHolder viewHolder;
        if (positiveDirection) {
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition));
        } else {
            viewHolder = new ViewHolder(linearLayout.getChildAt(currentPosition + 1));
        }
        viewHolder.lineView.setVisibility(VISIBLE);
        lineView.setVisibility(INVISIBLE);
    }

    private void resetState(int position) {//重置状态
        currentPosition = position;
        whichOne = 0;
        lastx = 0;
    }

    private void layoutLineView(int currentPosition) {//将游标布局到指定位置
        View item = linearLayout.getChildAt(currentPosition);
        lineViewMarginLeft = item.getLeft() - horizontalScrollView.getScrollX() + (item.getWidth() - lineView.getWidth()) / 2;

        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams();
        params.leftMargin = lineViewMarginLeft;
        lineView.setLayoutParams(params);

    }

    private int calculateMoveLength(int position, float offset) {
        int temp = position;
        if (temp >= linearLayout.getChildCount()) {
            temp = linearLayout.getChildCount() - 1;
        }
        View currentView = linearLayout.getChildAt(temp);//当前所指view

        int lastPosition = position + 1;
        if (lastPosition >= linearLayout.getChildCount()) {
            lastPosition = linearLayout.getChildCount() - 1;
        }
        View nextView = linearLayout.getChildAt(lastPosition);//将要滑动到的view

        Log.i("viewnull", temp + "   currentView-->" + currentView + "----------" + lastPosition + "     nextView-->" + nextView);

        int width = (currentView.getWidth() + nextView.getWidth()) / 2;//计算两view之间的总的滑动距离
        return Math.round(width * offset);//此次滑动的距离
    }

    private void move(int x) {
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) lineView.getLayoutParams();
        if (whichOne == 0) {//判断   一次完整的滑动   是否结束      一次滑动结束后 whichOne=0
            if (positiveDirection) {
                if (params.leftMargin % screenWidth > screenWidth / 2 && horizontalScrollView.canScrollHorizontally(1)) {//游标在右半边且recyclerView可以右滑 则滑动recyclerView
                    whichOne = whichOne | 1;
                } else {
                    whichOne = whichOne | 2;
                }
            } else {
                if (params.leftMargin % screenWidth < screenWidth / 2 && horizontalScrollView.canScrollHorizontally(-1)) {//游标在左半边且recyclerView可以左滑 则滑动recyclerView
                    whichOne = whichOne | 1;
                } else {
                    whichOne = whichOne | 2;
                }
            }
        }

        if (whichOne == 1) {
            horizontalScrollView.scrollBy(x - lastx, 0);//因为scrollBy的滑动是累加的,所以必须减去上次滑动的距离,实际移动的距离为差值  exp:第一次滑动1 第二次滑动2,第二次滑动3,scrollBy会滑动6,但实际我只想滑动3
            lastx = x;
        } else {
            params.leftMargin = lineViewMarginLeft + x;
            lineView.setLayoutParams(params);
        }
    }

    //GuideSlideView 滑动联动ViewPager
    private void setScrollViewPager(float length) {
        viewPager.scrollBy(-Math.round(length), 0);
    }

The same focus is to obtain the position and offset in the onPageScrolled callback, and then call setScroll. The difference is that each ViewHolder has its own cursor. After the sliding is completed, it is necessary to control the display of the ViewHolder and the full-screen running cursor when sliding to a specific position. condition.

Another difference is that OnTouchListener is set on HorizontalScrollView, which is to meet the needs of multiple touch points. When ViewPager slides to half and another touch point drags the guide bar, ViewPager will slide with it.

If you read the previous article , there is not much difference in the logic of sliding here, just replace RecyclerView with HorizontalScrollView

Effect

renderings
The screenshot may not be clear, and the cursor will flicker when swiping quickly

defect

  • Controls are not reused
  • When swiping quickly, the cursor flashes

In order to solve the above problems, see the third series of smooth guide bars that are perfectly combined with ViewPager

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325994742&siteId=291194637