1. 程式人生 > >Android橫向滑動載入更多的控制元件的實現---HorizontalScrollSlideView

Android橫向滑動載入更多的控制元件的實現---HorizontalScrollSlideView

Android橫向滑動載入更多的控制元件的實現—HorizontalScrollSlideView

需求

之前公司業務要求做一個橫向滑動的,可以載入更多的控制元件,第一時間想到的就是 RecyclerView 來實現 ,後面仔細想想滑動攔截不好控制等等
所以就換了個思路來實現了。

思路:

控制元件繼承自LinearLayout,外面包裹一層HorizontalScrollView,並重寫dispatchTouchEvent()事件,當橫向滑動到LinearLayout的右邊緣滑動到控制元件的右邊緣時,將隱藏的側拉頭跟隨手勢慢慢拉出。這中間伴隨著側拉頭的狀態實時的改變。鬆手時,如果側拉的距離已經足夠多,則回撥
OnSlideBottomListener 。當回撥結束時,回彈底部箭頭,讓側拉頭再次隱藏。 

大概的思路是這樣的:

這裡寫圖片描述

先上效果圖:

效果圖

先說件很操蛋的事情就是 HorizontalScrollView的滑動監聽事件是protected為了在外面能拿到這個滑動監聽,所以先把 這個控制元件重寫了把滑動事件先回調回來。

public class ObservableScrollView extends HorizontalScrollView {
    private OnScrollChangedListener onScrollChangedListener;

    public ObservableScrollView(Context context) {
        super
(context); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setOnScrollListener(OnScrollChangedListener onScrollChangedListener) { this
.onScrollChangedListener = onScrollChangedListener; } @Override protected void onScrollChanged(int x, int y, int oldX, int oldY) { super.onScrollChanged(x, y, oldX, oldY); if (onScrollChangedListener != null) { onScrollChangedListener.onScrollChanged(x, y, oldX, oldY); } } public interface OnScrollChangedListener { void onScrollChanged(int x, int y, int oldX, int oldY); } }

接下來便是我們的主要實現的了:

public class HorizontalScrollSlideView extends LinearLayout implements ObservableScrollView.OnScrollChangedListener {
    private static final String TAG = "ScrollSlideView";

    //移動觸發步幅
    private final int MOVE_STRIDE = 6;
    //記錄移動x
    private float mRecodX;
    //記錄偏移量
    private float mOffsetX;

    //底部分界線位置
    private int mBottomParting;
    //底部展示區長度
    private int mBottomShow;
    //底部觸發區長度
    private int mBottomAll;
    //是否有觸控
    private boolean isDown = false;

    private Handler mHandler;
    //滑動觸發的監聽
    private OnSlideBottomListener mOnSlideBottomListener;
    //內容外部的滑動view
    private ObservableScrollView mScroolView;
    //包裹內容view
    private LinearLayout mContentView;
    //底部展示view
    private View mBottomShowView;
    //底部觸發到監聽的view
    private View mBottomGoView;

    private boolean needScrollBottom = true;

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

    public HorizontalScrollSlideView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHandler = new Handler();
        //LayoutInflater.from(context).inflate(R.layout.horizontal_scroll_slide_view, this);
        //LayoutInflater.from(context).inflate(R.layout.horizontal_scroll_slide_view_bottom, this);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        mScroolView = new ObservableScrollView(context);
        mContentView = new LinearLayout(context);
        mScroolView.setLayoutParams(lp);
        mContentView.setLayoutParams(new ViewGroup.LayoutParams(lp));
        mScroolView.addView(mContentView);
        mScroolView.setHorizontalScrollBarEnabled(false);
        addView(mScroolView);
    }

    /**
     * 設定滑動區的內容
     *
     * @param views
     */
    public void setContentViews(List<View> views) {
        mContentView.removeAllViews();
        for (View view : views) {
            mContentView.addView(view);
        }
    }


    public void setContentView(View view) {
        mContentView.removeAllViews();
        mContentView.addView(view);
    }


    public ViewGroup getContentContainer() {
        return mContentView;
    }

    /**
     * 設定觸發goveiw的監聽
     *
     * @param listener
     */
    public void setOnSlideBottomListener(OnSlideBottomListener listener) {
        mOnSlideBottomListener = listener;
    }

    /**
     * 覆蓋後,返回自定義底部view
     *
     * @return 底部展現view
     */
    protected View getBottomShowView() {
        TextView textView = new TextView(getContext());
        textView.setText("繼續滑動\n檢視全部");
        textView.setGravity(Gravity.CENTER);
        textView.setClickable(false);
        textView.setEnabled(false);
//        textView.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
        textView.setTextColor(getContext().getResources().getColor(R.color.gray_616161));
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(dp2px(100), ViewGroup.LayoutParams.MATCH_PARENT);
        textView.setLayoutParams(lp);
        return textView;
    }

