防止Android状态栏出现(修改版)

30

我正在实现一个自助模式应用程序,并成功地使应用程序在Android 4.3之后全屏且没有状态栏出现,但无法在4.3和4.4版本中隐藏状态栏,因为当我们在屏幕顶部向下滑动时状态栏会出现。

我尝试以下方法使其全屏:

  • 在清单文件中指定全屏主题
  • 设置窗口标志,即setFlags
  • setSystemUiVisibility

可能是重复的问题,但没有找到具体的解决方案。

永久隐藏Android状态栏

最终我想要的是如何在一个活动中永久隐藏状态栏?在Android 4.3、4.4、5和6版本中均适用。

3个回答

86

由于无法在KitKat设备上防止状态栏出现在全屏模式中,因此我们进行了一项修改以满足要求,即阻止状态栏扩展。

为了使此方法起作用,该应用程序未被制成全屏。我们在状态栏上叠加了一个覆盖层并消耗了所有输入事件,从而防止了状态栏的扩展。

注意:

  • customViewGroup是扩展任何布局(帧、相对布局等)并消耗触摸事件的自定义类。
  • 要消耗触摸事件,请重写视图组的onInterceptTouchEvent方法并返回true。

更新:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> 

自定义视图组实现

代码:

WindowManager manager = ((WindowManager) getApplicationContext()
            .getSystemService(Context.WINDOW_SERVICE));

WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|

            // this is to enable the notification to recieve touch events
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |

            // Draws over status bar
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

    localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
    localLayoutParams.height = (int) (50 * getResources()
            .getDisplayMetrics().scaledDensity);
    localLayoutParams.format = PixelFormat.TRANSPARENT;

    customViewGroup view = new customViewGroup(this);

    manager.addView(view, localLayoutParams);

1
在我尝试了两周之后,绝对是唯一有效的方法。完全阻止了状态栏扩展。 - MikeyB
2
@user1416682 这是一个非常基础的customViewGroup,可以帮助你入门。链接 - sarme
2
@AbhimaanMadhav,我没有想到那个 :) 调用manager.removeView(view) 对我来说已经足够了。顺便问一下,你能否将其应用于禁用软按钮?目前对我来说还不起作用。 - longwalker
2
不再支持Android 8.0或Oreo设备。 - Run
3
在安卓8及以上版本的设备上,任何用户添加的窗口或视图都无法显示在状态栏之上,状态栏始终可以被下拉。 - Run
显示剩余26条评论

19

在Android M中,您需要获取额外的权限才能制作覆盖层。 android.permission.SYSTEM_ALERT_WINDOW 是不够的!因此,我使用了Abhimaan答案中的代码在 disableStatusBar() 中,并且必须创建意图以打开正确的设置对话框。我还在 onDestroy() 中添加了删除视图,以便在应用程序退出时启用状态栏。我还将叠加高度减小到40,因为它似乎足够了。该代码适用于5.1和6.0。

public static final int OVERLAY_PERMISSION_REQ_CODE = 4545;
protected CustomViewGroup blockingView = null;

@Override
protected void onDestroy() {

    super.onDestroy();

    if (blockingView!=null) {
        WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
        manager.removeView(blockingView);
    }
}


@Override
protected void onCreate(Bundle savedInstanceState) {

        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "Please give my app this permission!", Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
            } else {
                disableStatusBar();
            }
        }
        else {
            disableStatusBar();
        }
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "User can access system settings without this permission!", Toast.LENGTH_SHORT).show();
        }
        else
        { disableStatusBar();
        }
    }
}

protected void disableStatusBar() {

    WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));

    WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
    localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
    localLayoutParams.gravity = Gravity.TOP;
    localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |

            // this is to enable the notification to receive touch events
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |

            // Draws over status bar
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

    localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
    localLayoutParams.height = (int) (40 * getResources().getDisplayMetrics().scaledDensity);
    localLayoutParams.format = PixelFormat.TRANSPARENT;

    blockingView = new CustomViewGroup(this);
    manager.addView(blockingView, localLayoutParams);
}

1
很不幸,对我来说包括这个在内的所有解决方案都没有起作用。在我的自助应用程序中,我仍然可以下拉状态栏。我的应用程序处于固定模式下,这意味着状态栏为空白,但无论如何,我仍然可以从顶部滑动它。我不明白这是什么类型的固定,为什么Android在固定模式下不会永久隐藏它,因为它已经是空白的了。 - zeeshan
这是唯一对我有效的解决方案。使用这种方法,我还能够屏蔽底部的软键。谢谢!!! - netsuite_insights
它能在已Root的设备上运行吗?我已经在5.1棒棒糖(Lollipop)的Root设备上尝试了,但没有任何结果... - tsiro
3
无法在奥利奥系统上使用,请提供奥利奥版本的支持。 - umesh shakya
1
@Honza:对于Android 4.4和5,只需在清单文件中添加权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 即可。 - Alexey Ozerov
显示剩余10条评论

0
在我之前工作的项目中,我找到了一个解决方案,但是花费了很长时间。来自Stackoverflow和其他地方的各种线程帮助我想出了这个方法。它是针对Android M的一种解决方法,但却非常完美。因为有人要求,所以我认为如果可以使任何人受益,我应该在此发布。

现在已经过了一段时间,我不记得所有的细节,但CustomViewGroup是覆盖主ViewGroup的类,可以检测用户从顶部向下滑动以显示状态栏。但我们不想显示它,因此检测到用户的拦截并忽略任何进一步的操作,即Android OS将不会收到打开隐藏状态栏的信号。

然后还包括用于显示和隐藏状态栏的方法,您可以将其原样复制/粘贴到您希望显示/隐藏状态栏的代码中。

/**
 * This class creates the overlay on the status bar which stops it from expanding.
 */
public static class CustomViewGroup extends ViewGroup {

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

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.v("customViewGroup", "********** Status bar swipe intercepted");
        return true;
    }
}

public static void allowStatusBarExpansion(Context context) {
    CustomViewGroup view = new CustomViewGroup(context);
    WindowManager manager = ((WindowManager) context.getApplicationContext()
            .getSystemService(Context.WINDOW_SERVICE));
    manager.removeView(view);
}

// Stop expansion of the status bar on swipe down.
public static void preventStatusBarExpansion(Context context) {
    WindowManager manager = ((WindowManager) context.getApplicationContext()
            .getSystemService(Context.WINDOW_SERVICE));

    Activity activity = (Activity) context;
    WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
    localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
    localLayoutParams.gravity = Gravity.TOP;
    localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |

            // this is to enable the notification to receive touch events
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |

            // Draws over status bar
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

    localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
    //https://dev59.com/hnNA5IYBdhLWcg3wUL5Y
    int resId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
    int result = 0;
    if (resId > 0) {
        result = activity.getResources().getDimensionPixelSize(resId);
    }

    localLayoutParams.height = result;

    localLayoutParams.format = PixelFormat.TRANSPARENT;

    CustomViewGroup view = new CustomViewGroup(context);

    manager.addView(view, localLayoutParams);
}

我遇到了这个错误:android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@acbb68b -- permission denied for window type 2010。 我该如何解决它? - Cursed
请在 API >26 下使用 TYPE_APPLICATION_OVERLAY 代替 TYPE_SYSTEM_ERROR。 - Krasavello13
Android操作系统无法接收信号以打开隐藏的状态栏,具体如何实现? - Mahm00d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接