当前位置: 移动技术网 > IT编程>移动开发>Android > Android如何实现超级棒的沉浸式体验

Android如何实现超级棒的沉浸式体验

2018年11月08日  | 移动技术网IT编程  | 我要评论

中文谐音歌词,移动万花筒业务,修罗劫 李向东

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由发表于云+社区专栏

做app开发的过程中,有很多时候,我们需要实现类似于下面这种沉浸式的体验。

img沉浸式体验

一开始接触的时候,似乎大家都会觉这种体验实现起来,会比较困难。难点在于:

  1. 头部的背景图在推上去的过程中,慢慢的变得不可见了,整个区域的颜色变成的暗黑色,然后标题出现了。
  2. statusbar变的透明,且空间可以被利用起来,看我们的图片就顶到了顶 了。
  3. 我们的viewpager推到actionbar的下方的时候,就固定在了actionbar的下方,不能在往上面推了。
  4. 底部有一个控件,随着列表的向上滑动,它退出视角范围,以便于给出更多的空间来展示列表,其实整个沉浸式体验都是为了给列表留出更多的空间来展示。

好,总结起来以上就是我们的问题,也是需要解决的,一个一个解决了,这种需求也就实现了,那么,我们如何去一步一步来解决以上的问题呢?

1、头部背景和标题的渐隐渐现

首先,我们来分析第一个问题,头部的背景图在推上去的过程中,慢慢的变得不可见了,这种听起来好像是某种collapse,因此,很容易让人想到collapsingtoolbarlayout,如果你想要比较容易的了解collapsingtoolbarlayout

应用,建议看的文章,他给也给了一个动画,比较详细的介绍了这个的应用,例如:

imgcollapsingtoolbarlayout

对于里面的用法,我这里不作讲解了,但是如果你不了解这个布局的应用,我强烈建议你好好了解一下,才能继续下面走,只是想说明一下,走到这里,你有一个坑需要去填,那就是我们的标题动画可以不是这样的,而且,还是标题还是居中的,注意,这里的实现,标题不是居中的,是靠左的,这本来是android设计规范,但是设计师偏偏不买android规范的账,因此,我们必须躺过这个坑,然后,从stack overflow上了解到一个:

<android.support.v7.widget.toolbar
    android:id="@+id/toolbar_top"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:minheight="?attr/actionbarsize"
    android:background="@color/action_bar_bkgnd"
    app:theme="@style/toolbartheme" >


     <textview
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="toolbar title"
        android:layout_gravity="center"
        android:id="@+id/toolbar_title" />


</android.support.v7.widget.toolbar>

假设,这个方式是可行的,那么要解决居中的问题后,把返回按钮改为我们的按钮样式,然后,在耍点小诡计,让title开始是透明的,并且的图片:

collapsingtoolbarlayout.setcollapsedtitletextcolor(color.white);
//collapsingtoolbarlayout.setexpandedtitlecolor(color.white);
collapsingtoolbarlayout.setexpandedtitlecolor(color.transparent);

然而,假设,始终只是一个假设,实际上,这个假设不成立,我在尝试的时候,发现toolbar中的textview根本就不能使用android:layout_gravity="center"这种属性好吧,即使强行加上,效果也是靠左的。

那么,如何做,我的解决方式是这样的

<android.support.design.widget.appbarlayout
            android:id="@+id/appbarlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp">

            <android.support.design.widget.collapsingtoolbarlayout
                android:id="@+id/collapsing_tool_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentscrim="@color/b_g6"
                app:expandedtitlemarginend="10dp"
                app:expandedtitlemarginstart="10dp"
                app:layout_scrollflags="scroll|exituntilcollapsed|snap">

                <android.support.constraint.constraintlayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <imageview
                        android:id="@+id/igame_arena_rank_class_header_bg"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:scaletype="centercrop"
                        android:src="@drawable/bg_arena_rank_class"
                        app:layout_constraintdimensionratio="375:156" />
                        .........

                </android.support.constraint.constraintlayout>

                <android.support.v7.widget.toolbar
                    android:id="@+id/common_index_activity_tb_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:minheight="?android:attr/actionbarsize"
                    android:visibility="visible"
                    app:contentinsetleft="0dp"
                    app:contentinsetstart="0dp"
                    app:layout_collapsemode="pin">

                    <include
                        layout="@layout/igame_common_tool_bar"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </android.support.v7.widget.toolbar>


            </android.support.design.widget.collapsingtoolbarlayout>

        </android.support.design.widget.appbarlayout>

然后,include里面的布局是这样的

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