    /**
     * 覆蓋後,返回自定義底部觸發view
     *
     * @return 底部觸發view
     */
    protected View getBottomGoView() {
        TextView textView = new TextView(getContext());
        textView.setText("->");
        textView.setGravity(Gravity.CENTER);
//        textView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
        textView.setTextColor(getContext().getResources().getColor(R.color.gray_616161));
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(dp2px(20), ViewGroup.LayoutParams.MATCH_PARENT);
        textView.setLayoutParams(lp);
        return textView;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
//        mScroolView = findViewById(R.id.sv);
//        mContentView = findViewById(R.id.content);
        //mBottomShowView = findViewById(R.id.bottom_show);
        //mBottomGoView = findViewById(R.id.bottom_go);
        mScroolView.setOnScrollListener(this);

        View showView = getBottomShowView();
        if (showView != null) {
            addView(showView);
            mBottomShowView = showView;
        }

        View goView = getBottomGoView();
        if (goView != null) {
            addView(goView);
            mBottomGoView = goView;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mBottomShow = mBottomShowView.getWidth();
        mBottomAll = mBottomShow + mBottomGoView.getWidth();
        mBottomParting = mBottomShow / 2;
//        Log.i(TAG, "onmeassure: " + mBottomAll);
    }

    @Override
    public void onScrollChanged(int x, int y, int oldX, int oldY) {
        if (!isDown && x > oldX && isScrollBottom(true)) {
            setScrollX(mBottomShow);
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
//        Log.i(TAG, "dispatch: " + ev.getAction());
        if (isScrollBottom(true) || getScrollX() > 0) {
            handleTouch(ev);
        } else {
            mRecodX = ev.getX();
        }

        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            isDown = true;
        } else if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
            isDown = false;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //消費掉,保證dispatchTouchevent
        if (needScrollBottom) {
            ViewParent parent = this;
            while (!((parent = parent.getParent()) instanceof ViewPager))
                parent.requestDisallowInterceptTouchEvent(true);
        }
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean isIntercept = isScrollContentBottom() && ev.getAction() == MotionEvent.ACTION_MOVE;
//        Log.i(TAG, "onInterceptTouchEvent: " + ev.getAction() + "  isINtercept:" + isIntercept);
        if (isIntercept)
            getParent().requestDisallowInterceptTouchEvent(true);
        return isIntercept ? true : super.onInterceptTouchEvent(ev);
    }

    private boolean isScrollBottom(boolean isIncludeEqual) {
        int sx = mScroolView.getScrollX();
        int cwidth = mScroolView.getChildAt(0).getWidth();
        int pwidth = getWidth();
//        Log.i(TAG, "sx: " + sx + "cwidth: " + cwidth + "pwidth: " + pwidth);

        if (needScrollBottom)
            return isIncludeEqual ? sx + pwidth >= cwidth : sx + pwidth > cwidth;
        else
            return false;
    }

    public void setNeedScrollBottom(boolean needScrollBottom) {
        this.needScrollBottom = needScrollBottom;
    }


    private boolean isScrollContentBottom() {
        return getScrollX() > 0;
    }


    private boolean handleTouch(MotionEvent event) {

//        Log.i(TAG, "handletouch: " + event.getAction());
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mRecodX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mRecodX == 0)
                    mRecodX = event.getX();
                //移動的距離
                mOffsetX = (event.getX() - mRecodX);
                //是否達移動最小值
                if (Math.abs(mOffsetX) < MOVE_STRIDE) {
                    return true;
                }
                //手指左滑
                boolean isLeft = event.getX() - mRecodX < 0;
                mRecodX = event.getX();
                if (isLeft && getScrollX() >= mBottomAll) {
                    setScrollX(mBottomAll);
                    //Log.i(TAG,"1");
                } else if (!isLeft && getScrollX() <= 0) {
                    setScrollX(0);
                    //Log.i(TAG,"2");
                } else {
                    setScrollX((int) (getScrollX() - mOffsetX));
                    //Log.i(TAG,"3");
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                if (getScrollX() < mBottomParting) {
                    setScrollX(0);

                } else {
                    int delay = 0;
                    if (getScrollX() >= mBottomAll - MOVE_STRIDE) {
                        Log.i(TAG, "slide bottom!");
                        if (mOnSlideBottomListener != null) {
                            mOnSlideBottomListener.onSlideBottom();
                        }
                        delay = 1000;
                    }
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            setScrollX(mBottomShow);
                        }
                    }, delay);

                }
                break;
        }
        return true;
    }

    int dp2px(int dp) {
        return (int) (getContext().getResources().getDisplayMetrics().density * dp + 0.5f);
    }

    public interface OnSlideBottomListener {
        void onSlideBottom();
    }

}

使用起來也很簡單,就不單獨寫demo了:

horScrollView.setContentView(contanteView);
horScrollView.setOnSlideBottomListener(new HorizontalScrollSlideView.OnSlideBottomListener() {
            @Override
            public void onSlideBottom() {
               //響應滑動檢視更多的事件
            }
        });