0

My app has two activities with a DrawerLayout and a NavigationView (android.support.design.widget.NavigationView).

I use an app theme with parent Theme.AppCompat.Light.DarkActionBar, and for my Lollipop emulator, there are no problems.

But for my Jellybean device and emulator, I have a very strange behaviour:

The background of the NavigationView items is sometimes rendered as light grey (which would be ok for me), sometimes as a light blue shade (I think it's the Theme Holo default).

For my "real" app, the grey tint would always appear on the first drawer and the blue tint always on the other one. At first I thought the difference may be due to some detail like the first uses an ActionBarDrawerToggle and the other one doesn't, so maybe I had to somehow set the android:listSelector for the second one only.

So I created a MCVE-like sample and noticed that both drawers show both tints now and then. Is this a bug or am I missing something?

What can I do to get rid of Theme Holo?

Screenshots:

main drawer blue and grey tint

By the way, I have the same random behaviour for the ActionButton background:

MainActivity blue and grey action button

My code (~ 95% of it for those who like to run their own tests):

activity_main.xml just a 'Blank Activity' with a big "Hello World" at the center:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true">

    <TextView
        android:text="@string/hello_world"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

activity_main_decor.xml

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    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">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"/>

    <android.support.design.widget.NavigationView
        android:id="@+id/main_drawer"
        android:layout_width="@dimen/nav_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/menu_main_drawer"/>
</android.support.v4.widget.DrawerLayout>

activity_other.xml

<merge>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    <TextView
        android:text="Hello again!"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>
</merge>

activity_other_decor.xml

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout android:id="@+id/container"
             android:orientation="vertical"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent">
        <include layout="@layout/activity_other"/>
    </FrameLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/other_drawer"
        android:layout_width="@dimen/nav_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="end"
        app:menu="@menu/menu_other_drawer" />

</android.support.v4.widget.DrawerLayout>

MainActivity - the navigation drawer has to appear from the left and above the ActionBar( BTW thanks to @Peter Cai for some very useful lines )

public class MainActivity extends AppCompatActivity
{
private static final String             TAG = "StyleForDrawers main";
private static final String             DRAWER_OPEN = "main_drawer_open";

private DrawerLayout                    drawerLayout;
private NavigationView                  navigationView;
private ActionBarDrawerToggle           drawerToggle;
private boolean                         isDrawerOpen;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initNavDrawer();
}

private void initNavDrawer()
{
    final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(this);

    LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    drawerLayout = (DrawerLayout) inflater.inflate(R.layout.activity_main_decor, null);

    ViewGroup decor = (ViewGroup)activity.getWindow().getDecorView();
    View child = decor.getChildAt(0);
    decor.removeView(child);

    FrameLayout container = (FrameLayout)drawerLayout.findViewById(R.id.container);
    container.addView(child);
    decor.addView(drawerLayout);

    navigationView = (NavigationView) findViewById(R.id.main_drawer);
    navigationView.setItemTextColor(ContextCompat.getColorStateList(this, R.color.nav_item));
    navigationView.setItemTextAppearance(R.style.MyNavdrawerTextAppearance);
    View headerView = navigationView.inflateHeaderView(R.layout.navdrawer_list_header);

    // header View should appear below status bar, so set padding:
    (headerView.findViewById(R.id.nav_header_root)).setPadding(0, getStatusbarHeight(), 0, (int) getResources().getDimension(R.dimen.activity_vertical_margin));

    navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener()
    {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem)
        {
            Intent intent = null;
            int id = menuItem.getItemId();

            if (id == R.id.action_other_activity)
            {
                intent = new Intent(MainActivity.this, OtherActivity.class);
            }
            // -- skipped some code for Help etc. --

            drawerLayout.closeDrawer(navigationView);
            isDrawerOpen = false;

            if (intent != null)
            {
                intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                startActivity(intent);
            }

            return true;
        }
    });

    drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.sDrawerOpen, R.string.sDrawerClose)
    {

        @Override
        public void onDrawerClosed(View drawerView)
        {
            super.onDrawerClosed(drawerView);
            sPrefs.edit().putBoolean(DRAWER_OPEN, false).apply();
        }

        @Override
        public void onDrawerOpened(View drawerView)
        {
            isDrawerOpen = true;
            super.onDrawerOpened(drawerView);
            sPrefs.edit().putBoolean(DRAWER_OPEN, true).apply();
        }
    };

    drawerLayout.setDrawerListener(drawerToggle);

    if (getSupportActionBar() != null)
    {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setHomeAsUpIndicator(R.drawable.nav_drawer);
    }

    if (sPrefs.getBoolean(DRAWER_OPEN, true)) // open at first launch
    {
        drawerLayout.openDrawer(navigationView);
    }
    else
    {
        drawerLayout.closeDrawer(navigationView);
    }
}

