一、前言
之前我的项目有个需求,是要配合 AppBarLayout
的上下滚动缩放,调整 Toolbar
的背景颜色与状态栏颜色,还要适配多 Fragment
切换时状态栏颜色也及时跟进,以实现视觉统一的效果。然后从 Support
包中发现了 ScrimInsetsFrameLayout
这个类,看了源码之后感觉可以为我所用,于是就继承了这个类再加以反射技术,实现了想要的效果。并且目前来看,在效果方面(Android5.0 +)
二、实现
依赖的 Support
包:
compile 'com.android.support:design:26.1.0'
InsetsLayout.java
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.support.design.internal.ScrimInsetsFrameLayout;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* @author Jossing , Create on 2017/10/22
*/
public class InsetsLayout extends ScrimInsetsFrameLayout {
private List<StatusBarHeightObserver> mObserverList;
public InsetsLayout(Context context) {
this(context, null);
}
public InsetsLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public InsetsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mObserverList = new ArrayList<>();
}
@Override
protected void onInsetsChanged(WindowInsetsCompat insets) {
for (StatusBarHeightObserver observer : mObserverList) {
observer.update(insets.getSystemWindowInsetTop());
}
}
/**
* 添加状态栏高度的观察者
* @param observer 观察者
*/
public void addObserver(@NonNull StatusBarHeightObserver observer) {
mObserverList.add(observer);
}
/**
* 移除指定的观察者
* @param observer 观察者
*/
public void removeObserver(@NonNull StatusBarHeightObserver observer) {
mObserverList.remove(observer);
}
/**
* 清空观察者
*/
public void clearObserver() {
mObserverList.clear();
}
/**
* 设置状态栏下面 View 的前景颜色
* @param color 指定颜色的 16 进制整数值
*/
public void setInsetForegroundColor(@ColorInt int color) {
setInsetForeground(new ColorDrawable(color));
}
/**
* 设置状态栏下面 View 的前景颜色
* @param colorId 指定颜色的资源 id
*/
public void setInsetForegroundColorId(@ColorRes int colorId) {
setInsetForeground(new ColorDrawable(getResources().getColor(colorId)));
}
/**
* 设置状态栏下面 View 的前景
* @param insetForeground 前景可绘制对象
*/
public void setInsetForeground(@NonNull Drawable insetForeground) {
try {
Field field = getClass().getSuperclass().getDeclaredField("mInsetForeground");
field.setAccessible(true);
field.set(this, insetForeground);
} catch (Exception e) {
e.printStackTrace();
}
invalidate();
}
/**
* 获取状态栏下面 View 的前景
* @return 状态栏下面 View 的前景
*/
public Drawable getInsetForeground() {
try {
Field field = getClass().getSuperclass().getDeclaredField("mInsetForeground");
field.setAccessible(true);
return (Drawable)field.get(this);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 状态栏高度变化的回调接口
*/
public interface StatusBarHeightObserver {
/**
* 当状态栏高度发生变化时回调这个方法
* @param statusBarHeight 状态栏高度的像素值
*/
void update(int statusBarHeight);
}
}
三、使用
1. 布局文件 xxxx.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- DrawerLayout 和 InsetsLayout 的 android:fitsSystemWindows 属性必须设置为 true -->
<!-- 可以用 InsetsLayout 的 app:insetForeground 属性来设置默认的前景颜色 -->
<android.support.v4.widget.DrawerLayout
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:fitsSystemWindows="true">
<yourpackage.InsetsLayout
android:id="@+id/contentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:insetForeground="#0000" />
</android.support.v4.widget.DrawerLayout>
2. 在 Activity 里面
private InsetsLayout mInsetsLayout;
private void xxx() {
mInsetsLayout = (InsetsLayout) findViewById(R.id.contentFrame);
//设置状态栏颜色
// mInsetsLayout.setInsetForeground(new BitmapDrawable());
// mInsetsLayout.setInsetForegroundColor(Color.RED);
mInsetsLayout.setInsetForegroundColorId(R.color.colorPrimaryDark);
//添加状态栏高度观察者
mInsetsLayout.addObserver(new InsetsLayout.StatusBarHeightObserver() {
@Override
public void update(int statusBarHeight) {
//这里获取到的这个 int 值就是状态栏的高度了(px)
Log.i("statusBarHeight", statusBarHeight);
}
}
}
四、最后
代码介绍不多,但都有注释,大家应该都好理解。实现的那部分代码里面,有用到 Java 反射技术,还不懂的朋友不妨先自行了解反射,再回来看这篇博客。如果有好的建议呢,欢迎大家在评论区留言回复,或者联系我。
~~谢谢大家。