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;}@Overrideprotected 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;//记录移动xprivate float mRecodX;//记录偏移量private float mOffsetX;//底部分界线位置private int mBottomParting;//底部展示区长度private int mBottomShow;//底部触发区长度private int mBottomAll;//是否有触摸private boolean isDown = false;private Handler mHandler;//滑动触发的监听private OnSlideBottomListener mOnSlideBottomListener;//内容外部的滑动viewprivate ObservableScrollView mScroolView;//包裹内容viewprivate LinearLayout mContentView;//底部展示viewprivate View mBottomShowView;//底部触发到监听的viewprivate 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;}@Overrideprotected 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;}}@Overrideprotected 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);}@Overridepublic void onScrollChanged(int x, int y, int oldX, int oldY) {if (!isDown && x > oldX && isScrollBottom(true)) {setScrollX(mBottomShow);}}@Overridepublic 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);}@Overridepublic boolean onTouchEvent(MotionEvent event) {//消费掉,保证dispatchToucheventif (needScrollBottom) {ViewParent parent = this;while (!((parent = parent.getParent()) instanceof ViewPager))parent.requestDisallowInterceptTouchEvent(true);}return true;}@Overridepublic 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;elsereturn 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() {@Overridepublic 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() {@Overridepublic void onSlideBottom() {//响应滑动查看更多的事件}});

Android横向滑动加载更多的控件的实现---HorizontalScrollSlideView相关推荐

  1. 【Android归纳】基于XListView的下拉刷新、上拉加载更多的控件分析

    目录 前言 功能介绍 总体设计 组成 类关系图 详细设计 XlistViewHeader原理分析 XListViewFooter原理分析 XListView原理分析 代码带注释下载 目录 前言 如果你 ...

  2. 程矢Axure夜话:Axure手机原型视频教程之中继器上下滑动加载更多

    案例描述:中继器上下滑动加载更多 知识点: 中继器复合使用 效果图: 在线效果预览:(firefox原型文件) AxShare在线效果预览: 原型下载地址:中继器上下滑动加载更多.rp 在线视频: 实 ...

  3. 为系统加载右键注册控件选项【VB 注册控件】

    做VB的,经常注册和反注册OCX控件和DLL链 但是,每次都要 "开始"--"运行" --"regsvr32 C:\********\******.o ...

  4. 【MFC】动态加载Picture Control控件中的图片

    [MFC]动态加载Picture Control控件中的图片 前言 方法1:CBrush 方法2:SetBitmap 参考链接 方法3:重写MyPictureControl 前言 在MFC窗体中,我们 ...

  5. android listview自动加载更多,如何实现 Android ListView『上拉加载更多』?

    ListView上拉加载更多的UI需求 (1)向上滑动 ListView,当最后一个条目滚入屏幕时开始加载更多条目,在列表底部增加一个 footerView:一个 infinite progressB ...

  6. Android UI滑动加载源码

    2019独角兽企业重金招聘Python工程师标准>>> android UI 往右滑动,滑动到最后一页就自动加载数据并显示 如图: Java代码 package cn.anycall ...

  7. Android之横向滑动的广告(网格控件)

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/53264494 本文出自:[顾林海的博客] ##前言 很早以前写过一篇自 ...

  8. android 加载更多,如何在android中实现加载更多的recyclerview

    我想在Recyclerview中实现更多的加载.这是代码. 代码来自 github. https://gist.github.com/ssinss/e06f12ef66c51252563e MainA ...

  9. QT利用QAxWidget加载IE浏览器控件COM技术

    2019独角兽企业重金招聘Python工程师标准>>> 如何通过QAxWidget窗口加载COM组件?本文以加载微软的IEWebBrowser2组件为列,介绍详细的步骤: 1.增加一 ...

最新文章

  1. 适合初学者的Python小游戏开发,不仅有趣还能巩固自己所学知识
  2. 去除右键的一键备份到115网盘”
  3. PHP类的静态(static)方法和静态(static)变量使用介绍
  4. overfit underfit
  5. EqualLogic全攻略视频[(四)高级管理]
  6. Recbole自定义训练集、验证集和测试集推荐
  7. 开源项目参与_通过更好的文档吸引更多的项目参与人
  8. 深入理解ARM体系架构(S3C6410)---PWM实例
  9. C#3.0 为我们带来什么(3) —— 初始化器
  10. Spring Boot 学习之,数据库一 Spring-Data-Jpa 初探
  11. 如何聊离职原因,向面试官展示自己的忠诚
  12. 休闲娱乐游戏-贱鸟跳跳
  13. c#执行cmd命令并获取返回结果字符串
  14. Nginx 这一牛X的功能,你知道吗
  15. ZOJ Problem Set - 1292 Integer Inquiry
  16. 勒索病毒应急措施及防护方案
  17. 大学计算机大一上学期考试试题及答案,武汉理工大学大一上学期计算机基础试题题库及答案...
  18. PPT之背景与标题搭配
  19. 利用Python计算农历日期
  20. Firebird学习(02):数据库的中文参考资料

热门文章

  1. Android Tab 控件的使用
  2. Linux安装配置ssh 基于unbantu22.04.1 LTS版本
  3. SAP中导出物料评估类对应关系清单
  4. ios系统设置z-index不生效问题
  5. HBase的协处理器(Coprocessor)、HBase如何使用二级索引、observer协处理器、 endpoint协处理器、Hbase 协处理器加载方式
  6. win11无法安装msi程序的解决方法
  7. Persist Security Info
  8. C语言实现归并排序——2路归并排序
  9. Going out on a limb:Joint Extraction of Entity Mentions and Relations without Dependency Trees【论文笔记】
  10. MYSQL数据库(九)- 修改数据表名称、列名称