Android 项目优化笔记(三):首页

一、首页优化问题

那么首先回顾一下有关首页的优化建议

  1. 角色切换方式 可以优化,如果更换的话可能需要 重新设计,且涉及逻辑较多,暂不更改。
  2. 用户登录时 提示用户选择角色。(Maybe)
  3. 角色切换可添加动画,用户操作时从左上角动画延伸至整个首页。Reveal Effect (揭露效果)。
  4. MD 风格:头部 CoordinatorLayout + AppBarLayout + Toolbar,AppBarLayout + Toolbar 可以抽出来。底部导航栏可用 BottomNavigationView 替换。

二、解决问题

问题 1 和问题 2

问题 1 和问题 2 需要产品和业务沟通,本文只讨论技术实现,略过。

问题 3:角色切换动画

问题 3 中提到的 Reveal Effect (揭露效果) 是 # Material-Animations 中的动画效果之一。其它的动画效果包括 Activity 之间转换动画、共享元素动画,以及布局转换动画(布局宽高或状态改变时以动画方式进行)。

那么先看一下揭露效果实例:


reveal_yellow.gif

我希望在切换角色后从切换按钮执行切换动画至全屏,但是实际做出来效果比较差。首先项目中首页的背景并不是纯色,就算是纯色进行揭露,还要考虑要切换成什么颜色...所以这里暂时不做成揭露效果...不过先画个饼,在以后的项目优化过程中,会在合适的地方用上这个效果。

但是可以做成 Transition 效果,整体效果如下:


整体效果

在这之前,需要简单了解一下 Transition 和 Scene 的概念。

Transition

看一下官方文档的解释:

A Transition holds information about animations that will be run on its targets during a scene change.

渣翻:Transition 包含了一些动画信息,在场景(Scene)转换时使用。

另外根据文档可知 Transition 主要工作有两个:

  1. 获取属性值
  2. 根据捕获的属性值的更改来播放动画。

那么 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 进行切换:

  1. 首先创建 Fragment 并写好点击事件,这里是通过点击 MainActivity Toolbar 的左侧 TextView 来展示 PopupMenu,然后再进行点选切换角色。比较简单就不贴代码了,可以在源码中查看。

MainActivity

  1. 写好三个角色的布局,以便进行转换。货主页面是默认页:
scene_carrier
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:layout_marginTop="-50dp"
        android:src="@drawable/home_banner"
        android:adjustViewBounds="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ImageView
        android:layout_gravity="center_horizontal"
        android:src="@drawable/img_goods"
        android:adjustViewBounds="true"
        android:layout_width="200dp"
        android:layout_height="wrap_content" />

    <android.support.design.button.MaterialButton
        android:id="@+id/btn_consignor"
        android:layout_margin="16dp"
        android:text="发货"
        android:layout_width="match_parent"
        android:layout_height="56dp" />

</LinearLayout>

很简单,一个 LinearLayout 裹着两个图片一个按钮。需要注意的是该 LinearLayout 将作为 viewRoot 使用,id 为 ll_parent。

其它两个页面大同小异,就不贴了。

  1. 根据不同角色类型进行转换,点击切换角色时将角色类型传递并转换到相应的布局:
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:

  1. 首先是导入 design 包,后面的版本最好与项目所用的 SDK 版本以及其它 Android 库相同:
implementation 'com.android.support:design:28.0.0'
  1. 创建 BottomNavigationView :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_home"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bnv_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/navigation"
        app:labelVisibilityMode="labeled"/>
    
    <!--app:itemIconTint="@color/colorPrimary"-->
    <!--app:itemTextColor="@color/colorPrimary"-->

</LinearLayout>

一些属性的解释:

  • app:itemIconTint 和 app:itemTextColor :分别代表 icon 填充色(针对 Vector 图片)和文字颜色。可以创建 selector 来根据状态改变颜色:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item
      android:state_checked="true"
      android:color="@color/colorPrimary" />
  <item
      android:state_checked="false"
      android:color="@color/grey" />
 </selector>
  • app:labelVisibilityMode:item 初始状态展示模式。
  1. menu,也就是上面指定的 app:menu="@menu/navigation"
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/navigation_home"
        android:orderInCategory="0"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home" />

    <item android:id="@+id/navigation_dashboard"
        android:orderInCategory="1"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_1" />

    <item android:id="@+id/navigation_notifications"
        android:orderInCategory="2"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_2" />

    <item android:id="@+id/navigation_notifications2"
        android:orderInCategory="3"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_3" />

</menu>

可以指定 id、android:orderInCategory(指定item数字 id)、icon(图标)和 title(标题)。

注意事项:
*推荐使用 28 及以上版本的 BottomNavigationView,item 变多以后不会进行折叠
*app:labelVisibilityMode 是 28 版本才添加的功能,之前版本需要反射处理。

权限提示框

Dialog 就不赘述了,用 Google 推荐的 DialogFragment 把 Dialog 包起来就能达到不错的效果。放一个基础使用链接:

Android Dialog使用详解

如果有更多需求的话可以参考引用这个库:

material-dialogs

最后,贴上源码链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335