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

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

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

沉浸式体验

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

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

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

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

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

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

CollapsingToolbarLayout

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

<android.support.v7.widget.Toolbarandroid: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" ><TextViewandroid: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.AppBarLayoutandroid:id="@+id/appbarlayout"android:layout_width="match_parent"android:layout_height="wrap_content"app:elevation="0dp"><android.support.design.widget.CollapsingToolbarLayoutandroid: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.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid: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.Toolbarandroid: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"><includelayout="@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*******///<Viewandroid:id="@+id/common_index_activity_view_status_bar"android:layout_width="match_parent"android:layout_height="0dp" /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"><TextViewandroid:id="@+id/tv_toolbar_bg"android:layout_width="match_parent"android:layout_height="50dp"android:layout_centerInParent="true"tools:background="@color/b_G6" /><TextViewandroid: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="这里是标题" /><RelativeLayoutandroid: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"><ImageViewandroid: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>

效果就是这样

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

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {@Overridepublic 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*******///<Viewandroid: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.Toolbarandroid: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"><includelayout="@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();}@Overridepublic void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {params.dodgeInsetEdges = Gravity.BOTTOM;}@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {return dependency.getId() == id;}@Overridepublic 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.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.design.widget.AppBarLayoutandroid:id="@+id/appbarlayout"android:layout_width="match_parent"android:layout_height="wrap_content"app:elevation="0dp"><android.support.design.widget.CollapsingToolbarLayoutandroid: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.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid: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.Toolbarandroid: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"><includelayout="@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.IgameViewPagerandroid: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.ConstraintLayoutandroid: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 送你一份技术课程大礼包!

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

Android如何实现超级棒的沉浸式体验相关推荐

  1. android studio 显示view树_Android 沉浸式解析和轮子使用

    文 | 黑羽 on 移动电商 前言 我们先一起来回顾一下实现沉浸式状态栏的一般套路.在 Android 上,关于对 StatusBar(状态栏)的操作,一直都在不断改善,并且表现越来越好,在 Andr ...

  2. android朋友圈评论功能兼容沉浸式状态栏,九宫格图片显示

    android朋友圈评论功能,沉浸式状态栏,九宫格图片显示器,上拉加载下拉刷新功能,可直接用到项目中 背景 在前两个项目开发过程中用到了朋友圈这个功能,包含了评论回复.九宫格图片及大图展示展示,上拉加 ...

  3. android 沉浸式_【沉浸式体验】投影秀科技与视觉:体验亦真亦幻的超常感受

    沉浸式投影作为一种新型的交互方式 受大众关注 全面覆盖观众视角 展现给参与者带来了亦真亦幻的超常感受 为参与者带来身临其境的体验感 投影秀科技与视觉+美学的结合 让你的活动与众不同 一起体验投影秀的魅 ...

  4. 3D沉浸式体验开发技巧【Three.js】

    在本文中,我们将看看如何使用 Three.js 创建一个充满后期效果和微交互的迷你城市. 推荐:将 NSDT场景编辑器 加入你的3D开发工具链. 1.背景介绍 我是一个游戏爱好者. 我一直梦想创建一个 ...

  5. 【有美女看】提升用户体验,你不得不知道的事儿——巧用全屏与沉浸式体验,让用户更舒心~...

    最近公司项目中要求写一个视频直播录像及视频观看的功能,额,就本能地去看了一波当下主流的一些视频类APP,发现在爱奇艺等播放器中,在播放视频的时候都是fullScreen的,而这个在各大手游中也体现的比 ...

  6. 云XR平台支持沉浸式体验应用快速落地

    近日,阿里云与平行云联合发布云 XR 平台,降低云端视觉计算应用的开发门槛,加速数字孪生.虚拟人.虚拟现实.沉浸式体验与虚拟仿真平台等 XR 应用落地,帮助互联网.新零售.社交.工业.交通.城市管理等 ...

  7. “沉浸式”体验?异形屏适配?我把他们扒光了明明白白告诉你应该这样做

    看似复杂的沉浸式体验设计,其实也就是在处理以下两个 System UI与用户布局(setContentView)之间说不清理还乱的关系: StatusBar 系统状态栏 NavigationBar 系 ...

  8. 阿里云视频云互动虚拟技术,打造虚拟直播最佳沉浸式体验

    2022是"体育超级大年",冬奥会.亚运会.大运会.世界杯等各类大型体育赛事贯穿全年.由于受到疫情管控和物理空间的限制,赛事直播至关重要,观众体验需求也在不断升级. 于此,企业对直 ...

  9. VR全景为家装赋能,沉浸式体验家装设计效果

    房子.车子是我们目前难以解决的两大难题,很多人需要两代人的努力才能够买一套房子,然后还得经历等待交房.进行装修等一大堆事情才能住进去.尤其是室内装修,很多人都是有着痛苦的经历的,因为自己忙前忙后,结果 ...

  10. 将渲染计算搬到云端,开启低成本、强交互、沉浸式体验

    云渲染可以解放本地计算需求,这意味着生产力的大幅提升. 云渲染的基本原理是将3D渲染应用部署到云端,接收本地的控制指令发送到云端,云端启动游戏引擎并进行画面渲染,编码成视频流传输到本地. 不难看出,云 ...

最新文章

  1. 自动化运维工具Saltstack(一)
  2. Java 如何实现二维码?
  3. seaborn使用FacetGrid函数可视化山脊图(Ridgeline Plot with Seaborn)
  4. DayDayUp之Job:牛客网—算法工程师—剑指offer之66道在线编程(解决思路及其代码)——1~20
  5. linux 查看flash大小,Linux OpenWRT查看CPU,RAM,Flash信息参数
  6. selenium threading运行后退出_Selenium的使用
  7. webpack简单笔记
  8. 该虚拟机似乎正在使用
  9. API – MultiByteToWideChar的用法
  10. 原生html冻结表头,CSS如何实现表头冻结效果
  11. Maximo中调用XFire客户端的实现—Weblogic中间件
  12. C++复数的运算、运算符重载
  13. py导入包异常跳出_Python:尝试从导入的包导入模块时出现“ModuleNotFoundError”
  14. 变量类型 ROWID 和 UROWID
  15. 一套完整自定义工作流的实现
  16. 解压版tomcat7安装教程
  17. 如何看oracle 删除完全,怎么查看以前Oracle卸载干净没?
  18. CKEditor配置使用
  19. ubuntu中firebox无法联网
  20. Galaxy S4 GT-I9500如何root 安卓5.0.1

热门文章

  1. 编译器处理虚函数的原理
  2. 小程序发布新版本后,部分用户手机白屏
  3. win7系统如何一键清理系统垃圾【系统天地】
  4. 形象理解数字证书的基本安全功能
  5. 如何批量在图片上加文字?
  6. Java开发者,我到底要不要学大数据开发?
  7. ERP软件是什么意思,买菜大妈讲的通俗易懂
  8. 如何删除 Windows 10 上的 Windows.old 文件夹?
  9. 《疯狂原始人》温馨而搞笑片段截图
  10. ArcGIS空间数据分析实用工具——方向分布(标准差椭圆)