android:theme="@style/AppNoTitleTheme"
<style name="AppNoTitleTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="windowNoTitle">true</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style>
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);//可不加 } setContentView(R.layout.activity_main);//在此以前添加以上FLAG initView(); initEvent(); }
- //此FLAG可以使状态栏透明,且当前视图在绘制时,从屏幕顶端开始即top = 0开始绘制,这也是实现沉浸效果的基础
<RelativeLayout android:fitsSystemWindows="true" android:clipToPadding="false" android:layout_width="match_parent" android:layout_height="match_parent"></RelativeLayout>
<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/textdemo_image" android:src="@drawable/bg3" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- textdemo_titleholderview 此控件为占位控件设置其高度为状态栏的高度--> <View android:id="@+id/textdemo_titleholderview" android:layout_width="match_parent" android:layout_height="0dp" /> <!-- titlebar_view此控件为自定义的标题栏titlebar--> <include layout="@layout/titlebar_view"></include> <View android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/line_mode_view"></include> <include layout="@layout/line_mode_view"></include> </LinearLayout> </LinearLayout> </RelativeLayout>
ViewGroup.LayoutParams layoutParams = titlebarHolder.getLayoutParams();// layoutParams.height = getStatueBarHeight(); titlebarHolder.setLayoutParams(layoutParams); titlebarHolder.setBackgroundColor(Color.TRANSPARENT);//此处也可设置自定义的颜色,设置为透明则会直接看到底层的图片
- titlebarHolder为添加的顶部的(标题栏上方)占位控件
xmlns:app="http://schemas.android.com/apk/res-auto"//以便引用其余包下定义的属性值, e.g. app:layout_scrollFlags="
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <!--CoordinatorLayout:协调布局 该实例中能够协调子View的滑动事件效果,该实例中有两个子View: 1.AppBarLayout:用来包括CollapsingToolbarLayout(折叠布局),主要使用该AppBarLayout,已有的android.support.design.widget.AppBarLayout$ScrollingViewBehavior 2.RecyclerView:该View经过设置app:layout_behavior="@string/appbar_scrolling_view_behavior"属性,直接与AppBarLayout,相关联。 ps:由于CoordinatorLayout处理的是联动效果,通常须要一个可滑动操做的空间,通常可用的有(RecyclerView,4包中的NestedScrollView),可是(ListView)不可用 pps:总体联动实现是,先经过layout_behavior实现该RecyclerView与AppBarLayout的联动。而后是CollapsingToolbarLayout内部的效果,经过设置其app:layout_scrollFlags 及 android:minHeight以及子view的app:layout_collapseMode="pin"实现。 --> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- CollapsingToolbarLayout:折叠布局视图继承自Framlayout,全部子view默认从左上角开始 该实例中定义了两个子View,其中的 1.RelativeLayout:充当整个CollapsingToolbarLayout的可滑动布局,跟随滑动事件一块儿滑动 2.LinearLayout:充当titlebar使用,设置属性app:layout_collapseMode="pin"使其位置固定不动 ps:可使用ToolBar代替上面的的LinearLayout,可是此时CollapsingToolbarLayout的minHeight,无效会自动匹配该Toolbar的高度, 且该Toolbar不太好定义样式。 --> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsingToolbarLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:minHeight="200dp" app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"> <!--该RelativeLayout定义了总体的可折叠CollapsingToolbarLayout的底层布局 有总体的背景图片以ImageView引入,设置其android:scaleType="centerCrop"防止背景变形 其余为所须要的自定义的布局 --> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!--总体的折叠视图背景图片--> <ImageView android:id="@+id/textdemo_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/bg3" /> <!-- 本身所需的要跟随滚动折叠的布局试图--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!--简单的文字布局--> <include layout="@layout/line_mode_view"></include> <include layout="@layout/line_mode_view"></include> </LinearLayout> </LinearLayout> </RelativeLayout> <!--充当titleBar的布局--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_collapseMode="pin"> <!--占位控件,用来使下面的布局偏移到状态栏之下,具体高度代码设置--> <View android:id="@+id/textdemo_titleholderview" android:layout_width="match_parent" android:layout_height="0dp" /> <!--实际的TitleBar布局文件--> <include layout="@layout/titlebar_view"></include> </LinearLayout> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id="@+id/textdemo_recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> </android.support.v7.widget.RecyclerView> </android.support.design.widget.CoordinatorLayout></LinearLayout>
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerview; private LinearLayout titlebar; private View titlebarHolder; private ImageView textdemo_image; private CollapsingToolbarLayout collapsingToolbarLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//沉浸式状态栏实现的前提 this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);//可不加 } else { } setContentView(R.layout.activity_main); initView(); initEvent(); } private void initView() { recyclerview = (RecyclerView) findViewById(R.id.textdemo_recyclerview); titlebar = (LinearLayout) findViewById(R.id.textdemo_titlebar); titlebarHolder = findViewById(R.id.textdemo_titleholderview); textdemo_image = (ImageView) findViewById(R.id.textdemo_image); collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout); } private void initEvent() { LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext()); textdemo_image.setScaleType(ImageView.ScaleType.CENTER_CROP); ViewGroup.LayoutParams layoutParams = titlebarHolder.getLayoutParams(); layoutParams.height = getStatueBarHeight(); titlebarHolder.setLayoutParams(layoutParams); RecyclerView.Adapter adapter = new MyAdapter(); recyclerview.setLayoutManager(manager); recyclerview.setAdapter(adapter); //fitSystemWindow(); } //private void fitSystemWindow() { // // ViewCompat.setOnApplyWindowInsetsListener(collapsingToolbarLayout, new OnApplyWindowInsetsListener() { // @Override // public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { // insets.replaceSystemWindowInsets(0, 0, 0, 0); // return insets.consumeSystemWindowInsets(); // return insets; // } // }); //} private int getStatueBarHeight() {//拿取状态栏的高度 int identifier = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (identifier > 0) { return (int) getResources().getDimension(identifier); } return 0; } class MyAdapter extends RecyclerView.Adapter { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewholder(new TextView(getApplicationContext())); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((MyViewholder) holder).updateData("我是模拟条目文本:::::" + position); } @Override public int getItemCount() { return 50; } } class MyViewholder extends RecyclerView.ViewHolder { public MyViewholder(View itemView) { super(itemView); } public void updateData(String str) { ((TextView) itemView).setText(str); } }}
- bug相关解决代码
enterAlways - 实现quick return效果, 当向下移动时,当即显示该View(即初始时该View为所有折叠位于屏幕以外,当下拉动做时该控件会首先作出反应,直接显示出该view)
enterAlwaysCollapsed - 当你的View已经设置minHeight属性又使用此标志时,你的View只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。(此属性值与上面属性配合使用)
exitUntilCollapsed - 向上滚动时收缩View,但能够固定Toolbar一直在上面。
4.该该可滑动控件能够是RecyclerView或者v4包中的NestedScrollView,可是对于Listview无效。java
具体更为详细的这几个控件的介绍可参考:http://blog.csdn.net/lxk_1993/article/details/51443045android
下面是踩坑时间可跳过(在design包版本为:com.android.suport:design:23.4.0时会出现如下情况):git
此时加上上面介绍的沉浸式页面的实现按说已经能够实现所需效果,可是发现github
1.在KITKAT版本手机上运行时效果已经正常,windows
2.但在Lollipop版本的系统上运行时会出现以下效果:review代码可知此时关于沉浸式状态栏的设置,只有在Activity中添加的Flag(this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);)app
鉴于此参考大部分的作法,在CollapsingToolbarLayout直接子View添加属性fitsSystemWindows = true;(回顾此属性的意义为系统自动适配状态栏高度到控件,给设置此属性的View添加paddingTop值,以适应系统窗口 好比:状态栏/导航栏),因此按说不该该添加此属性值才对。ide
但实际效果为:函数
1.Lollipop版本的系统运行已经达到预期的效果。布局
2.可是,KITKAT版本的系统运行效果却又出现了和上面相似效果以下(有部分差异,此时KITKAT版本只有CollapsingToolbarLayout下的第一个设置了fitsSystemWindows = true的View会下移一个状栏的高度,其余的不会下移,在此表现即为CollapsingToolbarLayout子View中的第一个RelativeLayout会下移,可是做为TitleBar使用的第二个子View却不会下移)ui
for (int i = 0; i < collapsingToolbarLayout.getChildCount(); i++) {//拿取CollapsingToolbarLayout的子View并根据版本设置其fitsSystemWindows的属性 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { collapsingToolbarLayout.getChildAt(i).setFitsSystemWindows(true); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { collapsingToolbarLayout.getChildAt(i).setFitsSystemWindows(false); } else {//不支持沉浸式状态栏的版本,要把使View向下偏移的占位控件的高度设置为0 //设置充当TitleBar使用的控件里面的占位空间的高度为0, } }
// Update our child view offset helpers for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); if (mLastInsets != null && !ViewCompat final int insetTop = mLastInsets.getSystemWindowInsetTop(); if (child.getTop() < insetTop) { // If the child isn't set to fit system windows but is drawing within the inset // offset it down ViewCompat.offsetTopAndBottom(child, insetTop); } } getViewOffsetHelper(child).onViewLayout(); }
- .getFitsSystemWindows(child)) {
private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) { if (mLastInsets != insets) { mLastInsets = insets; requestLayout(); } return insets.consumeSystemWindowInsets(); }
ViewCompat.setOnApplyWindowInsetsListener(this, new android.support.v4.view.OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { return setWindowInsets(insets); } });
/** *方法的相关声明 * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying * window insets to this view. This will only take effect on devices with API 21 or above. */ public static void setOnApplyWindowInsetsListener(View v, OnApplyWindowInsetsListener listener) { IMPL.setOnApplyWindowInsetsListener(v, listener); }
static final ViewCompatImpl IMPL; static { final int version = android.os.Build.VERSION.SDK_INT; if (BuildCompat.isAtLeastN()) { IMPL = new Api24ViewCompatImpl(); } else if (version >= 23) { IMPL = new MarshmallowViewCompatImpl(); } else if (version >= 21) { IMPL = new LollipopViewCompatImpl(); } else if (version >= 19) { IMPL = new KitKatViewCompatImpl(); } else if (version >= 18) { IMPL = new JbMr2ViewCompatImpl(); } else if (version >= 17) { IMPL = new JbMr1ViewCompatImpl(); } else if (version >= 16) { IMPL = new JBViewCompatImpl(); } else if (version >= 15) { IMPL = new ICSMr1ViewCompatImpl(); } else if (version >= 14) { IMPL = new ICSViewCompatImpl(); } else if (version >= 11) { IMPL = new HCViewCompatImpl(); } else { IMPL = new BaseViewCompatImpl(); } }
// Update our child view offset helpers for (int i = 0, z = getChildCount(); i < z; i++) { final View child = getChildAt(i); if (mLastInsets != null && !ViewCompat.getFitsSystemWindows(child)) { final int insetTop = mLastInsets.getSystemWindowInsetTop(); if (child.getTop() < insetTop) { // If the child isn't set to fit system windows but is drawing within the inset // offset it down ViewCompat.offsetTopAndBottom(child, insetTop); } } getViewOffsetHelper(child).onViewLayout(); }
ViewCompat.setOnApplyWindowInsetsListener(collapsingToolbarLayout, new OnApplyWindowInsetsListener() { @Override public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { //insets.replaceSystemWindowInsets(0, 0, 0, 0); 该行代码无效 // return insets.consumeSystemWindowInsets(); return insets; } });