//*****请注意这个view*******///
    <view
        android:id="@+id/common_index_activity_view_status_bar"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

    <relativelayout
        android:layout_width="match_parent"
        android:layout_height="50dp">

        <textview
            android:id="@+id/tv_toolbar_bg"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_centerinparent="true"
            tools:background="@color/b_g6" />

        <textview
            android:id="@+id/common_index_header_tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerinparent="true"
            android:gravity="center"
            android:textcolor="@color/b_g99"
            android:textsize="@dimen/igame_textsize_xl"
            tools:text="这里是标题" />


        <relativelayout
            android:id="@+id/common_index_header_rl_back"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_centervertical="true"
            android:layout_gravity="center_vertical"
            android:visibility="visible">

            <imageview
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_centerinparent="true"
                android:contentdescription="@string/image_desc"
                android:scaletype="centerinside"
                android:src="@drawable/igame_actionbar_arrow_left" />
        </relativelayout>

    </relativelayout>
</linearlayout>

img效果就是这样

当然,这时候,标题是需要你自己设置渐隐渐现的。那么,我们依据什么呢?

appbarlayout.addonoffsetchangedlistener(new appbarlayout.onoffsetchangedlistener() {
            @override
            public void onoffsetchanged(appbarlayout appbarlayout, int verticaloffset) {
                mtitle.setalpha(-verticaloffset * 1.0f / appbarlayout.gettotalscrollrange());
            }
        });

依据的就是对appbarlayout的监听。

2、将statusbar变为透明,且利用他的空间来放我们的布局内容。

 /**
     * 使状态栏透明,并覆盖状态栏,对api大于19的显示正常,但小于的界面扩充到状态栏,但状态栏不为透明
     */
    @targetapi(build.version_codes.kitkat)
    public static void transparentandcoverstatusbar(activity activity) {
        //flag_layout_no_limits这个千万别用,带虚拟按键的机型会有特别多问题

//        //flag_translucent_status要求api大于19
//        activity.getwindow().addflags(windowmanager.layoutparams.flag_translucent_status);
//        activity.getwindow().addflags(windowmanager.layoutparams.flag_layout_in_screen);
//        //flag_layout_no_limits对api没有要求
//        activity.getwindow().addflags(windowmanager.layoutparams.flag_layout_no_limits);

        if (build.version.sdk_int >= build.version_codes.lollipop) {
            window window = activity.getwindow();
            window.clearflags(windowmanager.layoutparams.flag_translucent_status);
            window.getdecorview().setsystemuivisibility(view.system_ui_flag_layout_fullscreen
                    | view.system_ui_flag_layout_stable);
            window.addflags(windowmanager.layoutparams.flag_draws_system_bar_backgrounds);
            window.setstatusbarcolor(color.transparent);
            window.setnavigationbarcolor(resources.getsystem().getcolor(android.r.color.background_dark));
        } else if (build.version.sdk_int >= build.version_codes.kitkat) {
            window window = activity.getwindow();
            window.setflags(windowmanager.layoutparams.flag_translucent_status,
                    windowmanager.layoutparams.flag_translucent_status);
        }
    }

这里是在网上找的一个方法,直接调用即可,但是api需要大于19,相信目前基本上都满足吧。请注意,我的appbarlayout中并没有这个属性

android:fitssystemwindows="true"

如果你加了这个属性,嘿嘿,statusbar虽然空间可以利用,但是有一个你挥之不去的颜色覆盖在上面,

然后,你还记得上面那个布局中

//*****请注意这个view*******///
    <view
        android:id="@+id/common_index_activity_view_status_bar"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

这个作用可大了,就是为了对status_bar原始空间做偏移的,在代码中,需要动态的改变这个view的高度为statusbar的高度,怎么获取:

/**
     * 获取状态栏高度
     *
     * @param context context
     * @return 状态栏高度
     */
    public static int getstatusbarheight(context context) {
        // 获得状态栏高度
        int resourceid = context.getresources().getidentifier("status_bar_height", "dimen", "android");
        return context.getresources().getdimensionpixelsize(resourceid);
    }

完了之后,还需要设置我们自己塞进去的那个toolbar的高度为toolbar的高度加上statusbar的高度。

3、viewpager推到actionbar下面就不让在推了

这个其实需要你collapsingtoolbarlayout里面有一个子view是要使用pin模式的,那么这个子view是谁,显然就是那个toolbar了

<android.support.v7.widget.toolbar
                    android:id="@+id/common_index_activity_tb_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:minheight="?android:attr/actionbarsize"
                    android:visibility="visible"
                    app:contentinsetleft="0dp"
                    app:contentinsetstart="0dp"
                    app:layout_collapsemode="pin">

                    <include
                        layout="@layout/igame_common_tool_bar"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </android.support.v7.widget.toolbar>

4、底部控件随着列表的滑动渐渐隐藏

可以看到,底部的控件是覆盖在列表上的,列表向上滑动的时候,把他隐藏,就可以空出更多的控件看列表。那么,如何做呢?

