27

我有一个状态栏,上面设置了以下主题:

<!-- Base Theme for all "Material"-esque styles. We use NoActionBar
     so we can use the Toolbar at runtime.
-->
<style name="Material" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:windowTranslucentStatus">true</item>
    ...
</style>

我的大多数活动也有一个DrawerLayout,它可以根据我的喜好设置状态栏的颜色:

    mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.myapp_green));

我使用的是Toolbar, 而不是 default ActionBar,所以它存在于我的布局中(即导航抽屉绘制在它上面)。

一切正常,除了在我的一项活动中,我有一个带有ActionMode. 当它ActionMode被激活时(使用长按),它会覆盖Toolbar使用:

<item name="android:windowActionModeOverlay">true</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionModeStyle">@style/Material.Widget.ActionMode</item>

Material.Widget.ActionMode风格是:

<style name="Material.Widget.ActionMode" parent="@style/Widget.AppCompat.ActionMode">
    <item name="android:background">@color/myapp_green</item>
    <item name="background">@color/myapp_green</item>
</style>

现在,问题是,每当发生这种情况时,状态栏就会从myapp_green彩色变为黑色。这几乎就像状态栏半透明被关闭(我使用的是 Android 5.0)。我想知道如何才能使这种行为不发生,并保持状态栏颜色/半透明度不变。

我尝试添加<item name="android:windowTranslucentStatus">true</item>动作模式的样式,以及添加<item name="android:statusBarColor">@color/myapp_green</item>的样式ActionMode,均未成功。

更新:

我想知道这是否与我设置状态栏背景的古怪方式有关。我所有的 Activity 类都派生自 NavigationDrawerActivity.java:

/**
 * An {@link Activity} that supports a Navigation Drawer, which is a pull-out panel for navigation
 * menus. This drawer is pulled out from the left side of the screen (right side on RTL devices).
 */
public class NavigationDrawerActivity extends ActionBarActivity
  implements AdapterView.OnItemClickListener {

  private static final String LOGTAG = NavigationDrawerActivity.class.getSimpleName();

  private DrawerLayout mDrawerLayout;
  private ListView mDrawerList;
  private LayoutInflater mInflater;
  private NavigationDrawerItemAdapter mAdapter;
  private ActionBarDrawerToggle mDrawerToggle;

  private NavigationDrawerItem[] mNavigationDrawerItems;

  private Toolbar mAppBar;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
// We have to call super.setContentView() here because BaseActivity redefines setContentView(),
// and we don't want to use that.
super.setContentView(R.layout.navigation_drawer);

mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    setupNavigationDrawer();
  }

  @Override
  protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);

    // Sync the toggle state after onRestoreInstanceState has occurred.
    mDrawerToggle.syncState();
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    mDrawerToggle.onConfigurationChanged(newConfig);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    switch(id) {
      case android.R.id.home:
        return mDrawerToggle.onOptionsItemSelected(item);
    }

    return super.onOptionsItemSelected(item);
  }

  /**
   * Toggles the state of the navigation drawer (i.e. closes it if it's open, and opens it if
   * it's closed).
   */
  public void toggleNavigationDrawer() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
      closeNavigationDrawer();
    } else {
      openNavigationDrawer();
    }
  }

  /**
   * Opens the navigation drawer.
   */
  public void openNavigationDrawer() {
    mDrawerLayout.openDrawer(GravityCompat.START);
  }

  /**
   * Closes the navigation drawer.
   */
  public void closeNavigationDrawer() {
    mDrawerLayout.closeDrawer(GravityCompat.START);
  }

  /**
   * Initializes items specific to the navigation drawer.
   */
  private void setupNavigationDrawer() {
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.wiw_green));

        mAppBar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(mAppBar);

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);
        actionBar.setDisplayShowHomeEnabled(false);

        mDrawerToggle = new ActionBarDrawerToggle(
          this,                  /* Our context (Activity that hosts this drawer) */
          mDrawerLayout,         /* The DrawerLayout where the nav drawer will be drawn */
          R.string.drawer_open,  /* Description of "open drawer", for accessibility */
          R.string.drawer_close  /* Description of "close drawer", for accessibility */
        ) {

          /**
           * Called when a drawer has settled in a completely closed state.
           */
          public void onDrawerClosed(View view) {
            super.onDrawerClosed(view);
            supportInvalidateOptionsMenu();
          }

          /**
           * Called when a drawer has settled in a completely open state.
           */
          public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
            supportInvalidateOptionsMenu();
          }
        };

        mDrawerList = (ListView) mDrawerLayout.findViewById(R.id.drawer_list);

        mNavigationDrawerItems = buildNavDrawerItemsList();

        setupAdapter(mNavigationDrawerItems);

        setupNavigationDrawerHeader();

        mDrawerLayout.setDrawerListener(mDrawerToggle);
        mDrawerList.setOnItemClickListener(this);
      }

      @Override
      public void onItemClick(AdapterView<?> parent, View aView, int aPosition, long aId) {
        // Code not relevant
      }

      /**
       * Set the inner content view of this {@link NavigationDrawerActivity} to have a given layout.
       *
       * @param aLayoutId The id of the layout to load into the inner content view of this activity.
       */
      public void setDrawerContent(int aLayoutId) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ViewGroup root = (ViewGroup)findViewById(R.id.drawer_content);
        inflater.inflate(aLayoutId, root);
      }  
    }

