Material Design系列之Behavior实现支付密码弹窗和商品属性选择效果
今天的效果在支付宝、淘宝、京东等电商app中很常见。比如支付宝输入密码弹窗、商城下单时选择商品属性时,从下面浮动上来一个popupwindow,那么今天就带大家用behavior来实现这两个效果,结果你会发现简直只需要一行代码。
总结下现在用的app:
1. 仿支付宝弹出的输入支付密码窗口。
2. 仿淘宝/天猫弹出商品属性选择框。
3. 知乎首页上下滑动隐藏toolbar和navigationbar。
4. …
系列博客:
1. material design系列,behavior之bottomsheetbehavior与bottomsheetdialog
2. material design系列,behavior之swipedismissbehavior
3. material design系列,自定义behavior之上滑显示返回顶部按钮
4. material design系列,自定义behavior实现android知乎首页
5. material design系列,自定义behavior支持所有 view
效果预览
引文
在我的技术群里有小伙伴们讨论behavior,我也去玩了玩,我也对behavior写了系列博客。选中behavior然后ctrol + t后发现behavior的一个实现类:bottomsheetbehavior,我就到android官网上翻了下资料,一翻就发现了惊喜啊,下面就把这些惊喜介绍给大家。
更多文章请google/百度搜索我名字:严振杰,排名第一的就是我。
bottomsheetbehavior怎么玩(知乎bottom隐藏和显示)
玩这个东西,首先behavior作为coordinatorlayout的子view的layoutparams(原因看后文解释),所以coordinatorlayout是万万不能少的,先来亮出整个布局:
<android.support.design.widget.coordinatorlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.appbarlayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/apptheme.appbaroverlay"> <android.support.v7.widget.toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionbarsize" app:popuptheme="@style/apptheme.popupoverlay" /> </android.support.design.widget.appbarlayout> <linearlayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/tab_layout" android:gravity="center" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <button android:id="@+id/btn_bottom_sheet_control" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="sheet 显示/隐藏" /> </linearlayout> <linearlayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="?actionbarsize" android:layout_alignparentbottom="true" android:background="@android:color/holo_purple" app:layout_behavior="@string/bottom_sheet_behavior"> <button android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="第一" /> <button android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="第二" /> <button android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="第三" /> <button android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="第四" /> </linearlayout> </android.support.design.widget.coordinatorlayout>
大概介绍下,页面上只能看到toolbar和一个button:sheet 显示/隐藏,然后android:id="@+id/tab_layout"这个布局是横向的,给它设置了behavior:app:layout_behavior="@string/bottom_sheet_behavior",经过测试发现,如果不给tab_layout设置bottomsheetbehavior,它会浮动在整个页面的顶部,并在toolbar的下面。设置了bottomsheetbehavior它会被bottomsheetbehavior自动移动到页面底部外边,所以在页面上是看不到android:id="@+id/tab_layout"这个布局的。
页面画好了,难道它会自动开关吗,怎么去控制它的打开和关闭呢?那么我们就来看看这货的真实面貌,经过我看android的官方api发现,bottomsheetbehavior这个货有一个静态方法bottomsheetbehavior.from(view),会返回这个view引用的bottomsheetbehavior:
public static <v extends view> bottomsheetbehavior<v> from(v view) { viewgroup.layoutparams params = view.getlayoutparams(); if (!(params instanceof coordinatorlayout.layoutparams)) { throw new exception("the view is not a child of coordinatorlayout"); } coordinatorlayout.behavior behavior = params.getbehavior(); if (!(behavior instanceof bottomsheetbehavior)) { throw new illegalargumentexception("..."); } return (bottomsheetbehavior<v>) behavior; }
这个方法会检查这个view是否是coordinatorlayout的子view,如果是才会去拿到这个view的behavior,所以诸位也应该明白为什么我开头说behavior作为coordinatorlayout子view的layoutparams了。
接下来我们看看拿到这个货后怎么用:
private bottomsheetbehavior mbottomsheetbehavior; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.bsbehavior_activity); toolbar toolbar = (toolbar) findviewbyid(r.id.toolbar); setsupportactionbar(toolbar); getsupportactionbar().setdisplayhomeasupenabled(true); findviewbyid(r.id.btn_bottom_sheet_control).setonclicklistener(onclicklistener); // 拿到这个tab_layout对应的bottomsheetbehavior mbottomsheetbehavior = bottomsheetbehavior.from(findviewbyid(r.id.tab_layout)); }
findviewbyid(r.id.btn_bottom_sheet_control).setonclicklistener(onclicklistener);是给刚才说的页面中的button设置了监听,我们用这个按钮来控制tab_layout的显示和隐藏。
我发现有一个方法可以获取到它所依附的view此时的状态:mbottomsheetbehavior.getstate(),翻阅了源码后发现它的返回值有以下几种:
/** * the bottom sheet is dragging. */ public static final int state_dragging = 1; /** * the bottom sheet is settling. */ public static final int state_settling = 2; /** * the bottom sheet is expanded. */ public static final int state_expanded = 3; /** * the bottom sheet is collapsed. */ public static final int state_collapsed = 4; /** * the bottom sheet is hidden. */ public static final int state_hidden = 5;
当我看到state_expanded和state_collapsed就明白了它的用法了,不就是展开和隐藏起来了麽?所以我们判断这个状态,如果是隐藏就显示,如果是显示就隐藏:
@override public void onclick(view v) { if (v.getid() == r.id.btn_bottom_sheet_control) { if (mbottomsheetbehavior.getstate() == bottomsheetbehavior.state_expanded) { mbottomsheetbehavior.setstate(bottomsheetbehavior.state_collapsed); } else if (mbottomsheetbehavior.getstate() == bottomsheetbehavior.state_collapsed) { mbottomsheetbehavior.setstate(bottomsheetbehavior.state_expanded); } } }
到这里,知乎首页的bottom的隐藏和显示也就讲玩了,接下来我们来看看支付宝淘宝的下方弹窗如何实现。
bottomsheetdialog怎么玩(商城下单商品属性选择弹窗)
这个类的发现也是在android官网搜索bottomsheetbehavior时发现的,一看到bottomsheetdialog后心中狂喜,后来经过我验证,它显示的效果和我猜想的一模一样啊,既然是个dialog,那么用法应该和普通dialog没啥去区别了吧。
然后我就顺势new了一个bottomsheetdialog:
private bottomsheetdialog mbottomsheetdialog; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); ... createbottomsheetdialog(); } private void createbottomsheetdialog() { mbottomsheetdialog = new bottomsheetdialog(this); view view = layoutinflater.from(this).inflate(r.layout.dialog_bottom_sheet, null, false); mbottomsheetdialog.setcontentview(view); recyclerview recyclerview = (recyclerview) view.findviewbyid(r.id.recyclerview); ... recyclerview.setadapter(adapter); }
view里面是一个recyclerview:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.recyclerview xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" />
然后用下面的代码去控制它的显示和隐藏。
if (mbottomsheetdialog.isshowing()) { mbottomsheetdialog.dismiss(); } else { mbottomsheetdialog.show(); }
当这个dialog show出来的时候发现它显示了一半,嗯这个效果确实不错,这样就达到了我们最初说的支付宝密码弹窗和淘宝/天猫商品属性选择。我们滑动的时候如果下面有内容它就会expanded,如果是一个普通的view(非recyclerview、nestedscrollview)将不会继续往上滑动,下面的内容会继续跟着出来,但是同样可以向下滑动隐藏,也可以调用dismiss和close关闭。
bottomsheetdialog的神坑
作为一个有情怀的程序员,这里把我踩过的坑和解决方案跟大家分享一下。
我发现当这个dilaog打开再关闭后,无法用dialog.show()再次打开,为什么呢?
我去阅读了一下bottomsheetdialog源代码,发现了如下代码:
@override public void setcontentview(view view, viewgroup.layoutparams params) { super.setcontentview(wrapinbottomsheet(0, view, params)); } private view wrapinbottomsheet(int layoutresid, view view, viewgroup.layoutparams params) { final coordinatorlayout coordinator = view.inflate(getcontext(),r.layout...., null); framelayout bottomsheet = (framelayout) coordinator.findviewbyid(r.id.design_bottom_sheet); bottomsheetbehavior.from(bottomsheet).setbottomsheetcallback(mbottomsheetcallback); ... return coordinator; } private bottomsheetcallback mbottomsheetcallback = new bottomsheetcallback() { @override public void onstatechanged(@nonnull view bottomsheet, int newstate) { if (newstate == bottomsheetbehavior.state_hidden) { dismiss(); } } @override public void onslide(@nonnull view bottomsheet, float slideoffset) { } };
也就是说,系统的bottomsheetdialog是基于bottomsheetbehavior封装的,这里判断了当我们滑动隐藏了bottomsheetbehavior中的view后,内部是设置了bottomsheetbehavior的状态为state_hidden,接着它替我们关闭了dialog,所以我们再次调用dialog.show()的时候dialog没法再此打开状态为hide的dialog了。
这里就有个疑问了:
google为啥没有提供我们自己设置bottomsheetcallback的接口呢?
没有关系,看了源码发现很简单,我们自己来实现,并且在监听到用户滑动关闭bottomsheetdialog后,我们把bottomsheetbehavior的状态设置为bottomsheetbehavior.state_collapsed,也就是半个打开状态(bottomsheetbehavior.state_expanded为全打开),根据源码我把设置的方法提供下:
private void setbehaviorcallback() { view view = mbottomsheetdialog.getdelegate().findviewbyid(android.support.design.r.id.design_bottom_sheet); final bottomsheetbehavior bottomsheetbehavior = bottomsheetbehavior.from(view); bottomsheetbehavior.setbottomsheetcallback(new bottomsheetbehavior.bottomsheetcallback() { @override public void onstatechanged(@nonnull view bottomsheet, int newstate) { if (newstate == bottomsheetbehavior.state_hidden) { mbottomsheetdialog.dismiss(); bottomsheetbehavior.setstate(bottomsheetbehavior.state_collapsed); } } @override public void onslide(@nonnull view bottomsheet, float slideoffset) { } }); }
这样就解决了bottomsheetdialog关闭后不能再次打开的问题了。
源码下载:http://xiazai.jb51.net/201609/yuanma/androidbehavior(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。