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:
By the way, I have the same random behaviour for the ActionButton
background:
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.