我实际上必须运行DrawerLayout.setStatusBarBackgroundColor()它才能产生效果。只是更改colorPrimaryDarkvalues-v21/styles.xml状态栏没有影响。我觉得这可能是问题的根源......这些类正在从非 Material 主题转换为新的、类似 Material 的主题,所以我想知道在转换到colorPrimaryDark才能正确识别。

4

9 回答 9

6

我在这里发布答案是因为,虽然其他解决方案很有帮助,但他们都没有准确回答这个问题。我能够发现呼叫DrawerLayout.setStatusBarBackgroundColor()导致了麻烦。我的解决方案如下:

  1. 启用windowTranslucentStatuswindowDrawsSystemBarBackgrounds在主题中。

  2. 删除对DrawerLayout.setStatusBarBackgroundColor()my 内部的调用NavigationDrawerActivity,所有Activity类都从中派生。

  3. values-v21/styles.xml在我的基本主题中设置以下样式:

    <item name="android:colorPrimary">@color/app_green</item>
    <item name="colorPrimary">@color/app_green</item>
    <item name="android:colorPrimaryDark">@color/app_green_dark</item>
    <item name="colorPrimaryDark">@color/app_green_dark</item>
    <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
    <item name="android:statusBarColor">?attr/colorPrimaryDark</item>
    
  4. NavigationDrawerActivity类内部,在其onCreate()方法中,我执行以下操作(感谢@Tinadm 提供这部分答案): getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

  5. 在展示 的类中ActionMode,我添加了以下方法:

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        // This is to highlight the status bar and distinguish it from the action bar,
        // as the action bar while in the action mode is colored app_green_dark
        getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.app_green_darker));
      }
    
      // Other stuff...
      return true;
    }
    
    @Override
    public void onDestroyActionMode(ActionMode mode) {
      mActionMode = null;
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.app_green_dark));
      }
    }
    

销毁时重新启用半透明状态栏ActionMode可以使我的导航抽屉绘制在状态栏下方。还值得注意的是覆盖操作栏,因此如果在启用时(通过从左到右滑动)ActionMode拉出导航抽屉,它将覆盖导航抽屉。ActionMode我将在启用时禁用此功能ActionMode,以免混淆用户。

于 2015-05-26T22:47:52.503 回答
4

好吧,您的临时解决方案是在您最需要它或问题开始时调用此代码

if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP){
    getWindow().setStatusBarColor(Color.BLUE); 
    // or Color.TRANSPARENT or your preferred color
}

编辑

你试过这个吗

// actionMode.getCustomView().setBackgroundColor.(Color.TRANSPARENT);

@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {    
    //but this time you let the actionmode's view change the statusbar's
    // visibility
    actionMode.getCustomView().setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_LOW_PROFILE); //or preferred one
    return true;
}

当你的actionMode被销毁时,状态栏将被恢复,所以也覆盖它并重新设置你的状态栏颜色。

希望能帮助到你

于 2015-05-18T18:45:47.850 回答
3

不久前我遇到了同样的问题,我的解决方案是在 onCreateActionMode 和 onDestroyActionMode 设置并恢复状态栏颜色。

//onCreateActionMode
mStartColor =  activity.getWindow().getStatusBarColor();
getWindow().setStatusBarColor(color);

//onDestroyActionMode
activity.getWindow().setStatusBarColor(mStartColor);

当然,在使用 setStatusBarColor 方法之前,您需要检查它是否在棒棒糖上,因为它在 L 之前不可用。

额外:如果您使用 DrawerLayout,您还需要在其上设置 statusBarColor。

mDrawerLayout.setStatusBarBackground(new ColorDrawable(color));

希望有帮助!

于 2015-05-25T09:52:47.633 回答
2

我和你有完全相同的问题。我设法使用解决方法解决了它。

  1. 我做的第一件事就是windowTranslucentStatus=true从我的主题中删除;
  2. 然后我习惯getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);让内容填满状态栏下的区域。
  3. 我将<item name="android:colorPrimaryDark">@color/primary_dark</item>primary_dark设置为“#51000000”。注意 alpha 值以实现与 windowTranslucent 提供的相同的透明度。由于我的工具栏是蓝色的,我的状态栏现在是深蓝色的。
  4. 仅此一项并不能解决问题。当动作模式处于活动状态时,在我的情况下状态栏仍然是黑色的。但是现在,不使用 translucentStatusBar,我可以将颜色应用到我的状态栏,所以我使用了:

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        getActivity().getWindow().setStatusBarColor(Color.parseColor("#026592"));
        return true;
    }
    

