实现一个网易云音乐的 BottomSheetDialog
作者:林冠宏 / 指尖下的幽灵
掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8
博客:http://www.cnblogs.com/linguanh/
GitHub : https://github.com/af913337456/
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities
目录
- 前序
- 直观对比下 gif 效果
- Android SDK 自带的
BottomSheetDialog
- 网易云音乐 的
BottomSheetDialog
- 我开源 的仿网易云音乐
BottomSheetDialog
- Android SDK 自带的
- 核心代码简述
前序:
因为
APP
需要参照
到网易云音乐的 BottomSheetDialog
的效果,找了一圈没找到,所以动手写了一个,涉及圈子里经常露面的知识点有下面三点,也是个实战应用
- 事件分发系列的--冲突处理 & 分发顺序
- View 绘制流程的--Measure 模式
- 相对屏幕取 View 的坐标
先来直观对比下 gif 效果
- 首先是-- Android SDK 自带的
BottomSheetDialog
- 然后是--网易云音乐 的
BottomSheetDialog
- 最后是--我开源 的仿网易云音乐
BottomSheetDialog
首先是-- Android SDK 自带的 BottomSheetDialog
下面的 gif 图是一个Android SDK 自带的 BottomSheetDialog
内部加了 RecyclerView
列表控件的效果
可以看出:
- 下滑动作会收起,隐藏掉
dialog
- 上滑会完全展开
- 展开后,才能滑动
RecyclerView
内部
其次
- 如果你内部使用的是
ListView
列表控件,你会发现会有其他奇怪的情况。
然后是--网易云音乐 的 BottomSheetDialog
下面的 gif 图是一个Android 版 网易云音乐
的BottomSheetDialog
效果
可以看出:
- 下滑动作会有
范围
回弹,也就是下滑到一定距离才会收起,隐藏掉dialog
- 上滑不给展开
- 能够在半展开的情况下,内嵌滑动列表控件,例如
listView
- 和列表控件滑动不冲突,在
列表控件
滑尽的时候,可以下滑隐藏dialog
最后是--我开源 的仿网易云音乐 BottomSheetDialog
可以看出,效果和网易云的一样
核心代码简述
SDK 的 BottomSheetDialog 内部布局的结构如下:
--FrameLayout --|--CoordinatorLayout --|--|--FrameLayout --|--|--|--Our ContentView // 最后是我们设置的 ContentView
CoordinatorLayout 在 Action_Move
事件时,必要的时候对其子 View 进行事件拦截,所以有第一个 gif 看到的效果,具体不详说。
第一个步骤 --- 防止 CoordinatorLayout
对 Our ContentView
拦截事件
这里使用 ListView 做例子,设置onTouch
,在内部做适当时候的适当阻止CoordinatorLayout
拦截事件。
// ListView setOnTouchListener( new OnTouchListener() { @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { if (bottomCoordinator == null) return false; // 拿出当前列表第一个可见 item 的 pos int firstVisiblePos = getFirstVisiblePosition(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = event.getRawY(); bottomCoordinator.requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: moveY = event.getRawY(); if ((moveY - downY) > 10) { // 下滑情况 if (firstVisiblePos == 0 && isOverScroll) { // 列表控件,例如 listView 已经滑到头了,允许被拦截 bottomCoordinator.requestDisallowInterceptTouchEvent(false); break; } } // 上滑时,总是不允许被拦截,listView 消耗当前事件 bottomCoordinator.requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_UP: break; } return false; } } );
第二个步骤,让 ListView
能在半展开的情况下,显示完整的数据条数
重写 onMeasure
,使用自定义的测量模式。
// ListView @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(bottomCoordinator == null){ super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } // 以黄金分割的尺寸来显示 listView 的高度 int size = (int)((float)(getResources().getDisplayMetrics().heightPixels*0.618)); int newHeightSpec = MeasureSpec.makeMeasureSpec( size, // mode,非法的情况,super 直接使用 size 做高,看源码后,你会发现也可以使用 exact 模式 Integer.MIN_VALUE ); super.onMeasure(widthMeasureSpec, newHeightSpec); }
第三个步骤,实现 BottomSheetDialog
范围回弹
/** * 添加 top 距离顶部多少的时候触发收缩效果 * @param targetLimitH int 高度限制 */ @SuppressWarnings("all") public void addSpringBackDisLimit(final int targetLimitH){ if(coordinator == null) return; // totalHeight 屏幕的总像素高度 final int totalHeight = getContext().getResources().getDisplayMetrics().heightPixels; // currentH 当前我们的 列表控件 展开的高度 final int currentH = (int) ((float)totalHeight*0.618); // 0.618 是黄金分割点,随便自定义,对应 contentView final int leftH = totalHeight - currentH; coordinator.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_MOVE: // 计算相对于屏幕的 坐标 bottomSheet.getGlobalVisibleRect(r); break; case MotionEvent.ACTION_UP: // 抬手的时候判断 int limitH; if(targetLimitH < 0) limitH = (leftH + currentH/3); else limitH = targetLimitH; if(r.top <= limitH) if (mBehavior != null) // 范围内,让它继续是 半展开的状态 mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); break; } return false; } } ); }
完
如果您认为这篇文章还不错或者有所收获,您可以通过扫描一下下面的支付宝二维码 打赏我一杯咖啡【物质支持】,也可以点击右下角的【推荐】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力
、
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
iOS ijkplayer 数据缓存过程 详解
iOS ijkplayer 数据缓存过程 详解 [objc] view plain copy ijkplayerbuffering过程 staticvoidvoid*SDL_RunThread(voidvoid*data) { @autoreleasepool{ SDL_Thread*thread=data; pthread_setname_np(thread->name); thread->retval=thread->func(thread->data); returnNULL; } } //read_thread线程在此处开启 在read_thread函数内如果ijkplayer播放器处于缓冲状态ffp->packet_buffering标志位为true if(ffp->packet_buffering){ io_tick_counter=SDL_GetTickHR(); if(abs((int)(io_tick_counter-prev_io_tick_counter))>BUFFERING_CHECK_PER_MILLISECONDS...
- 下一篇
YCBanner轮播图
YCBanner轮播图 主要引导界面滑动导航 + 大于1页时无限轮播 + 自定义指示器 项目地址:https://github.com/yangchong211/YCBanner 目录介绍 1.功能说明 2.使用说明 3.图片展示 4.其他介绍 1.功能说明 1.1 自定义轮播图,可以设置轮播红点或者轮播数字,多种指示器,并且灵活设置位置 1.2 支持多种轮播图适配器,无限轮播adapter,静态管理adapter,和动态管理adapter。支持多种场合使用。 1.3 支持自定义hintView,十分灵活,拓展性强 1.4 无限循环自动轮播、手指按下暂停轮播、抬起手指开始轮播 1.5 优化:在页面onPause中调用停止轮播,在页面onResume中调用开始轮播 1.6 支持监听item点击事件,支持轮播图中ViewPager的滑动监听事件 1.7 不仅支持轮播图,还支持引导页面,十分方便 2.使用说明 2.1 直接在项目build文件中添加库即可:compile 'cn.yc:YCBannerLib:1.3' 关于具体的使用方法,可以直接参考代码 2.2 在布局中写,可以设置选择的属...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7设置SWAP分区,小内存服务器的救世主
- Mario游戏-低调大师作品
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- 2048小游戏-低调大师作品
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题