values-v19

    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

MainActivity 布局代码:

<?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:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <android.support.v7.widget.Toolbar        android:id="@+id/toolbar"        android:layout_width="match_parent"        android:background="@mipmap/bg_toolbar"        android:layout_height="?actionBarSize"        android:background="@color/colorPrimary"        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Hello World!" />LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

MainActivity 代码:

public class MainActivity extends AppCompatActivity {    private Toolbar mToolbar;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mToolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(mToolbar);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.menu_statusbar, menu);        return super.onCreateOptionsMenu(menu);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

DetailActivity 布局代码

<?xml version="1.0" encoding="utf-8"?>.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent" >    .support.design.widget.AppBarLayout        android:id="@+id/id_appbar"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">        .support.design.widget.CollapsingToolbarLayout            android:layout_width="match_parent"            android:layout_height="180dp"            app:layout_scrollFlags="scroll|exitUntilCollapsed"            app:expandedTitleMarginStart="48dp"            app:expandedTitleMarginEnd="64dp"            app:statusBarScrim="@null">            "@+id/id_toolbar_backdrop"                android:scaleType="centerCrop"                android:src="@mipmap/img"                android:layout_width="match_parent"                android:layout_height="match_parent"                app:layout_collapseMode="parallax" />            .support.v7.widget.Toolbar                android:id="@+id/id_toolbar"                android:layout_width="match_parent"                android:layout_height="?attr/actionBarSize"                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"                app:layout_collapseMode="pin" />        .support.design.widget.CollapsingToolbarLayout>    .support.design.widget.AppBarLayout>    .support.v4.widget.NestedScrollView        android:layout_width="match_parent"        android:layout_height="match_parent"        app:layout_behavior="@string/appbar_scrolling_view_behavior">    .support.v4.widget.NestedScrollView>.support.design.widget.CoordinatorLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

DetailActivity

public class DetailActivity extends AppCompatActivity {    private Toolbar mToolbar;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_detail);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);        }        mToolbar = (Toolbar) findViewById(R.id.id_toolbar);        setSupportActionBar(mToolbar);        getSupportActionBar().setDisplayHomeAsUpEnabled(true);    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        switch (item.getItemId()) {            case android.R.id.home:                ActivityCompat.finishAfterTransition(this);                return true;        }        return super.onOptionsItemSelected(item);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在 style.xml 中设置了 true 属性后,会发现出现了下面这种情况: 
Toolbar 到了状态栏的下面

那么这种情况,网上也已经给出了相关的解决方法:在布局里面设置 android:fitsSystemWindows="true" 属性。

但是这不是最终解决方案,因为我们想要这样的效果:

当然,这在 5.0 以上是可以轻松实现的,但是在 Android 4.4 上,却是这样子的:

背景图并没有铺满到状态栏的位置,所以,为了兼容 Android 4.4 ,我们不使用 android:fitsSystemWindows="true" 属性。

有研究过的朋友,可能已经知道我要做什么了。

原理也很简单:就是给 Toolbar 设置一个 PaddingTop

  • 如果有使用过 SystemBarTint 
    这个库的朋友,应该能看的出来,SystemBarTint 也是通过获取 状态栏高度 来实现的。
  • SystemBarTint 
    会新建一个高度等于状态栏高度的 View ,并把 View 添加到 DecorView 。
  • SystemBarTint 这种方法不是很好,这样 ContentView 
    区域就少了一块,而且给 Toolbar 设置背景图片也不能铺满到状态栏位置。

自定义 Toolbar:

