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 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() {               //响应滑动查看更多的事件            }        });

更多相关文章

  1. Android(安卓)Tv ListView支持遥控器item获取焦点不能滚动的详解
  2. Android开发之拖动条/滑动条控件、星级评分控件功能的实例代码
  3. Listview中item与button类控件获取焦点问题
  4. Android(安卓)ViewPager嵌套ViewPager滑动冲突的解决方法
  5. Android学习之 使用依赖注入函数库Roboguice
  6. [九鼎RK3399Pro] Android(安卓)8.1默认自动给APP普通权限
  7. Android(安卓)中的TabHost控件的使用
  8. Android实现自定义菜单
  9. [置顶] Android点击Button实现功能的几种方法

随机推荐

  1. Hackborn的吐槽和Munn的剖析: Android(安
  2. Android多分辨率支持以及各种类型图标尺
  3. Flutter实现购物车动画
  4. XML解析和生成之--pull(android内置)
  5. 警惕针对中国移动手机用户的Android恶意
  6. Android(安卓)Launcher研究--手把手教你
  7. android自学总结
  8. Android手掌抑制功能的实现
  9. 我的Android进阶之旅------>Android中的
  10. App基于html/css/js的开发