既然,我们是包裹在coordinatorlayout中,那么,显然,最好的方式是使用layout_behavior了,我这里实现了一个bottombehavior:

public class bottombehavior extends coordinatorlayout.behavior {
    private int id;
    private float bottompadding;
    private int screenwidth;
    private float designwidth = 375.0f;//设计视图的宽度,通常是375dp,

    public bottombehavior() {
        super();
    }

    public bottombehavior(context context, attributeset attrs) {
        super(context, attrs);
        screenwidth = getscreenwidth(context);
        typedarray typedarray = context.getresources().obtainattributes(attrs, r.styleable.bottombehavior);
        id = typedarray.getresourceid(r.styleable.bottombehavior_anchor_id, -1);
        bottompadding = typedarray.getfloat(r.styleable.bottombehavior_bottom_padding, 0f);
        typedarray.recycle();
    }

    @override
    public void onattachedtolayoutparams(@nonnull coordinatorlayout.layoutparams params) {
        params.dodgeinsetedges = gravity.bottom;
    }

    @override
    public boolean ondependentviewchanged(coordinatorlayout parent, view child, view dependency) {
        return dependency.getid() == id;
    }

    @override
    public boolean layoutdependson(coordinatorlayout parent, view child, view dependency) {
        child.settranslationy(-(dependency.gettop() - (screenwidth * bottompadding / designwidth)));
        log.e("bottombehavior", "layoutdependson() called with: parent = [" + dependency.gettop());
        return true;
    }


    public static int getscreenwidth(context context) {
        windowmanager wm = (windowmanager) context.getsystemservice(context.window_service);
        display display = null;
        if (wm != null) {
            display = wm.getdefaultdisplay();
            point size = new point();
            display.getsize(size);
            int width = size.x;
//            int height = size.y;
            return width;
        }
        return 0;
    }
}

这个里面有两个自定义属性,id,bottompadding,id表示基于哪个控件的相对位置改变,我这打算基于viewpager

这个控件,看源码可以知道,只有当ondependentviewchanged返回ture时,layoutdependson才会被回调。bottompadding是表示一个初始的偏移,因为viewpager本身不是顶在屏幕顶端的(开始被图片占据了一部分控件),因此,需要扣除这部分占有。

同理,加入让你实现一个悬浮在左侧,右侧,滑动隐藏,停止显示的,也都可以参考类似behavior的方式,减少代码耦合。

总结

最后整个布局是这样子的

<?xml version="1.0" encoding="utf-8"?>
<com.tencent.igame.view.common.widget.igamerefreshlayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/igame_competition_detail_fragment_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.coordinatorlayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.appbarlayout
            android:id="@+id/appbarlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp">

            <android.support.design.widget.collapsingtoolbarlayout
                android:id="@+id/collapsing_tool_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:contentscrim="@color/b_g6"
                app:expandedtitlemarginend="10dp"
                app:expandedtitlemarginstart="10dp"
                app:layout_scrollflags="scroll|exituntilcollapsed|snap">

                <android.support.constraint.constraintlayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <imageview
                        android:id="@+id/igame_arena_rank_class_header_bg"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:scaletype="centercrop"
                        android:src="@drawable/bg_arena_rank_class"
                        app:layout_constraintdimensionratio="375:156" />
                        ............

                </android.support.constraint.constraintlayout>

                <android.support.v7.widget.toolbar
                    android:id="@+id/common_index_activity_tb_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:minheight="?android:attr/actionbarsize"
                    android:visibility="visible"
                    app:contentinsetleft="0dp"
                    app:contentinsetstart="0dp"
                    app:layout_collapsemode="pin">

                    <include
                        layout="@layout/igame_common_tool_bar"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </android.support.v7.widget.toolbar>


            </android.support.design.widget.collapsingtoolbarlayout>

        </android.support.design.widget.appbarlayout>

        <com.tencent.igame.widget.viewpager.igameviewpager
            android:id="@+id/igame_arena_rank_class_vp_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

        <android.support.constraint.constraintlayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_gravity="bottom"
            android:background="@color/b_g6"
            android:paddingleft="12dp"
            android:paddingright="12dp"
            app:anchor_id="@+id/igame_arena_rank_class_vp_content"
            app:bottom_padding="156.0"
            app:layout_behavior="com.tencent.igame.common.widget.bottombehavior">
..........底部布局

        </android.support.constraint.constraintlayout>

    </android.support.design.widget.coordinatorlayout>

</com.tencent.igame.view.common.widget.igamerefreshlayout>

注:igamerefreshlayout实际上就是封装的pulltorefreshview,igameviewpager是我们封装的viewpager,减少每次写viewpager的套路代码。

按照这个框架来,相信你很容易写出这个样子的布局。

相关阅读
【每日课程推荐】机器学习实战!快速入门在线广告业务及ctr相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网