使用ActionMode时,状态栏在Lollipop

时间:2015-05-12 01:08:39

标签: android statusbar android-actionmode

我的主题设置中有以下状态栏:

<!-- 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,而不是默认的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()才能生效。只需更改colorPrimaryDark中的values-v21/styles.xml即可对状态栏产生任何影响。我觉得这可能是问题的根源......这些类正在从非Material主题转换为一个新的Material类主题,所以我想知道当我进行转换时是否遗漏了一些东西正确识别colorPrimaryDark

9 个答案:

答案 0 :(得分:5)

我在这里发布了一个答案,因为虽然其他解决方案很有帮助,但他们都没有完全回答这个问题。我能够发现拨打DrawerLayout.setStatusBarBackgroundColor()的电话造成了麻烦。我的解决方案如下:

  1. 启用windowTranslucentStatus和 主题中的windowDrawsSystemBarBackgrounds
  2. 移除对DrawerLayout.setStatusBarBackgroundColor()NavigationDrawerActivity内部Activity所有values-v21/styles.xml个类的调用。
  3. 在我的NavigationDrawerActivity基本主题中设置以下样式:
  4.         @color/app_green
            @color/app_green
            @color/app_green_dark
            @color/app_green_dark
            ?attr/colorPrimaryDark
            ?attr/colorPrimaryDark
    
    1. onCreate()类的内部,在getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);方法中,我执行以下操作(感谢@Tinadm获取此部分答案):ActionMode

    2. 在我的班级里面展示了ActionMode,我添加了以下方法:

    3.     @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启用时(通过从左向右滑动)拉出导航抽屉,它将覆盖导航抽屉。我将在启用short a = 1, b = 1; auto res = a + b; // decltype(res) == int 时禁用此功能,因此不会混淆用户。

答案 1 :(得分: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被销毁时,状态栏将会恢复,因此也要覆盖它并重新设置statusBar颜色。

希望有所帮助

答案 2 :(得分:3)

前一段时间我遇到了同样的问题,我的解决方案是在onCreateActionMode&amp; onDestroyActionMode设置并恢复状态栏颜色。

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

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

当然你需要在使用setStatusBarColor方法之前检查它在lolipop上是不可用的前L。

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

mDrawerLayout.setStatusBarBackground(new ColorDrawable(color));

希望有所帮助!

答案 3 :(得分: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提供的相同trasparency。由于我的工具栏是蓝色的,我的状态栏现在是深蓝色。
  4. 仅这一点并没有解决问题。当操作模式处于活动状态时,状态栏在我的情况下仍为黑色。但是现在,不使用translucentStatusBar,我可以将颜色应用到我的状态栏,所以我使用了:

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        getActivity().getWindow().setStatusBarColor(Color.parseColor("#026592"));
        return true;
    }
    
  5. (请注意,我使用的颜色没有alpha。这是RGB颜色与我的深蓝色半透明状态栏完全匹配)。

答案 4 :(得分:1)

浏览Translucent system bars

上的Android文档
  

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

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

答案 5 :(得分:0)

问题可能在这里。

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

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

<强> $ 0.02

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

答案 6 :(得分:0)

我遇到了同样的问题,我按照以下方式解决了这个问题

问题在于为所有视图覆盖相同的颜色。但我无法使用R.color.mycolorColor.parseColor("#412848")保存我。

答案 7 :(得分:0)

sed

答案 8 :(得分: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

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

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

否则,只需拨打setStatusBarColor(...)onSupportActionModeStarted()中的onSupportActionModeFinished()即可,这就是我在添加导航抽屉之前所做的事情。