my application provide different contents, some of them is with image and others not.
For those which have image i am using CollapsingToolbarLayout it works fine but for those which have not image I want collapse CollapsingToolbarLayout and prevent expanding, disable it.
Here is my item activity code:
public class ItemActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item);
collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setStatusBarScrimResource(R.color.black_transparent_190);
collapsingToolbar.setContentScrimResource(R.color.gray_transparent_150);
collapsingToolbar.setExpandedTitleTextAppearance(R.style.MyTheme_Toolbar_Title_Expanded);
appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
appBarLayout.setExpanded(false);
loadData();
}
public void loadData() {
if(haveImage) {
// loading data and image
appBarLayout.setExpanded(true, true);
} else {
appBarLayout.setExpanded(false);
}
}
}
I have already tried
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams();
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
collapsingToolbar.setLayoutParams(params);
and
CollapsingToolbarLayout.LayoutParams mParams = new CollapsingToolbarLayout.LayoutParams(collapsingToolbar.getLayoutParams());
mParams.setCollapseMode(CollapsingToolbarLayout.LayoutParams.COLLAPSE_MODE_OFF);
mToolbar.setLayoutParams(mParams);
It works, it's prevent collapsing!
But collapsing toolbar is expanded!
How can I collapse toolbar and disable it for scrolling events?
You use setExpanded(false) on appBarLayout as normal, but you need to create a custom CollapsingToolbarLayout as below
I followed the idea in this link.
Override CollapsingToolbarLayout and override dispatchTouchEvent returning true like this
public class MyCustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
public MyCustomCollapsingToolbarLayout(Context context) {
super(context);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return true;
}
}
Related
I am trying to create a collapsible toolbar like the one on chesesquare repository, but I am facing this problem:
https://www.youtube.com/watch?v=THdxcyEc1CA&feature=youtu.be
Anyone know how to solve this problem?
You can check the state of AppBarLayout on configuration change(orientation), store it. Then you can set that state for the AppBarLayout after configuration change applied.
public class CheeseDetailActivity extends AppCompatActivity {
public static final String EXTRA_NAME = "cheese_name";
AppBarLayout appBarLayout;
boolean isCollapsed = false;
//state change listener
AppBarLayout.OnOffsetChangedListener toolbarStateListener = new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (verticalOffset == 0) {
// Collapsed
isCollapsed = true;
} else {
// Not collapsed
isCollapsed = false;
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Intent intent = getIntent();
final String cheeseName = intent.getStringExtra(EXTRA_NAME);
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle(cheeseName);
//getting app bar layout
appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
//set listener to listen state change of app bar layout
appBarLayout.addOnOffsetChangedListener(toolbarStateListener);
loadBackdrop();
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//save state on orientation change
savedInstanceState.putBoolean("isCollapsed", isCollapsed);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//get state on orientation change
isCollapsed = savedInstanceState.getBoolean("isCollapsed");
}
#Override
protected void onResume() {
super.onResume();
//set state of app bar layout
appBarLayout.setExpanded(isCollapsed);
}
private void loadBackdrop() {
final ImageView imageView = (ImageView) findViewById(R.id.backdrop);
Glide.with(this).load(Cheeses.getRandomCheeseDrawable()).centerCrop().into(imageView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.sample_actions, menu);
return true;
}
}
I have edited CheeseDetailActivity in CheeseSquare repository.
On the onConfigurationChanged you could try to run
handler.postDelayed(new Runnable(){
#Overrride
public void run(){
toolbar.invalidate();
}
},[Try different time lapses(miliseconds)]);
I had gone through How to add toolbars to AppCompatPreferenceActivity?
I'm using appcompat-v7:23.0.1 and support-v4:23.0.1
Before using AppCompayPreferenceActivity, my PreferenceActivity looks the following without toolbar.
Without Toolbar
public class JStockPreferenceActivity extends PreferenceActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// Display the fragment as the main content.
JStockPreferenceFragment jStockPreferenceFragment = JStockPreferenceFragment.newInstance();
jStockPreferenceFragment.setArguments(this.getIntent().getExtras());
this.getFragmentManager().beginTransaction().replace(android.R.id.content, jStockPreferenceFragment).commit();
}
}
After using AppCompayPreferenceActivity, with some Toolbar initialization code, the outcome looks as the following
The outcome isn't as expected :-
The Toolbar overlaps with PreferenceFragment
When touching on Toolbar, the event will be consumed by PreferenceFragment. This means, when you touch on toolbar, "Table header" preference will be touched instead.
With Toolbar
public class JStockPreferenceActivity extends AppCompatPreferenceActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// Display the fragment as the main content.
JStockPreferenceFragment jStockPreferenceFragment = JStockPreferenceFragment.newInstance();
jStockPreferenceFragment.setArguments(this.getIntent().getExtras());
this.getFragmentManager().beginTransaction().replace(android.R.id.content, jStockPreferenceFragment).commit();
}
initToolbar();
}
private void initToolbar() {
Toolbar toolbar;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewGroup root = (ViewGroup) findViewById(android.R.id.list).getParent().getParent().getParent();
toolbar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.toolbar, root, false);
root.addView(toolbar, 0);
} else {
toolbar = null;
}
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
My toolbar is
toolbar.xml
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" >
<!-- android:elevation="4dp" is used due to http://www.google.com/design/spec/what-is-material/elevation-shadows.html#elevation-shadows-elevation-android- -->
</android.support.v7.widget.Toolbar>
I was wondering, what I had did wrong, which causes Toolbar overlapped with PreferenceFragment
After some research and getting advice from Ian Lake, I manage to make it work by
Change from AppCompatPreferenceActivity, to AppCompatActivity+PreferenceFragmentCompat
The following solution is workable for API 10 and above.
JStockPreferenceActivity.java
public class JStockPreferenceActivity extends AppCompatActivity {
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.jstock_preference_activity);
initToolbar();
if (savedInstanceState == null) {
// Display the fragment as the main content.
JStockPreferenceFragment jStockPreferenceFragment = JStockPreferenceFragment.newInstance();
jStockPreferenceFragment.setArguments(this.getIntent().getExtras());
this.getSupportFragmentManager().beginTransaction().replace(R.id.content, jStockPreferenceFragment).commit();
}
}
JStockPreferenceFragment.java
public class JStockPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener, PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
public static JStockPreferenceFragment newInstance() {
return new JStockPreferenceFragment();
}
However, by doing so, there is a shortcoming. Your previous custom DialogPreference no longer work. To solve this, you need to
Replace DialogPreference with PreferenceDialogFragmentCompat.
Replace DialogPreference in XML with ListPreference. (I'm not exactly sure whether ListPreference as replacement is a right way. It works anyway)
Manually show PreferenceDialogFragmentCompat, in PreferenceFragmentCompat's onPreferenceDisplayDialog.
JStockPreferenceFragment.java
public class JStockPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener, PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
public static JStockPreferenceFragment newInstance() {
return new JStockPreferenceFragment();
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Showing custom dialog preference.
private void showPreferenceDialogFragmentCompat(PreferenceDialogFragmentCompat preferenceDialogFragmentCompat) {
preferenceDialogFragmentCompat.setTargetFragment(this, 0);
preferenceDialogFragmentCompat.show(this.getFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
}
private void showCurrencySymbolPreferenceDialogFragmentCompat(Preference preference) {
CurrencySymbolPreferenceDialogFragmentCompat currencySymbolPreferenceDialogFragmentCompat = CurrencySymbolPreferenceDialogFragmentCompat.newInstance(preference.getKey());
showPreferenceDialogFragmentCompat(currencySymbolPreferenceDialogFragmentCompat);
}
// Showing custom dialog preference.
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// Callback when we are using custom dialog preference.
#Override
public Fragment getCallbackFragment() {
return this;
}
#Override
public boolean onPreferenceDisplayDialog(PreferenceFragmentCompat preferenceFragmentCompat, Preference preference) {
final String key = preference.getKey();
if (_CURRENCY_SYMBOL_OPTIONS.equals(key)) {
showCurrencySymbolPreferenceDialogFragmentCompat(preference);
return true;
}
return false;
}
// Callback when we are using custom dialog preference.
////////////////////////////////////////////////////////////////////////////////////////////////
CurrencySymbolPreferenceDialogFragmentCompat.java
public class CurrencySymbolPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
public CurrencySymbolPreferenceDialogFragmentCompat() {
}
public static CurrencySymbolPreferenceDialogFragmentCompat newInstance(String key) {
CurrencySymbolPreferenceDialogFragmentCompat fragment = new CurrencySymbolPreferenceDialogFragmentCompat();
Bundle b = new Bundle(1);
b.putString("key", key);
fragment.setArguments(b);
return fragment;
}
#Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
...
this.getPreference().setSummary(getSummary());
...
}
}
}
preferences.xml
<android.support.v7.preference.ListPreference
android:title="#string/preference_currency_symbol_title"
android:key="_CURRENCY_SYMBOL_OPTIONS" />
Please note that, you need to add the following item in your theme.
<item name="preferenceTheme">#style/PreferenceThemeOverlay</item>
Some bugs regarding theme
However, the theme-ing isn't perfect yet - PreferenceFragmentCompat requires preferenceTheme to be set
This is a known issues : https://code.google.com/p/android/issues/detail?id=183376
There is proposed workaround https://stackoverflow.com/a/32108439/72437 But, the workaround will only work for v14 and above, not v7 and above.
My app has a toolbar that should be present on every view. Currently, I do the following in my onCreate() method for each Activity I have:
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Does this need to be done in every onCreate() method in every Activity or is there a simpler way? Also, as a side question, how can I implement a "back" feature in the toolbar that takes the user back one action if they click it?
Create a Base class for Activity
public abstract class BaseActivity extends AppCompatActivity {
Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResource());
configureToolbar();
}
protected abstract int getLayoutResource();
private void configureToolbar() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fm = getSupportFragmentManager();
if (fm != null && fm.getBackStackEntryCount() > 0) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
finish();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
And in each Activity extends this BaseActivity to get the ToolBar and implementing the back feature.
At last don't forget to include the ToolBar in each activity layout.
Edit:
Override that method getLayoutResource() in each Activity and pass the layout id.
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public int getLayoutResource() {
return R.layout.activity_main;
}
This is my implementation. It removes the need of the getLayoutResources() from the accepted answer and brings back the "setContentView()" in all activities as normal
public abstract class BaseActivity extends AppCompatActivity {
Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
protected boolean useToolbar() {
return true;
}
#Override
public void setContentView(int layoutResID) {
View view = getLayoutInflater().inflate(layoutResID, null);
configureToolbar(view);
super.setContentView(view);
}
private void configureToolbar(View view) {
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
if (toolbar != null) {
if (useToolbar()) {
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
toolbar.setVisibility(View.GONE);
}
}
}
}
From here on you just extend BaseActivity. If you don't want a toolbar you will have to override the useToolbar().
Don't forget to add in activity.xml at the top
<include layout="#layout/toolbar" />
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<merge 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="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
</merge>
It depends on your implementation but if you want avoid boilerplate code you should use good programming OO.
An Example using Fragment.
public abstract class FragmentBase extends Fragment {
protected void settingsToolbar(View rootView) {
Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar);
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
// TODO add your code and your requirements
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
}
I hope this can give you an idea.
If you have used Activity then Create BaseActivity that extends AppCompatActivity or ActionBarActivity(Deprecated) and move Toolbar code to BaseActivity.
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
}
If you have used Fragment then Create BaseFragment that extends Fragment and move Toolbar code to BaseFragment.
public class BaseFragment extends Fragment {
View main;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
main = inflater.inflate(R.layout.fragment_about, container, false);
Toolbar toolbar = (Toolbar) main.findViewById(R.id.toolbar);
getActivity().setSupportActionBar(toolbar);
return main;
}
}
In main XML layout you have to add Toolbar xml code.
Now in every view(Activity) extends BaseActivity instead of AppCompatActivity or ActionBarActivity so you can get access Toolbar in every view.
public class YourActivity extends BaseActivity{
//your code
}
EDIT1:
main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:theme="#style/toolbarTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/green"
android:minHeight="?attr/actionBarSize" />
</RelativeLayout>
EDIT2:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
add these two lines below setSupportActionBar(toolbar); in BaseActivity.
I hope it helps!
Create a base activity and initialize your tool bar in this class. Now it can be extends to all other child activity.
FirtActivity extends BaseActivity
SecondActivity extends BaseActivity
In base activity toll bar back button click you can check like below mentioned way
if(this instance of FirstActivity){
//do stuff here
}else if(this instance of SecondActivity){
//do stuff here
}
I have Toolbar and ScrollView in my layout. I wanna Hide/Show Toolbar when user scroll ScrollView. There was the same questions before here in stackoverflow but I didnt find right answer. Here below code which I used. Unfortunatly it works only with ActionBar. Is it any was to use this with Toolbar cause now I have errors in isShowing, hide, show methods?
Thanks for any help!
public class MainActivity extends ActionBarActivity implements ViewTreeObserver.OnScrollChangedListener{
private float mToolbarBarHeight;
Toolbar mToolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
mToolbar = (Toolbar) findViewById(R.id.application_toolbar);
setSupportActionBar(mToolbar);
//hiding the toolbar
final TypedArray mTypedArray = getTheme().obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
mToolbarBarHeight = mTypedArray.getDimension(0, 0);
mTypedArray.recycle();
((ScrollView)findViewById(R.id.mScrollView)).getViewTreeObserver().addOnScrollChangedListener(this);
}
****
#Override
public void onScrollChanged() {
float y = ((ScrollView)findViewById(R.id.mScrollView)).getScrollY();
if (y >= mToolbarBarHeight && mToolbar.isShowing()) {
mToolbar.hide();
} else if ( y==0 && !mToolbar.isShowing()) {
mToolbar.show();
}
}
}
My requirement is shown in the picture below My navigation drawer should be opened from the right side. I have implemented this. My navigation drawer open from right to left. But the problem is toggle icon is always on the left side. How can I set toggle icon to the right?
I have checked the following SO questions, but none of them came to any help:
Change toggle button image Icon In Navigation Drawer right to left
Drawer Toggle in right Drawer
enter link description here
Here is what I have tried:
code for my layout activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="end">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.nav.MainActivity"
android:foregroundGravity="right">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_gravity="right"
app:popupTheme="#style/AppTheme.PopupOverlay"
android:foregroundGravity="right"
android:textAlignment="viewEnd"
android:touchscreenBlocksFocus="false" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
android:id="#+id/navigation_view"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="end"
app:headerLayout="#layout/nav_header"
app:menu="#menu/menu_navigation"
android:textAlignment="viewEnd" />
</android.support.v4.widget.DrawerLayout>
Code for my activity
public class MainActivity extends AppCompatActivity {
private DrawerLayout drawerLayout;
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initNavigationDrawer();
}
#TargetApi(Build.VERSION_CODES.M)
public void initNavigationDrawer() {
NavigationView navigationView = (NavigationView)findViewById(R.id.navigation_view);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
int id = menuItem.getItemId();
switch (id){
case R.id.home:
Toast.makeText(getApplicationContext(),"Home",Toast.LENGTH_SHORT).show();
drawerLayout.closeDrawers();
break;
case R.id.settings:
Toast.makeText(getApplicationContext(),"Settings",Toast.LENGTH_SHORT).show();
break;
case R.id.trash:
Toast.makeText(getApplicationContext(),"Trash",Toast.LENGTH_SHORT).show();
drawerLayout.closeDrawers();
break;
case R.id.logout:
finish();
}
return true;
}
});
drawerLayout = (DrawerLayout)findViewById(R.id.drawer);
ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.drawer_open,R.string.drawer_close){
#Override
public void onDrawerClosed(View v){
super.onDrawerClosed(v);
}
#Override
public void onDrawerOpened(View v) {
super.onDrawerOpened(v);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item != null && item.getItemId() == android.R.id.home) {
if (drawerLayout.isDrawerOpen(Gravity.RIGHT)) {
drawerLayout.closeDrawer(Gravity.RIGHT);
}
else {
drawerLayout.openDrawer(Gravity.RIGHT);
}
}
return false;
}
};
drawerLayout.addDrawerListener(actionBarDrawerToggle);
actionBarDrawerToggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (drawerLayout.isDrawerOpen(Gravity.RIGHT)) {
drawerLayout.closeDrawer(Gravity.RIGHT);
} else {
drawerLayout.openDrawer(Gravity.RIGHT);
}
}
});
}
}
There's really no (practical) way to make ActionBarDrawerToggle do that, as it is always set on the start/left-side navigation button. However, that class is basically just a DrawerListener that manages a specialized Drawable, and wires an ImageButton to the DrawerLayout. We can put together something similar for an end/right-side drawer with an ImageButton that we can place on that same side in a Toolbar (which is required for this example).
public class EndDrawerToggle implements DrawerLayout.DrawerListener {
private final DrawerLayout drawerLayout;
private final AppCompatImageButton toggleButton;
private final int openDrawerContentDescRes;
private final int closeDrawerContentDescRes;
private DrawerArrowDrawable arrowDrawable;
public EndDrawerToggle(DrawerLayout drawerLayout, Toolbar toolbar,
int openDrawerContentDescRes, int closeDrawerContentDescRes) {
this.drawerLayout = drawerLayout;
this.openDrawerContentDescRes = openDrawerContentDescRes;
this.closeDrawerContentDescRes = closeDrawerContentDescRes;
toggleButton = new AppCompatImageButton(toolbar.getContext(), null,
R.attr.toolbarNavigationButtonStyle);
toolbar.addView(toggleButton, new Toolbar.LayoutParams(GravityCompat.END));
toggleButton.setOnClickListener(v -> toggle());
loadDrawerArrowDrawable();
}
public void syncState() {
if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
setPosition(1f);
} else {
setPosition(0f);
}
}
public void onConfigurationChanged(Configuration newConfig) {
loadDrawerArrowDrawable();
syncState();
}
#Override
public void onDrawerSlide(#NonNull View drawerView, float slideOffset) {
setPosition(Math.min(1f, Math.max(0f, slideOffset)));
}
#Override
public void onDrawerOpened(#NonNull View drawerView) {
setPosition(1f);
}
#Override
public void onDrawerClosed(#NonNull View drawerView) {
setPosition(0f);
}
#Override
public void onDrawerStateChanged(int newState) {}
private void loadDrawerArrowDrawable() {
arrowDrawable = new DrawerArrowDrawable(toggleButton.getContext());
arrowDrawable.setDirection(DrawerArrowDrawable.ARROW_DIRECTION_END);
toggleButton.setImageDrawable(arrowDrawable);
}
private void toggle() {
final int drawerLockMode = drawerLayout.getDrawerLockMode(GravityCompat.END);
if (drawerLayout.isDrawerVisible(GravityCompat.END)
&& (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {
drawerLayout.closeDrawer(GravityCompat.END);
} else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
drawerLayout.openDrawer(GravityCompat.END);
}
}
private void setPosition(float position) {
if (position == 1f) {
arrowDrawable.setVerticalMirror(true);
setContentDescription(closeDrawerContentDescRes);
} else if (position == 0f) {
arrowDrawable.setVerticalMirror(false);
setContentDescription(openDrawerContentDescRes);
}
arrowDrawable.setProgress(position);
}
private void setContentDescription(int resId) {
toggleButton.setContentDescription(toggleButton.getContext().getText(resId));
}
}
The EndDrawerToggle class works exactly the same way as ActionBarDrawerToggle does when used with a Toolbar (except the constructor call doesn't need an Activity argument): first instantiate the toggle, then add it as a DrawerListener, and sync it in the Activity's onPostCreate() method. If you're already overriding the Activity's onConfigurationChanged() method, you'll want to call the toggle's corresponding method there, like you would for an ActionBarDrawerToggle.
private EndDrawerToggle drawerToggle;
public void initNavigationDrawer() {
...
drawerLayout = (DrawerLayout) findViewById(R.id.drawer);
drawerToggle = new EndDrawerToggle(drawerLayout,
toolbar,
R.string.drawer_open,
R.string.drawer_close);
drawerLayout.addDrawerListener(drawerToggle);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
If you have two drawers and need to use ActionBarDrawerToggle and EndDrawerToggle simultaneously, it is possible, but we'll need to handle intercepting and dispatching drawer motion events to the correct toggle.
If you prefer fewer classes, you could subclass ActionBarDrawerToggle and merge EndDrawerToggle's functionality into it, dispatching each DrawerListener method call either to the super class, or to the local end toggle code.
However, composition is arguably much cleaner here, and it will let us use EndDrawerToggle as is. This example is a DrawerListener that relays syncState() and onConfigurationChanged() calls to each toggle, but dispatches the listener method calls only to the appropriate one, depending on which drawer is moving.
public class DualDrawerToggle implements DrawerLayout.DrawerListener {
private final DrawerLayout drawerLayout;
private final Toolbar toolbar;
private final ActionBarDrawerToggle actionBarDrawerToggle;
private final EndDrawerToggle endDrawerToggle;
public DualDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar,
int startDrawerOpenContDescRes, int startDrawerCloseContDescRes,
int endDrawerOpenContDescRes, int endDrawerCloseContDescRes) {
this.drawerLayout = drawerLayout;
this.toolbar = toolbar;
this.actionBarDrawerToggle =
new ActionBarDrawerToggle(activity, drawerLayout, toolbar,
startDrawerOpenContDescRes, startDrawerCloseContDescRes);
this.endDrawerToggle =
new EndDrawerToggle(drawerLayout, toolbar,
endDrawerOpenContDescRes, endDrawerCloseContDescRes);
}
public void syncState() {
actionBarDrawerToggle.syncState();
endDrawerToggle.syncState();
}
public void onConfigurationChanged(Configuration newConfig) {
actionBarDrawerToggle.onConfigurationChanged(newConfig);
// Fixes bug in ABDT, which only reloads the up nav indicator, for some reason.
final DrawerArrowDrawable dad = new DrawerArrowDrawable(toolbar.getContext());
actionBarDrawerToggle.setDrawerArrowDrawable(dad);
endDrawerToggle.onConfigurationChanged(newConfig);
}
#Override
public void onDrawerSlide(#NonNull View drawerView, float slideOffset) {
if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
actionBarDrawerToggle.onDrawerSlide(drawerView, slideOffset);
} else {
endDrawerToggle.onDrawerSlide(drawerView, slideOffset);
}
}
#Override
public void onDrawerOpened(#NonNull View drawerView) {
if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
actionBarDrawerToggle.onDrawerOpened(drawerView);
} else {
endDrawerToggle.onDrawerOpened(drawerView);
}
}
#Override
public void onDrawerClosed(#NonNull View drawerView) {
if (isStartDrawerView(drawerView, drawerLayout.getLayoutDirection())) {
actionBarDrawerToggle.onDrawerClosed(drawerView);
} else {
endDrawerToggle.onDrawerClosed(drawerView);
}
}
#Override
public void onDrawerStateChanged(int newState) {}
#SuppressLint("RtlHardcoded")
static boolean isStartDrawerView(View drawerView, int layoutDirection) {
final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
final int horizontalGravity = gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
if ((horizontalGravity & GravityCompat.RELATIVE_LAYOUT_DIRECTION) > 0) {
return horizontalGravity == GravityCompat.START;
} else {
if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
return horizontalGravity == Gravity.RIGHT;
} else {
return horizontalGravity == Gravity.LEFT;
}
}
}
}
Again, DualDrawerToggle works exactly the same way as ActionBarDrawerToggle does when used with a Toolbar, except for the extra content description resource IDs in the constructor.
Do note that the one DualDrawerToggle creates and manages both other toggles internally. You don't need to set up any other toggle instances in your Activity; just the DualDrawerToggle.
Notes:
If you read through the code, you'll see that EndDrawerToggle – and therefore also DualDrawerToggle – can be readily adapted to put a toggle on pretty much anything that can display a Drawable and register clicks; e.g., a FloatingActionButton, an options menu item, a TextView's compound drawable, etc. Simply replace the AppCompatImageButton with your target UI component, which can be passed in the constructor in place of the Toolbar, since the toggle won't be added to that anymore.
Additionally, this could be modified to work with the start/left-aligned drawer, too – completely replacing ActionBarDrawerToggle – so that its toggle could be placed on those various components, as well.
The AppCompatImageButton used here is a regular child of the Toolbar. If you're using a menu on the Toolbar, that menu will take precedence in the layout, and push the toggle inward. To keep it on the outside, you can modify the class as described above to set the toggle on an action menu item. I've an example of that in my answer here.
That menu item example might also be useful if your design requires you use the decor-supplied ActionBar in lieu of your own Toolbar. Though ActionBarDrawerToggle can work with a decor-supplied ActionBar, this example can not, as is.
In your android manifest add this line:
android:supportsRtl="true"
to your application, like so:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
Then in your onCreate method, add this line:
getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
WARNING::: This only works for SdkVersion 17+, so if your application targets a lower minimum SDK, you will have to create a custom menu and override the OnCreateOptions method(Unless there's another way which I'm not aware of, which is definitely possible).
https://developer.android.com/guide/topics/manifest/application-element.html#supportsrtl