美文网首页安卓UIAndroid开发Android开发
Android 项目优化笔记(三):首页

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

作者: Marker_Sky | 来源:发表于2019-02-22 16:55 被阅读259次

    一、首页优化问题

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

    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

    最后,贴上源码链接

    相关文章

      网友评论

        本文标题:Android 项目优化笔记(三):首页

        本文链接:https://www.haomeiwen.com/subject/yfexsqtx.html