当透明状态栏遇到输入框

前言

国际惯例:先发一通牢骚,他大爷的&*¥%……&,别误会我不是骂产品的,我是痛诉Android的状态栏适配的
事情是这样的,文章详情界面需要透明状态栏,下面可以评论,需求效果图如下。
头部状态栏透明:

这里写图片描述

底部有输入框:

这里写图片描述

我想关于透明状态栏的实现不算难,网上一搜有很多文章都写过,我在这里提供一些参考链接:

  • Android开发:Translucent System Bar 的最佳实践
  • 透明状态栏(StatusBar)的全适配
  • Android状态栏合集-管你透不透明
  • Android-transulcent-status-bar
  • Android 透明状态栏实现方案

难点是:

  1. 如果布局中要用到软键盘,就必须加上android:fitsSystemWindows=”true”属性,否则键盘弹出时,adjustResize属性不起作用,这样就会导致软件盘覆盖输入框。但是当你加上android:fitsSystemWindows=”true”时会导致状态栏变成灰白色
  2. 华为手机,准确的说是Android原生的系统在你设置完透明状态栏后会是半透明,并不是透明

实现

踩坑步骤

先实现透明:

Window w = getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    w.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    w.setStatusBarColor(Color.TRANSPARENT);
}

效果如下
当透明状态栏遇到输入框_第1张图片

但是设置android:fitsSystemWindows=”true”后效果是这样

当透明状态栏遇到输入框_第2张图片

这样的效果肯定不是我们想要的效果,后来我尝试了不使用android:fitsSystemWindows=”true”,自己监听布局变化,代码将输入框顶起到软件盘上面,下面是代码可以了解一下,但是这样写有深坑,就是华为手机还有Google原生系统有一个东西叫做NavigationBar,就是下面这个。这个东西会影响我的计算,总之自己计算就不是个明智的选择

这里写图片描述

    /**
     * 动态调整布局,使软件盘弹出时输入框弹出到软件盘上面
     */
    private void adjustKeyboard() {
        //初始化控件
        rootLayout = (RelativeLayout) findViewById(R.id.root_layout);
        final View v_push = findViewById(R.id.v_push);
        //创建一个矩形, 用于获取输入法高度
        final Rect r = new Rect();
        //获取屏幕根View
        final View decorView = this.getWindow().getDecorView();
        //获取顶起ScrollView用的控件的布局参数, 我们是用LinearLayout包裹的所以注意来兴是LinearLayout的LayoutParams
        final RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) v_push.getLayoutParams();
        //获取布局根View的ViewTree观察器, 监听布局变化
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            //当布局改变时, 会调用本方法
            @Override
            public void onGlobalLayout() {
                //判断键盘显隐状态
                decorView.getWindowVisibleDisplayFrame(r);
                int screenHeight = rootLayout.getRootView().getHeight();
                int keyboardHeight = screenHeight - r.bottom;
                if (currentKeyboardHeight != keyboardHeight) {
                    // 当键盘状态不一致时, 再改变布局
                    layoutParams.height = keyboardHeight;
                    v_push.setLayoutParams(layoutParams);
                    //将键盘高度保存
                    currentKeyboardHeight = keyboardHeight;
                }
            }
        });
    }

后来在stackoverflow搜索到了这个坑相关的问题如:

windowSoftInputMode=“adjustResize” not working with translucent action/navbar
Android How to adjust layout in Full Screen Mode when softkeyboard is visible
我采取了其中一种方案自定义FrameLayout作为界面的根布局解决上面那一块灰白色的东东

/**
 * @author Kevin
 * Date Created: 3/7/14
 *
 * https://code.google.com/p/android/issues/detail?id=63777
 * 
 * When using a translucent status bar on API 19+, the window will not
 * resize to make room for input methods (i.e.
 * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and
 * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are
 * ignored).
 * 
 * To work around this; override {@link #fitSystemWindows(android.graphics.Rect)},
 * capture and override the system insets, and then call through to FrameLayout's
 * implementation.
 * 
 * For reasons yet unknown, modifying the bottom inset causes this workaround to
 * fail. Modifying the top, left, and right insets works as expected.
 */
public final class CustomInsetsFrameLayout extends FrameLayout {
    private int[] mInsets = new int[4];

    public CustomInsetsFrameLayout(Context context) {
        super(context);
    }

    public CustomInsetsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public final int[] getInsets() {
        return mInsets;
    }

    @Override
    protected final boolean fitSystemWindows(Rect insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // Intentionally do not modify the bottom inset. For some reason,
            // if the bottom inset is modified, window resizing stops working.
            // TODO: Figure out why.

            mInsets[0] = insets.left;
            mInsets[1] = insets.top;
            mInsets[2] = insets.right;

            insets.left = 0;
            insets.top = 0;
            insets.right = 0;
        }

        return super.fitSystemWindows(insets);
    }

    @Override
    public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            mInsets[0] = insets.getSystemWindowInsetLeft();
            mInsets[1] = insets.getSystemWindowInsetTop();
            mInsets[2] = insets.getSystemWindowInsetRight();
            return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
                    insets.getSystemWindowInsetBottom()));
        } else {
            return insets;
        }
    }
}

效果如下:
当透明状态栏遇到输入框_第3张图片

你会看到,这样写确实灰白色statusbar没了,但是还是出了幺蛾子,titleBar被压在了状态栏下面,显然这也不是我们想要的效果,没有看到想要的效果先不要骂娘,你会发现我们距离成功之后一个statusBar的高度,好吧,这还不好办给他来个padding不就行了对就这么干。最终:

    private void initTitleBar() {
        titleBarLayout= (FrameLayout) findViewById(R.id.title_bar_layout);
        int statusBarHeight = getStatusBarHeight();
        Window w = getWindow();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                w.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
                w.setStatusBarColor(Color.TRANSPARENT);
            }else{
                w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
            //改变titlebar的高度
            ViewGroup.LayoutParams lp = titleBarLayout.getLayoutParams();
            lp.height += statusBarHeight;
            titleBarLayout.setLayoutParams(lp);
            //设置paddingtop
            titleBarLayout.setPaddingRelative(0, statusBarHeight, 0, 0);
        }
    }

    //获取statusbar高度
    public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

效果:
当透明状态栏遇到输入框_第4张图片

总结

上面的方案只是解决方案的一种,具体问题说不定具体解决,如果你之后一个界面需要这样,你可以直接按照这种方式写,如果多个界面建议封装起来写在BaseActivitiy里面

有时候你会遇到半透明的情况效果如下:
当透明状态栏遇到输入框_第5张图片
可以加一句代码w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);在下面代码的前面

w.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            w.setStatusBarColor(Color.TRANSPARENT);

参考链接

  • http://stackoverflow.com/questions/21092888/windowsoftinputmode-adjustresize-not-working-with-translucent-action-navbar

你可能感兴趣的:(Android,Android遇问题,自定义View)