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

article/2025/9/25 2:51:14

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

http://chatgpt.dhexx.cn/article/9QbMTsvW.shtml

相关文章

Android HorizontalScrollView 水平滑动 在listview上面动态添加图片

Android HorizontalScrollView 水平滑动 listview 上动态添加图片 最近遇到了个 在listview展示广告的需要动态添加图片 如图&#xff1a; 使用了 horizontalScrollView 在listview上进行添加 java代码&#xff1a; package com.baozi.bzhorizontalscrollview;impor…

HorizontalScrollView实现Gallery

从简便的方式&#xff0c;继承LinearLayout再布局了添加视图&#xff0c;使其左右滚动。 主界面Activity: package com.xmz.activity;import java.util.HashMap;import java.util.Map;import android.app.Activity;import android.os.Bundle;public class MainActivity extend…

Android中HorizontalScrollView的使用总结

HorizontalScrollView是Google推出的用来滚动查看视图的控件&#xff0c;已经替代了Gallery。 由于HorizontalScrollView继承自FrameLayout&#xff0c;这意味着你只能在它下面放置一个子控件&#xff0c;即在控件内部只能放一个字控件&#xff08;一般使用LinearLayout&#…

Android控件——HorizontalScrollView使用(一)

1. HorizontalScrollView简单使用 Gallery&#xff08;画廊&#xff09;是一个锁定中心条目并且拥有水平滚动列表的视图&#xff0c;一般用来浏览图片&#xff0c;并且可以响应事件显示信息&#xff1b;Gallery还可以和ImageSwitcher组件结合使用来实现一个通过缩略图来浏览图…

android HorizontalScrollView讲解

前言 本章内容是android.widget.HorizontalScrollView&#xff0c;译为"横向滚动条"&#xff0c;版本为Android 2.3 r1&#xff0c;翻译来自"Tina"&#xff0c;感谢"Tina"为大家带来精彩的翻译稿 &#xff01;期待你加入Android API 中文的翻译&…

Android中HorizontalScrollView的使用

由于移动设备物理显示空间一般有限&#xff0c;不可能一次性的把所有要显示的内容都显示在屏幕上。所以各大平台一般会提供一些可滚动的视图来向用户展示数据。Android平台框架中为我们提供了诸如ListView、GirdView、ScrollView等滚动视图控件&#xff0c;这几个视图控件也是我…

Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果

转载请标明出处&#xff1a;http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后&#xff0c;Google推荐使用ViewPager和HorizontalScrollView来实现Gallery的效果。的确HorizontalScrollView可以实现Gallery的效果&#xff0c;但是Horizo…

HorizontalScrollView入门技术

HorizontalScrollView是一个滚动视图,可以帮助我们实现菜单栏之类的方法,实现左滑动右滑动. 常用于做一些APP的导航条,那么我们如何进行实现呢? 首先肯定是声明布局,做一个示范: (这是在一个相对布局中做的 上面是我们的HorizontalScrollView,下面是一个可以滑动的ViewPager…

横向滑动视图HorizontalScrollView精炼详解

一、前期基础知识储备 由于移动设备物理显示空间一般有限&#xff0c;不可能一次性的把所有要显示的内容都显示在屏幕上。所以各大平台一般会提供一些可滚动的视图来向用户展示数据。Android平台框架中为我们提供了诸如ListView、GirdView、ScrollView、RecyclerView等滚动视图…

HorizontalScrollView 详解

2019独角兽企业重金招聘Python工程师标准>>> gallrey由于浪费内存问题被和谐了&#xff0c;现在一般都使用这个代替了或第三方库 类概述 用 于布局的容器&#xff0c;可以放置让用户使用滚动条查看的视图层次结构&#xff0c;允许视图结构比手机的屏幕大。Horizonta…

【Android控件】HorizontalScrollView的基础使用记录(滚动条自定义)

目录​​​​​​​ 效果图 简介 注意事项 基础属性 滚动条全部设置 滚动条是否总显示 自定义滚动条滑动背景和滚动条背景 设置滚动条的宽度 设置滚动条距离 其它常规设置 设置滚动速度 布局代码示例 总结 效果图 简介 HorizontalScrollView是水平滚动标签。垂直…

MySQL读写分离配置

介绍 MySQL主从复制是一个异步的复制过程&#xff0c;底层是基于Mysql数据库自带的二进制日志功能。就是一台或多台MySQL数据库(slave&#xff0c;即从库)从另 一台MySQL数据库(master&#xff0c;即主库)进行日志的复制然后再解析日志并应用到自身&#xff0c;最终实现从库的…

MySQL 读写分离配置实践

文章目录 一、环境准备1. 查看主从复制状态2. 查看JDK版本3. 打开root的远程连接权限4. 安装MyCat 二、配置文件1. server.xml2. schema.xml 三、启动服务1. 配置文件问题一2. 配置文件问题二 四、MyCat 9066端口和8066端口1. 9066管理端口2. 8066数据端口 五、验证读写分离1. …

mysql读写分离中间件有哪些

mysql中间件有哪些 mysql-proxy是官方提供的mysql中间件产品可以实现负载平衡&#xff0c;读写分离&#xff0c;failover等&#xff0c;但其不支持大数据量的分库分表且性能较差。下面介绍几款能代替其的mysql开源中间件产品&#xff0c;Atlas&#xff0c;cobar&#xff0c;tdd…

配置mysql读写分离

准备起码三台服务器我这里准备了 192.168.0.63 mycat 192.168.0.64 主 192.168.0.65 从 如果是在多台 Linux 系统中组建的 MyCAT 集群&#xff0c;那需要在 MyCAT Server 所在的服务器上配置对 其他 IP 和主机名的映射&#xff0c;配置方式如下&#xff1a; vi /etc/h…

MySQL读写分离原理

文章目录 一、读写分离的概念二、引入中间件MyCat三、MyCat服务端口和管理端口 一、读写分离的概念 读写分离是基于主从复制来实现的。在实际的应用环境中&#xff0c;肯定是读操作多&#xff0c;就像我们在电商平台上去购买东西&#xff0c;可能看了100个也就买了一两个。所以…

Amoeba实现mysql读写分离

一、关于读写分离 amoeba : 英[ə’mi:bə] 读写分离&#xff08;Read/Write Splitting&#xff09;&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作&#xff08;INSERT、UPDATE、DELETE&#xff09;&#xff0c;而从数据库处理SELECT查询操作。 数据库复制被用…

Atlas中间件实现Mysql读写分离

目录 一、Atlas介绍 二、实现Mysql读写分离 1、实验环境 2、搭建一主一从配置 3、安装Atlas 一、Atlas介绍 [ˈtləs] Atlas 是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上&…

mysql 读写分离_详解MySQL读写分离

主从复制的原理 MySQL的主从复制和读写分离两者有着紧密的联系&#xff0c;首先要部署主从复制&#xff0c;只有主从复制完成了才能在此基础上进行数据的读写分离。 读写分离的原理 简单来说&#xff0c;读写分离就是只在主服务器上写&#xff0c;只在从服务器上读。基本原理是…

mysql-读写分离

1.什么是读写分离 在数据库集群架构中&#xff0c;让主库负责处理写入操作&#xff0c;而从库只负责处理select查询&#xff0c;让两者分工明确达到提高数据库整体读写性能。当然&#xff0c;主数据库另外一个功能就是负责将数据变更同步到从库中&#xff0c;也就是写操作。 2…