自定义仿Viewpager

自定义仿Viewpager

public class MyViewPager extends ViewGroup {
    /**
     * 手势识别器
     * 在onTouchEvent方法中把事件传递给手势识别器
     */
    private GestureDetector detector;
    /**
     * 当前页面的下标位置
     */
    private int currentIndex;
    /**
     * 起始坐标位置
     */
    private float startX;

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

    private void initView(final Context context) {
        /**
         * 实例化手势识别器
         */
        detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
//            /**
//             * 长按
//             * @param e
//             */
//            @Override
//            public void onLongPress(MotionEvent e) {
//                super.onLongPress(e);
//                Toast.makeText(context, "长按", Toast.LENGTH_SHORT).show();
//            }
//
//            /**
//             * 双击
//             * @param e
//             * @return
//             */
//            @Override
//            public boolean onDoubleTap(MotionEvent e) {
//                Toast.makeText(context, "双击", Toast.LENGTH_SHORT).show();
//                return super.onDoubleTap(e);
//            }

            /**
             * 滑动
             * @param e1 按下
             * @param e2 放开
             * @param distanceX 横坐标滑动的距离
             * @param distanceY 纵坐标滑动的距离
             * @return
             */
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                /**
                 * 通过此方法实现滑动
                 */
                scrollBy((int) distanceX, 0);


                return true;
            }
        });

    }

    /**
     * 布局
     *
     * @param changed
     * @param l       左上角横坐标
     * @param t       左上角纵坐标
     * @param r       右下角横坐标
     * @param b       右下角纵坐标
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 遍历子View并为其指定在屏幕的坐标位置
        for (int i = 0; i < getChildCount(); i++) {
            View childAt = getChildAt(i);
            childAt.layout(i * getWidth(), 0, (i + 1) * getWidth(), getHeight());
        }
    }


    /**
     * 测量
     * 根据widthMeasureSpec求得宽度width和父View给的模式
     * 根据自身宽度width和自身的padding值相减,求得子view可以获得的宽度newWidth
     * 根据newWidth和模式求得一个新的MeasureSpec值:
     * MeasureSpec.makeMeasureSpec(newSize,newMode);
     * 用新的MeasureSpec来计算子View
     *
     * @param widthMeasureSpec  父层视图给当前视图的宽和模式
     * @param heightMeasureSpec 系统测量时测量多次
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int sizeW = MeasureSpec.getSize(widthMeasureSpec);
        int modeW = MeasureSpec.getMode(widthMeasureSpec);
        int sizeH = MeasureSpec.getSize(heightMeasureSpec);
        int modeH = MeasureSpec.getMode(heightMeasureSpec);

        Log.e("sizeW=", sizeW + "");
        Log.e("modeW=", modeW + "");
        Log.e("sizeH=", sizeH + "");
        Log.e("modeH=", modeH + "");
        for (int i = 0; i < getChildCount(); i++) {
            View childAt = getChildAt(i);
            // 测量每个子View的宽高
            childAt.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }


    /**
     * 事件的分发
     * Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件
     * 还是继续分发给子控件处理
     * 返回true表示不继续分发,事件没有被消费,返回false则继续向下分发
     * 如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截
     *
     * @param ev
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //TODO 条件
        // 将事件分发给孩子
        //getChildAt(0).dispatchTouchEvent(ev);
        return super.dispatchTouchEvent(ev);
    }


    private float downX;
    private float downY;

    /**
     * 事件的拦截
     * 该方法是ViewGroup特有的方法,负责事件的拦截
     * 返回true表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理
     * 返回false则不拦截继续往下传递
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        detector.onTouchEvent(ev); //防止突然闪动
        //默认传递给孩子
        boolean result = false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e("onInterceptTouchEvent", "ACTION_DOWN");
                // 记录坐标
                downX = ev.getX();
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("onInterceptTouchEvent", "ACTION_MOVE");
                // 记录结束值
                float endX = ev.getX();
                float endY = ev.getY();
                // 计算绝对值
                float distanceX = Math.abs(endX - downX);
                float distanceY = Math.abs(endY - downY);
                if (distanceX > distanceY && distanceX > 5) {
                    //如果横向移动距离大于纵向移动距离则拦截事件
                    result = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                Log.e("onInterceptTouchEvent", "ACTION_UP");
                break;
        }
        return result;
    }

    /**
     * 触摸事件
     * 用于事件的处理,返回true表示消费处理当前事件,返回false则不处理
     * 交给子控件继续分发
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        // 把事件传递给手势识别器
        detector.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e("onTouchEvent", "ACTION_DOWN");
                startX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("onTouchEvent", "ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e("onTouchEvent", "ACTION_UP");
                float endX = event.getX();
                int tmpIndex = currentIndex;
                if ((startX - endX) > getWidth() / 2) {
                    // 显示下一个页面
                    tmpIndex++;
                } else if ((endX - startX) > getWidth() / 2) {
                    tmpIndex--;
                }
                /**
                 * 根据下标位置移动到指定页面
                 */
                scrollToPager(tmpIndex);
                break;
        }
        return true;
    }

    /**
     * 屏蔽非法值,根据位置移动到指定页面
     *
     * @param tmpIndex
     */
    private void scrollToPager(int tmpIndex) {
        if (tmpIndex < 0) {
            tmpIndex = 0;
        }
        if (tmpIndex > getChildCount() - 1) {
            tmpIndex = getChildCount() - 1;
        }
        //当前变量的下标位置
        currentIndex = tmpIndex;
        /**
         * 手放开后自动完成滑动
         *
         */
        //float distanceX  = currentIndex*getWidth()-getScrollX();
//        scrollTo(currentIndex*getWidth(),0);
        /**
         * 使用数值发生器完成剩下的滑动
         */
        ValueAnimator valueAnimator = ValueAnimator.ofInt(getScrollX(), currentIndex * getWidth());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                scrollTo(value, 0);
            }
        });
        valueAnimator.setDuration(500).start();

    }
}

这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值