@Override
protected void onPause()
{
    super.onPause();
    SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    sPrefs.edit().putBoolean(DRAWER_OPEN, isDrawerOpen).apply();
}

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
    return drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
}

public static int getStatusbarHeight()
{
    int retValue = 0;
    int resID = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resID > 0) {
        retValue = getResources().getDimensionPixelSize(resID);
    }
    return retValue;
}

}

OtherActivity - the drawer is like a context menu and appears from the right without overlapping the ActionBar:

public class OtherActivity extends AppCompatActivity
{
    private static final String             TAG = "StyleForDrawers other";
    private static final String             DRAWER_OPEN = "other_drawer_open";

    private DrawerLayout                    mDrawerLayout;
    private NavigationView                  mNavigationView;
    private boolean                         mIsDrawerOpen;


@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_other_decor);
    initMenuDrawer();
}

private void initMenuDrawer()
{
    final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(this);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

    mNavigationView = (NavigationView) findViewById(R.id.other_drawer);
    mNavigationView.setItemTextColor(ContextCompat.getColorStateList(this, R.color.nav_item));
    mNavigationView.setItemTextAppearance(R.style.MyNavdrawerTextAppearance);

    mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener()
    {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem)
        {
            int id = menuItem.getItemId();

            if (id == R.id.action_back_to_main)
            {
                finish();
            }
            // -- skipped some code for doing something (else) --
            mDrawerLayout.closeDrawer(mNavigationView);
            mIsDrawerOpen = false;
            return true;
        }
    });

    mDrawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener()
    {
        @Override
        public void onDrawerOpened(View drawerView)
        {
            super.onDrawerOpened(drawerView);
            mIsDrawerOpen = true;

            sPrefs.edit().putBoolean(DRAWER_OPEN, true).apply();
        }

        @Override
        public void onDrawerClosed(View drawerView)
        {
            super.onDrawerClosed(drawerView);
            mIsDrawerOpen = false;
            sPrefs.edit().putBoolean(DRAWER_OPEN, false).apply();
        }

    });

    if (sPrefs.getBoolean(DRAWER_OPEN, false)) // don't open at first launch
    {
        mDrawerLayout.openDrawer(mNavigationView);
    }
    else
    {
        mDrawerLayout.closeDrawer(mNavigationView);
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.menu_other, menu);
    return true;
}

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

    if (id == R.id.action_opendrawer)
    {
        if (mIsDrawerOpen)
        {
            mDrawerLayout.closeDrawer(mNavigationView);
        }
        else
        {
            mDrawerLayout.openDrawer(mNavigationView);
        }
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

menu_main.xml unnecessary

menu_other.xml contains an action button to open/close the drawer:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <item android:id="@+id/action_opendrawer"
      android:title="open/close drawer"
      android:icon="@drawable/nav_drawer"
      android:orderInCategory="100"
      app:showAsAction="always"/>
</menu>

styles.xml

<resources>
    <color name="accent_orange">#ff6d00</color>
    <color name="my_background">#eeeeee</color>
    <color name="my_text">#311b92</color>
    <color name="my_sec_text">#B1311b92</color>
    <color name="my_actionbar">#1a237e</color>
    <color name="my_statusbar">#1a237e</color>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

    <item name="colorPrimary">@color/my_actionbar</item>
    <item name="colorPrimaryDark">@color/my_statusbar</item>
    <item name="colorAccent">#ffc400</item>
    <item name="android:windowBackground">@color/my_background</item>
    <item name="android:textColorPrimary">@color/my_text</item>
    <item name="android:textColor">@color/my_text</item>
    <item name="android:textColorSecondary">@color/my_sec_text</item>
</style>

<style name="MyNavdrawerTextAppearance" parent="TextAppearance.AppCompat.Small">
    <item name="android:textStyle">bold</item>
</style>
</resources>

UPDATE (May 2016)

Meanwhile I've also tried setting my own background for the NavigationView items. The background is changed but on touching I still get a ripple for Lollipop and higher (which is ok) and unfortunately still the alternating effect on Jellybean, probably from some kind of list selector which I don't know how to manipulate.

UPDATE 2 (September 2016)

I installed a new version of Android Studio, am using the newest versions of build tools and all the libraries (24.0.2 respectively 24.2.0) and even ditched the ActionBar in favour of Toolbar. The last means I won't have to use the hack in Main to get the nav drawer to cover the ActionBar, so I'm not using a LayoutInflater in my code any more.

But still no luck :(

I'm pretty sure that this is a bug: the same code behaving differently from one app run to the next and the inconsistency between activities Main and Other during one and the same run.

Community
  • 1
  • 1
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
  • "both drawers show both tints now and then" -- is the tint consistent per app run? IOW, if you run the app, and it starts showing the blue tint, will it stay with the blue tint consistently until some major event (e.g., fresh activity instance, fresh process)? Or will some taps result in blue and others in grey? If the former, perhaps those widgets are not being wrapped in their `appcompat-v7` replacements. I don't know why that would happen (I rarely use `appcompat-v7`), but it's the best technical explanation I can think of for your symptoms. – CommonsWare May 11 '16 at 10:19
  • @CommonsWare - "fresh activity instance" seems to be sufficient: as long as I stay with one activity, the colors don't change. But sometimes it is sufficient to press BACK from MainActivity after launching and to launch it immediately afterwards to get a new tint. Or the colors change if one jumps between the two activities (including BACK from OtherActivity) . Sometimes the ActionButton in OtherActivity has the same blue tint as the drawer item, sometimes it's just the drawer. FWIW I've also played the monkey on an IceCreamSandwich emulator but there only seems to be a Jellybean problem. – Bö macht Blau May 11 '16 at 11:09
  • @CommonsWare (cont.) Is there anything I can do in my code to improve the situation? – Bö macht Blau May 11 '16 at 11:10
  • Beats me. This sort of squirrelly behavior is one of the reasons I avoid `appcompat-v7`. You might fire up Hierarchy View and see if it indicates which widgets are AppCompat-ized or not. Or, walk the view hierarchy in your code and dump the classes in the hierarchy to LogCat. If my theory holds up -- the tint change is because some widgets are not getting wrapped -- then we have something more concrete to work on. – CommonsWare May 11 '16 at 12:43
  • 1
    Though one thing you can do to help your cause is to **always** get your `LayoutInflater` via `getLayoutInflater()` on your activity or fragment. Using anything else, such as your use of `getSystemService()`, is a problem. I cannot explain the random aspect of your results, but this is a code smell that is in the vicinity of your issue. – CommonsWare May 11 '16 at 12:46
  • @Commonsware - ok, I found Hierarchy Viewer. The only objects with an `AppCompat`prefix are the ones inside my own layout. So I'd say none of the ActionBar or NavigationView widgets are getting wrapped. (I changed the line with the inflater just to be on the safe side but I have a feeling that it's not the primary source of the problem because this line only appears in MainActivity and in my "real" app the Holo tint is only visible in the other activity.) – Bö macht Blau May 11 '16 at 15:52
  • Unfortunately, I don't know what to expect for `NavigationView`. The action bar items wouldn't be "wrapped" so much as be actual ones from `appcompat-v7`. And your `ActionBarDrawerToggle` should already be the one from `appcompat-v7`. Unfortunately, I do not have much in the way of further suggestions for you. – CommonsWare May 11 '16 at 15:55
  • @Commonsware - thanks anyway for your time - and for pointing me to Hierarchy Viewer (I never even knew it existed). I think I'll try to set the NavigationView item background expressly for pre-Lollipop. Even if I'm not able to clone the original, I'll get close enough. – Bö macht Blau May 11 '16 at 16:41

0 Answers0