public class CompatToolbar extends Toolbar {    private boolean mLayoutReady;    public CompatToolbar(Context context) {        this(context, null);    }    public CompatToolbar(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, android.support.v7.appcompat.R.attr.toolbarStyle);    }    public CompatToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        if (!mLayoutReady) {            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {                if ((getWindowSystemUiVisibility() &                        (SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) ==                        (SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) {                    int statusBarHeight = getStatusBarHeight();                    ViewGroup.LayoutParams params = getLayoutParams();                    params.height = getHeight() + statusBarHeight;                    setPadding(0, statusBarHeight, 0, 0);                }            }            mLayoutReady = true;        }    }    private int getStatusBarHeight() {        Resources res = Resources.getSystem();        int resId = res.getIdentifier("status_bar_height", "dimen", "android");        if (resId > 0) {            return res.getDimensionPixelSize(resId);        }        return 0;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

自定义 CompatToolbar 主要通过 (getWindowSystemUiVisibility() & 
(SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 
(SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
 判断是不是透明状态栏,如果是就执行里面的内容:

  • 获取状态栏高度。
  • 修改 父ViewGroup 的 LayoutParams 的高度为 原高度 + 状态栏高度
  • 最后给 Toolbar 设置 PaddingTop 以保证 Toolbar 显示在状态栏下方。

下面修改一下布局,把我们的自定义 CompatToolbar 替换掉原的 Toolbar:

Android 4.4 上效果出来了,已经实现了我们想要的效果。

但是, Android 5.0+ 上却出问题了;如果已经尝试过自定义 Toolbar 解决 Android 4.4 兼容的朋友可能已经遇到过了: 
(由于不会搞 GIF 图,所以只截了两张图,一张是 Toolbar 收起前,一张是 Toolbar 收起后)

这又是什么情况??

经过多次查看源码和 Debug 调试,终于发现了问题所在:

在 CollapsingToolbarLayout 的 onLayout 方法中:

@Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        ......        // 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();        }        ......    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在 Android 5.0 以上的系统中, mLastInsets 这个参数是不为空的:

  • 先通过mLastInsets.getSystemWindowInsetTop() 拿到状态栏高度。
  • 然后通过 ViewCompat.offsetTopAndBottom(child, insetTop) 去给子 View (CollapsingToolbarLayout 布局内的 ImageView 和 Toolbar)设置顶部偏移量为 状态栏高度。

那么 mLastInsets 又是什么呢?

private WindowInsetsCompat mLastInsets;
  • 1
  • 1

从名字可以看出来:窗口插图。那么它是什么时候初始化的呢?

    public CollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {        ......        ViewCompat.setOnApplyWindowInsetsListener(this,                new android.support.v4.view.OnApplyWindowInsetsListener() {                    @Override                    public WindowInsetsCompat onApplyWindowInsets(View v,                            WindowInsetsCompat insets) {                        return setWindowInsets(insets);                    }                });    }    private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {        if (mLastInsets != insets) {            mLastInsets = insets;            requestLayout();        }        return insets.consumeSystemWindowInsets();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

从上面的代码可以看出来, CollapsingToolbarLayout 是在初始化的时候给自己设置了一个窗口插图监听器

    /**     * 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);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

查看 ViewCompat.setOnApplyWindowInsetsListener() 源码可以看到,注释里面写着:此方法只对 Api 21 以上有效

那么现在可以理解为什么 Android 4.4 上的 mLastInsets 为空了;ViewCompat.setOnApplyWindowInsetsListener() 原来就是为了实现 Android 5.0 以上的插图效果的。

现在清楚了,只要 mLastInsets 为空就能解决这个问题了,可是翻遍了 CollapsingToolbarLayout 的源码只有一个设置 mLastInsets 方法:

private WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {        if (mLastInsets != insets) {            mLastInsets = insets;            requestLayout();        }        return insets.consumeSystemWindowInsets();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

可以看到这是一个私有的方法,也就是说我们用不了这个方法(就算能用也不能传个 null 进去吧……)。

自定义 CompatCollapsingToolbarLayout:

public class CompatCollapsingToolbarLayout extends CollapsingToolbarLayout {    private boolean mLayoutReady;    public CompatCollapsingToolbarLayout(Context context) {        this(context, null);    }    public CompatCollapsingToolbarLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CompatCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        if (!mLayoutReady) {            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {                if ((getWindowSystemUiVisibility() &                        (SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) ==                        (SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) {                    try {                        Field mLastInsets = CollapsingToolbarLayout.class.getDeclaredField("mLastInsets");                        mLastInsets.setAccessible(true);                        mLastInsets.set(this, null);                    } catch (NoSuchFieldException e) {                        e.printStackTrace();                    } catch (IllegalAccessException e) {                        e.printStackTrace();                    }                }            }            mLayoutReady = true;        }        super.onLayout(changed, left, top, right, bottom);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

还是老方法,通过 (getWindowSystemUiVisibility() & 
(SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 
(SYSTEM_UI_FLAG_LAYOUT_STABLE|SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
 判断是不是透明状态栏,如果是:

  • 通过反射拿到 CollapsingToolbarLayout 中的变量 mLastInsets ,然后置为空。

下面是 Androi 5.0+ 效果图:

至此,大功告成!

/**************************************************/

也许有人会考虑另一种方法:

  • 只自定义CollapsingToolbarLayout,通过反射给 private WindowInsetsCompat setWindowInsets(windowinsets) 方法设值, 结合android:fitsSystemWindows="true" ,去兼容 Android 4.4

这样不是更简单吗?

我只能很遗憾的告诉你,WindowInsets 相关的类和方法是 API 20+ 才有的。

源码地址:https://github.com/fanxin92/TransparentStatusBarSample

更多相关文章

  1. android中菜单的使用
  2. Android(安卓)Re-installation failed解决方法
  3. Android(安卓)TV自定义通用标题栏(组合控件)
  4. Android实现CoverFlow效果三
  5. android曲线图
  6. Android:获取屏幕分辨率方法汇总
  7. android使用Intent操作拨打号码发送短信
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android构建面试知识
  2. Activity启动流程详解
  3. 关于Android的开发经验总结
  4. android之命令行创建AVD
  5. Android(安卓)TTS 实战一:认识 TTS
  6. Android双机(网络和USB)调试及其完美ROOT
  7. Android入门教程(三)之------导入现有And
  8. 基于Android(安卓)5.0的源码编译过程
  9. Android(安卓)doc 之应用程序基础
  10. Windows Phone是一个严重被低估的移动操