自定义ViewGroup,实现水平 HorizontalView

package com.imooc.dialogdemo.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

public class HorizontalView extends ViewGroup {

    private int lastInterceptX;
    private int lastInterceptY;
    private int lastX;
    private int lastY;

    private int currentIndex = 0;// 当前子元素
    int childWidth = 0;
    private Scroller scroller;
    private VelocityTracker velocityTracker;

    public HorizontalView(Context context) {
        super(context);
        initView();
    }

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

    public HorizontalView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        scroller = new Scroller(getContext());
        velocityTracker = VelocityTracker.obtain();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int withMode = MeasureSpec.getMode(widthMeasureSpec);
        int withSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        measureChildren(widthMeasureSpec,heightMeasureSpec);

        // 如果没有子 view 则宽和高都设置为 0
        if (getChildCount()==0){
            setMeasuredDimension(0,0);
        }
        // 处理 wrap_content 情况
        // 宽和高都是 AT_MOST: 宽度为所有子view 的宽度之和,高度为第一个子View 的高度
        else if (withMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){

            View childView = getChildAt(0);
            setMeasuredDimension(childView.getMeasuredWidth()*getChildCount(),childView.getMeasuredHeight());
        }
        //宽是 AT_MOST: 宽度为所有子view 的宽度之和
        else if (withMode == MeasureSpec.AT_MOST){
            View childView = getChildAt(0);
            setMeasuredDimension(childView.getMeasuredWidth()*getChildCount(),heightSize);
        }
        //高是 AT_MOST: 宽度为第一个子View 的高度
        else if (heightMode == MeasureSpec.AT_MOST){
            View childView = getChildAt(0);
            setMeasuredDimension(withSize,childView.getMeasuredHeight());
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        int left = 0;
        View child;
        for (int i = 0; i < childCount; i++) {
            child = getChildAt(i)
                    ;
            if (child.getVisibility() != GONE){
                int width = child.getMeasuredWidth();
                childWidth = width;
                child.layout(width,0,left+width,child.getMeasuredHeight());
                left+=width;
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        int x = (int) ev.getX();
        int y = (int) ev.getY();

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                intercept = false;
                // 动画没结束,则强制结束
                if (!scroller.isFinished()){
                    scroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = x - lastInterceptX;
                int deltaY = y - lastInterceptY;
                // 判断是不是水平滑动
                if (Math.abs(deltaX) - Math.abs(deltaY) > 0){
                    intercept = true;
                }else{
                    intercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
        }
        lastX = x;
        lastY = y;
        lastInterceptX = x;
        lastInterceptY = y;
        return intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        int x = (int) ev.getX();
        int y = (int) ev.getY();

        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                // 动画没结束,则强制结束
                if (!scroller.isFinished()){
                    scroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = x - lastX;
                scrollBy(-deltaX,0);
                break;
            case MotionEvent.ACTION_UP:
                int distance = getScrollX() - currentIndex*childWidth;
                if (Math.abs(distance)>childWidth/2){
                    if (distance>0){
                        currentIndex++;
                    }else{
                        currentIndex--;
                    }
                }else{
                    velocityTracker.computeCurrentVelocity(1000);
                    float xV = velocityTracker.getXVelocity();
                    if (Math.abs(xV)>50){
                        // 切换到上一个页面
                        if (xV>0){
                            currentIndex --;
                        }else{
                            currentIndex ++;
                        }
                    }
                }
                currentIndex = currentIndex < 0 ? 0: currentIndex > getChildCount() -1?
                        getChildCount()-1:currentIndex;
                smoothScrollTo(currentIndex * childWidth, 0);
                velocityTracker.clear();
                break;
        }
        lastX = x;
        lastY = y;
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()){
            scrollTo(scroller.getCurrX(),scroller.getCurrY());
            postInvalidate();
        }
    }

    /**
     * 弹性滑动到指定位置
     */
    private void smoothScrollTo(int destX, int destY) {
        scroller.startScroll(getScrollX(),getScrollY(),destX-getScrollX(),
                destY - getScrollY(),1000);
        invalidate();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值