自定义类似于ViewPager的可上下滑动切换效果的视图

东郭淇
2023-12-01

今天给大家分享一个使用Scroller滚动类实现的控件,我们知道,viewpager实现的是一种左右切换的效果,使用scroller+事件分发来实现viewpager本身并不是一件特别难的事,因为viewpager其内部的实现原理就是这样。为了跟大家更好地分享下scroller的用法,我实现了一个可以上下切换的效果。下面直接贴该类代码:

1.VDViewPager.class

import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * Created by _H_JY on 2016/3/3.
 * instruction:A custom viewpager which srcolls to vertical direction.
 */

public class VDViewPager extends ViewGroup{

    /*滚动实例*/
    private Scroller mScroller;

    /*判断是否滑动的最小距离*/
    private int mTouchSlop;

    /*手指触摸到屏幕时y值*/
    private float mYDown;

    /*手指在竖直方向上移动的距离*/
    private float mYMove;

    /*标记最后的位置*/
    private float mLastMove;

    /*标记上边缘*/
    private int mTopBorder;

    /*标记下边缘*/
    private int mBottomBorder;

    public VDViewPager(Context context) {
        this(context, null);
    }

    public VDViewPager(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VDViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        /*初始化滚动实例*/
        mScroller = new Scroller(context);

        /*获取最小滑动值,该值用于判断是否可滑*/
        ViewConfiguration vc = ViewConfiguration.get(context);
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int cCount = this.getChildCount();
        for (int i=0;i<cCount;i++){
            View childView = this.getChildAt(i);
            /*测量每一个子View*/
            measureChild(childView,widthMeasureSpec,heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if(changed){
            int cCount = this.getChildCount();
            for (int i=0;i<cCount;i++){
                View childView = this.getChildAt(i);
                /*从上往下布局*/
                childView.layout(0,i*childView.getMeasuredHeight(),childView.getMeasuredWidth(),(i+1)*childView.getMeasuredHeight());
            }
            mTopBorder = this.getChildAt(0).getTop();
            mBottomBorder = this.getChildAt(this.getChildCount()-1).getBottom();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                mYDown = ev.getRawY();
                mLastMove = mYDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mYMove = ev.getRawY();
                int diff = (int) Math.abs(mYMove - mYDown);
                if(diff > mTouchSlop){ //判断是否可滑
                    return true;
                }
                mLastMove = mYMove;
                break;
            default:
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
                mYMove = event.getRawY();
                int scrolledY = (int) (mLastMove - mYMove);
                if(this.getScrollY() + scrolledY < mTopBorder){
                    /*到第一个子View顶端时不给下滑*/
                    this.scrollTo(mTopBorder,0);
                    return true;
                }else if(this.getScrollY() + scrolledY + this.getHeight() > mBottomBorder){
                    /*到最后一个子View底端时不给上滑*/
                    this.scrollTo(0,mButtomBorder - this.getHeight());
                    return true;
                }
                this.scrollBy(0,scrolledY);
                mLastMove = mYMove;
                break;
            case MotionEvent.ACTION_UP:
                int targetIndex = (this.getScrollY()+this.getHeight()/2)/this.getHeight();
                /*得到应该滑动的距离*/
                int dy = targetIndex*this.getHeight() - this.getScrollY();
                /*开始滑动*/
                mScroller.startScroll(0,this.getScrollY(),0,dy);
                invalidate();
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }


    @Override
    public void computeScroll() {
        super.computeScroll();
        /*平滑滚动*/
        if(mScroller.computeScrollOffset()){
            this.scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            invalidate();
        }
    }
}

上面的注释写得也比较清晰了,相信大家都能看懂。



2.布局文件(activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<g.scrolltest.view.VDViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="g.scrolltest.MainActivity">


    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="One"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Two"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Three"/>

</g.scrolltest.view.VDViewPager>

布局文件相当简单,就三个按钮,没什么好说的额。。。


3.MainActivity.class(主Activity)

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }


}

啥都没写,更不用说。。。运行效果图就不贴了。哈哈我太懒了


 类似资料: