一、首页优化问题
那么首先回顾一下有关首页的优化建议:
- 角色切换方式 可以优化,如果更换的话可能需要 重新设计,且涉及逻辑较多,暂不更改。
- 用户登录时 提示用户选择角色。(Maybe)
- 角色切换可添加动画,用户操作时从左上角动画延伸至整个首页。Reveal Effect (揭露效果)。
- MD 风格:头部 CoordinatorLayout + AppBarLayout + Toolbar,AppBarLayout + Toolbar 可以抽出来。底部导航栏可用 BottomNavigationView 替换。
二、解决问题
问题 1 和问题 2
问题 1 和问题 2 需要产品和业务沟通,本文只讨论技术实现,略过。
问题 3:角色切换动画
问题 3 中提到的 Reveal Effect (揭露效果) 是 # Material-Animations 中的动画效果之一。其它的动画效果包括 Activity 之间转换动画、共享元素动画,以及布局转换动画(布局宽高或状态改变时以动画方式进行)。
那么先看一下揭露效果实例:
我希望在切换角色后从切换按钮执行切换动画至全屏,但是实际做出来效果比较差。首先项目中首页的背景并不是纯色,就算是纯色进行揭露,还要考虑要切换成什么颜色...所以这里暂时不做成揭露效果...不过先画个饼,在以后的项目优化过程中,会在合适的地方用上这个效果。
但是可以做成 Transition 效果,整体效果如下:
在这之前,需要简单了解一下 Transition 和 Scene 的概念。
Transition
看一下官方文档的解释:
A Transition holds information about animations that will be run on its targets during a scene change.
渣翻:Transition 包含了一些动画信息,在场景(Scene)转换时使用。
另外根据文档可知 Transition 主要工作有两个:
- 获取属性值
- 根据捕获的属性值的更改来播放动画。
那么 Transition 主要包含的类型有:
- ChangeBounds:改变 View 的大小和位置
- Fade:改变透明度
- TransitionSet:组合动画
- AutoTransition:默认的组合动画,封装淡出、改变View大小,最后淡入效果。
Scene
A scene represents the collection of values that various properties in the View hierarchy will have when the scene is applied.
渣翻:一个Scene是当前View所有属性的集合。比如一个布局里所有 View 的属性,包括它们的宽高等,都可以保存为一个 Scene。
这样 Scene 对象将提供一些属性值,再通过 Transition 的动画来改变这些属性值,以达成动画效果。
有关 Transition 和 Scene 的概念先记录到这里。考虑到该项目有三个角色:货主、承运商和司机,需要进行角色切换。且三个角色布局有所不同,所以比较适合使用 Transition + Scene 进行切换。下面就使用最简单的 AutoTransition 进行切换:
- 首先创建 Fragment 并写好点击事件,这里是通过点击 MainActivity Toolbar 的左侧 TextView 来展示 PopupMenu,然后再进行点选切换角色。比较简单就不贴代码了,可以在源码中查看。
MainActivity
- 写好三个角色的布局,以便进行转换。货主页面是默认页:
很简单,一个 LinearLayout 裹着两个图片一个按钮。需要注意的是该 LinearLayout 将作为 viewRoot 使用,id 为 ll_parent。
其它两个页面大同小异,就不贴了。
- 根据不同角色类型进行转换,点击切换角色时将角色类型传递并转换到相应的布局:
public static final String USER_TYPE_CONSIGNOR = "货主";
public static final String USER_TYPE_CARRIER = "承运商";
public static final String USER_TYPE_DRIVER = "司机";
...
/**
* 根据不同角色类型进行场景转换
* @param type 类型
*/
public void transitionView(String type){
switch (type){
case Constants.USER_TYPE_CONSIGNOR:
TransitionUtil.transitionLayout(ll_parent,R.layout.scene_consignor,getActivity());
break;
case Constants.USER_TYPE_CARRIER:
TransitionUtil.transitionLayout(ll_parent,R.layout.scene_carrier,getActivity());
break;
case Constants.USER_TYPE_DRIVER:
TransitionUtil.transitionLayout(ll_parent,R.layout.scene_driver,getActivity());
break;
default:
break;
}
}
*ll_parent:就是主页 xml 根布局,上面提到的 viewRoot。
TransitionUtil
/**
* AutoTransition 转换
* @param sceneRoot 根局部
* @param layout 转换到的布局
* @param context context
*/
public static void transitionLayout(ViewGroup sceneRoot, @LayoutRes int layout, Context context){
Scene scene = Scene.getSceneForLayout(sceneRoot,layout,context);
TransitionManager.go(scene);
}
看一下核心代码,进行 Transition 动画的三个要素:
- Scene:调用
Scene#getSceneForLayout
传递根布局和要转换到的布局作为参数,生成 Scene 对象,这样储存了两个布局信息的 Scene 对象就产生了。
创建 Scene 对象的方式还有另外几种,后面会记录。 - TransitionManager:Transition 的管理者,使用该类的一些静态方法来执行转换。
- Transition:TransitionManager 默认提供一个
AutoTransition
,参考下面源码:
TransitionManager#go(@NonNull Scene scene)
private static Transition sDefaultTransition = new AutoTransition();
...
public static void go(@NonNull Scene scene) {
changeScene(scene, sDefaultTransition);
}
只有一个 scene 参数的方法就使用默认的 AutoTransition。
TransitionManager#go(@NonNull Scene scene, @Nullable Transition transition)
public static void go(@NonNull Scene scene, @Nullable Transition transition) {
changeScene(scene, transition);
}
上面的方法可以使用自定义的 Transition。
注意:
“承运商”页面有一个按钮有点击事件,但是该页面只是在进行 Transition 后才会加载,所以该按钮应该在“承运商”页面被转换后才能设置点击事件:
case Constants.USER_TYPE_CARRIER:
TransitionUtil.transitionLayout(ll_parent,R.layout.scene_carrier,getActivity());
ll_parent.findViewById(R.id.btn_carrier).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "找货", Toast.LENGTH_SHORT).show();
}
});
总结:
创建 Scene 的方式有:
- 直接 new:
mScene1 = new Scene(mSceneRoot,mSceneRoot.findViewById(R.id.container));
- 通过
Scene#getSceneForLayout
静态方法创建:
mScene2 = Scene.getSceneForLayout(mSceneRoot,R.layout.scene2,this);
创建 Transition 的方式有:
- new 想要的动画类型:
TransitionManager.go(scene1, new ChangeBounds());
- 从xml创建
TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
最后通过 TransitionManager 执行:
- TransitionManager.go:一般配合 Scene 使用。
- TransitionManager.beginDelayedTransition:不转换 Scene,只改变当前 View 状态可用此方法。
有关 Transition 和 Scene 还有很多内容,包括 5.0以上 Activity 的跳转也可以用 Transition,参考
Material-Animations
适配:
官方的只适配到 Android 5.0系统以上,这里有一个库可以支持到 4.0 系统:
Transitions-Everywhere
Transitions-Everywhere中文文档(译)
问题 4:MD 风格
首先搭一个常用的主体布局,ViewPager+Fragment。这样做的好处是既可以像微信一样左右滑动,又可以禁止滑动只能点选切换。比较简单,MainActivity。
接下来是 Toolbar,简单封装到 BaseActivity。因为教程也很多,这里略过,可以参考我简单封装的:BaseAppCompatActivity。
再然后是 BottomNavigationView,需要注意的是 BottomNavigationView 最多支持五个 item:
- 首先是导入 design 包,后面的版本最好与项目所用的 SDK 版本以及其它 Android 库相同:
implementation 'com.android.support:design:28.0.0'
- 创建 BottomNavigationView :
一些属性的解释:
- app:itemIconTint 和 app:itemTextColor :分别代表 icon 填充色(针对 Vector 图片)和文字颜色。可以创建 selector 来根据状态改变颜色:
- app:labelVisibilityMode:item 初始状态展示模式。
- menu,也就是上面指定的 app:menu="@menu/navigation"
可以指定 id、android:orderInCategory(指定item数字 id)、icon(图标)和 title(标题)。
注意事项:
*推荐使用 28 及以上版本的 BottomNavigationView,item 变多以后不会进行折叠
*app:labelVisibilityMode 是 28 版本才添加的功能,之前版本需要反射处理。
权限提示框
Dialog 就不赘述了,用 Google 推荐的 DialogFragment 把 Dialog 包起来就能达到不错的效果。放一个基础使用链接:
Android Dialog使用详解
如果有更多需求的话可以参考引用这个库:
material-dialogs
最后,贴上源码链接。