(请注意,我使用的颜色没有 alpha。这是与我的深蓝色半透明状态栏完全匹配的 RGB 颜色)。

于 2015-05-21T15:04:08.620 回答
1

浏览Translucent 系统栏上的 Android 文档

如果您正在创建自定义主题,请将这些主题之一(Theme.Holo.NoActionBar.TranslucentDecor 和 Theme.Holo.Light.NoActionBar.TranslucentDecor)设置为父主题或在您的主题中包含 windowTranslucentNavigation 和 windowTranslucentStatus 样式属性。

因此,如果您使用支持库,则您的样式必须扩展一些 AppCompat 样式并在 ActionBarStyle 上同时使用 windowTranslucentNavigation 和 windowTranslucentStatus。

于 2015-05-18T16:30:35.477 回答
0

问题可能就在这里。

<item name="android:background">@color/myapp_green</item>
<item name="background">@color/myapp_green</item>

“android:background”是正确的语法,我不认为“background”是。为什么你需要两次指定背景颜色呢?删除第二个,它应该没问题。如果没有,那么我希望我下面的 2 美分对setBackgroundColor方法的主题有所帮助。

0.02 美元

根据我的经验,我发现setBackgroundColor()Android 中的方法有时会表现得很奇怪。发生这种情况时,我setBackground()改用,并以可绘制的形式指定颜色:getResources().getDrawable(R.color.xxxx). 我从来不知道为什么这解决了我过去遇到的类似问题,但到目前为止我已经使用了这个解决方法,没有任何明显的副作用。它可能不是最好的解决方案(特别是如果您已经设置了背景可绘制对象),但是当其他方法似乎不起作用时它会派上用场。

于 2015-05-18T18:58:20.483 回答
0

我遇到了同样的问题,我通过以下方式解决了

问题在于为所有视图覆盖相同的颜色。但我无法继续R.color.mycolorColor.parseColor("#412848")救了我。

于 2015-05-21T12:55:03.013 回答
0
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        super.onPrepareActionMode(mode, menu);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window w = getActivity().getWindow();
            w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        return true;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        super.onDestroyActionMode(mode);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window w = getActivity().getWindow();
            w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }
于 2016-04-13T20:03:30.907 回答
0

虽然我之前使用了之前的解决方案,但添加导航抽屉时存在更多问题,如果您希望抽屉在系统栏后面绘制,以及与设置和清除窗口标志或状态栏颜色相关的许多故障(甚至官方Gmail 应用程序有这些故障!)

这是我现在使用的解决方案:

@Override
public void onSupportActionModeStarted(@NonNull ActionMode mode) {
    super.onSupportActionModeStarted(mode);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Post this so it ideally happens after the window decor callbacks.
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                try {
                    final AppCompatDelegate delegate = getDelegate();
                    final Class clazz = Class.forName("android.support.v7.app.AppCompatDelegateImplV7");
                    final Field mStatusGuardField = clazz.getDeclaredField("mStatusGuard");
                    mStatusGuardField.setAccessible(true);
                    final View mStatusGuard = (View) mStatusGuardField.get(delegate);
                    if (mStatusGuard != null) {
                        mStatusGuard.setBackgroundColor(/* Should match your desired status bar color when the action mode is showing, or alternatively you could use Color.TRANSPARENT here and just update the colors through getWindow().setStatusBarColor() below. The key is to avoid the black status guard from appearing. */);
                    }
                } catch (Exception e) {
                    /* Log or handle as desired. */
                }
            }
        });
        // Also set the status bar color. This is to avoid a momentary frame or 
        // two where the black will still be visible.
       getWindow().setStatusBarColor(/* Should match your desired status bar color when the action mode is showing. */);
    }
}

@Override
public void onSupportActionModeFinished(@NonNull ActionMode mode) {
    super.onSupportActionModeFinished(mode);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Reset to the theme's colorPrimaryDark. This would normally result in 
        // the black status guard being seen momentarily, but because we set it 
        // to the same color as the action mode background, this intermediate 
        // state is not visible.
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }
}

这显然是一个大技巧,它的动画效果并不像上下文操作栏那样,但它对我有用。支持https://stackoverflow.com/a/38702953/1317564提出这个想法。

请注意,仅当您使用导航抽屉或在 values-v21/theme.xml 中设置了这些属性时才需要这样做:

<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>

否则,只调用and可能会起作用setStatusBarColor(...),这就是我在添加导航抽屉之前所做的。onSupportActionModeStarted()onSupportActionModeFinished()

于 2016-08-25T21:03:55.580 回答