Панель инструментов - Переход от кнопки выдвижения к задней панели только с одним действием
Я искал какое-то время о том, как изменить значок открытия/закрытия ящика (от гамбургера до стрелки) до простой стрелки назад. Мое приложение на данный момент имеет только одну активность, которая переключается между несколькими фрагментами. В какой-то момент я хочу перейти от одного из основных фрагментов (т.е. Одного из фрагментов в ящике) к фрагменту, который иерархически находится под предыдущим фрагментом (т.е. Фрагмент "Добавить новый" ). В этом новом фрагменте я хочу, чтобы на панели инструментов отображалась кнопка "Назад", а не кнопка "Ящик".
Я довольно долго смотрел вокруг и пробовал разные решения. Вот наиболее примечательные:
- Изменить значок ящика назад на стрелку - я успешно удалил значок ящика, но на месте ничего... ничего. Нет настройки, нет кнопки возврата, нет значка. Я подозреваю, что это связано с тем, что у моей Activity нет родителя, но кроме дешевой работы (создайте еще одно действие, которое действует как родитель, запускающее основное действие), я теряюсь в том, что делать.
- Переключение между изображениями Android Navigation Drawer и Up caret при использовании фрагментов - Как и выше, все же имеет гораздо более подробную информацию. В конечном итоге значок по-прежнему не превращается в кнопку "Назад".
- Переключатель панели инструментов для леденца для Android между открытым/закрытым ящиком и кнопкой назад - Мне это трудно понять, но в конечном итоге значок ящика можно прослушивать и ничего не делать (хотя Я считаю, что знаю, как заставить его действовать как задняя пресса). Однако значок не изменяется.
В настоящий момент я думаю о длинном, трудном методе создания пользовательского значка, который я скрываю и показываю (и скрываю/показываю значок нативного ящика). Однако есть ли лучший способ переключения между ящиком и кнопками возврата?
В качестве побочного вопроса, связанного со мной, я смотрел документы Material Design, а несколько примеров имеют X в верхнем левом углу. Насколько отличается реализация, чем реализация кнопок "ящик" и "назад/вверх"?
Спасибо ~
Edit:
Я могу выяснить, как заменить значок, но как я могу получить событие click?
До сих пор это было мое лучшее преимущество:
Что я сейчас пробовал:
- При необходимости отключить DrawerToggle (т.е.
mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
)
- Добавлены журналы в onOptionsItemSelected в моем NavigationDrawerFragment, моей активности, а также в диалоговом окне DialogFragment, который я тестирую в настоящее время, если
item.getItemId() == android.R.id.home
- true. Ни один из этих операторов журналов не выключается.
Для лучшего контекста теперь у меня есть полный фрагмент экрана, который добавляет кнопку "Сохранить" в меню и меняет значок ящика на "X". Фрагмент может получить событие меню сохранения, но даже активность и ящик не могут получить при нажатии X.
Edit2:
В соответствии с запросом, вот какой-то код. Обратите внимание, что это все из это репозиторий Github, над которым я активно работаю (обратите внимание, что у меня есть несколько бесполезных функций здесь или там из быстрое тестирование).
ActivityMain:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Add the toolbar
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
setSupportActionBar(mToolbar);
}
// Initialize the drawer
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
// Set up the drawer
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout),
mToolbar);
// TODO: Check if this helps to catch the main toolbar button click
getSupportActionBar().setDisplayShowHomeEnabled(true);
// Get the titles for the Toolbar
mTitles = getResources().getStringArray(R.array.drawer_items);
mDrawerPosition = -1;
if (savedInstanceState == null) {
// If there was no saved position, then the default, starting position should be used
forceChangeItemSelected(0);
}
else {
// Otherwise, get the saved position from the bundle
int position = savedInstanceState.getInt(KEY_DRAWERPOS);
mNavigationDrawerFragment.setSelectedItem(position);
// Title needs to be re-set
getSupportActionBar().setTitle(mTitles[position]);
}
// If I include the below bit, then the DrawerToggle doesn't function
// I don't know how to switch it back and forth
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(LOG_TAG, "Navigation was clicked");
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
Log.d(LOG_TAG, "Activity responding to menu click...");
if(item.getItemId() == android.R.id.home) Log.d(LOG_TAG, "Activity got it....");
// If the fragment is supposed to handle things, then let it
if(mIsFragmentHandlingMenus) return false;
int id = item.getItemId();
if(id == R.id.save) {
// This isn't implemented! If chosen, then there a bug!
Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
}
return super.onOptionsItemSelected(item);
}
@Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
// Simply store the setting
mIsFragmentHandlingMenus = isFragmentHandlingMenus;
// Toggle the drawer as necessary
mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}
NavigationDrawerFragment:
public void toggleDrawerUse(boolean useDrawer) {
// Enable/Disable the icon being used by the drawer
mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
// TODO: Enable/Disable the drawer even being able to open/close
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(LOGTAG, "Drawer responding to menu click...");
if(item.getItemId() == android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
GoalAdderFragment:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Allow this fragment to handle toolbar menu items
setHasOptionsMenu(true);
// Set up the toolbar
((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(android.R.drawable.ic_menu_close_clear_cancel);
((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Cache the Activity as the frag handler if necessary
if(mFragHandler == null)
mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
// Tell the Activity to let fragments handle the menu events
mFragHandler.fragmentHandlingMenus(true);
}
@Override
public void onDetach() {
super.onDetach();
// Tell the Activity that it can now handle menu events once again
mFragHandler.fragmentHandlingMenus(false);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.save_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();
switch (item.getItemId()) {
case R.id.save:
return true;
case android.R.id.home:
return true;
default:
break;
}
return false;
}
Решение:
Это окончательное решение, с которым я столкнулся, с помощью принятого ниже ответа:
NavigationDrawerFragment
private View.OnClickListener mOriginalListener;
public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
/* Rest of setting up code */
// Save the default listener after setting everything else up
mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}
// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
// Enable/Disable the icon being used by the drawer
mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);
// Switch between the listeners as necessary
if(useDrawer)
mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
else
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
}
});
}
Ответы
Ответ 1
Возможно, это не то, что вы хотели бы услышать, но даже с концептуальной точки зрения я бы выбрал новую деятельность, а не фрагмент.
Ваше основное действие строго связано с ящиком, поэтому загрузка нового фрагмента без доступа к ящику не имеет для меня никакого смысла (но не стесняйтесь ждать других ответов, если вы так думаете). Новое действие решило бы обе проблемы, поскольку у него не было бы ящика и могло бы быть ребенком основного.
Ваш побочный вопрос тоже выглядит. Активность "Добавить новый" могла бы хорошо вписаться в визуальный шаблон "полноэкранный диалог" из руководства. См:
http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs
Этот шаблон имеет "сохранить", положительную кнопку вверху справа и X. Концептуально кнопка X предназначена для отмены/прерывания процесса, а не для перемещения по стопке. Это означает, что вы увольняете что-то, не допуская никаких действий. Это хорошо подходит для того, что вы хотите сделать.
С точки зрения дизайна, это легко сделать с помощью нового Activity
, который может оставаться поверх других. Кроме того, если точка фрагментов в принципе может представлять два или более одновременно в планшетах и больше экрана - снова - я бы не был так доволен старым фрагментом слева и "Добавить новый" фрагмент справа.
Скорее - на планшетах - я бы пошел на плавающий диалог, как это было предложено в рекомендациях.
http://www.google.com/design/spec/components/dialogs.html#dialogs-confirmation-dialogs
Итак, полноэкранная активность с кнопкой X для телефонов и плавающим диалогом (с кнопками внизу) для планшетов. Это, для меня, является наиболее ориентированным на руководство принципом.
Я рекомендую прочитать всю ссылку. Отличие между < - и X,
X отличается от стрелки "Вверх", которая используется, когда состояние просмотров постоянно сохраняется или когда приложения имеют возможности создания или автосохранения. Например, в настройках используется стрелка "Вверх", так как все изменения совершаются немедленно.
А также
Прикоснувшись к X в этот пример настроек отменит все изменения. Изменения будут сохранены только при нажатии кнопки "Сохранить".
Ответ 2
Поместите этот код в onCreate()
вашего Activity
. Хорошо работает для меня. Даже используя compileSdk 23
и выше.
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if(toolbar != null) {
toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.syncState();
drawer.setDrawerListener(toggle);
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); // show back button
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
} else {
//show hamburger
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
toggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawer.openDrawer(GravityCompat.START);
}
});
}
}
});
Ответ 3
Он должен работать даже для последнего API 24.
В вашей деятельности onCreate()
сделайте следующее:
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
final DrawerLayout drawer = (DrawerLayout) view.findViewById(R.id.drawer_layout);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getSupportFragmentManager().popBackStack();
}
});
} else {
toggle.setDrawerIndicatorEnabled(true);
toggle.setToolbarNavigationClickListener(originalToolbarListener);
}
}
});
Ответ 4
Ответ от @matusalem отлично работает. У меня только один бит, чтобы добавить к нему - будьте осторожны, потому что ящик также можно открыть, прокручивая его с левой стороны экрана. Для некоторых это может быть желательно, но для меня я отключил ящик, потому что это не имело смысла ни в одном фрагменте, а в моем основном фрагменте. Салфетка здесь легко отключена -
Ящик навигации - отключить прокрутку
Это, вероятно, относится к комментарию к ответу, но у меня недостаточно репутации. Мои извинения.
Ответ 5
У меня была такая же проблема с переключением между меню гамбургера и обратной стрелкой внутри той же активности при смене фрагментов. Вот мое рабочее решение, надеюсь, что это кому-то поможет.
Слушатель внутри вашей деятельности:
private View.OnClickListener toolbarMenuListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//will be called only if toggle.setDrawerIndicatorEnabled(false); !
Log.v(tag,"toggle onClick:"+v.getId()+" android.R.id.home:"+android.R.id.home);
onBackPressed();
}
};
Код onCreate() что-то вроде:
...
...
setSupportActionBar(toolbar);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
//set listener so you know when back on arrow is pressed
toggle.setToolbarNavigationClickListener(toolbarMenuListener);
...
...
Часть, которую вас интересуют с комментариями (возвращаемый класс является частью моего класса, может быть недействительным):
/**
* Method to set up action bar drawer.
* @param enableBackDrawerIcon set true if want to show drawer back arrow,
* false to show hamburger menu.
* @param title shown next to drawer icon
*/
public BaseMenusActivity drawerSetupToggle(boolean enableBackDrawerIcon, String title) {
//NOTE: order of methods call is important!
// If you change order order of setDrawerIndicatorEnabled and setDisplayHomeAsUpEnabled
// method calls it won't work, weird bugs will happen (like no icon at all)
if(enableBackDrawerIcon){
Log.v(tag,"show drawer back icon");
//hides hamburger menu and enables View.OnClickListener to be called
toggle.setDrawerIndicatorEnabled(false);
//show back arrow
if(getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
Log.v(tag,"show hamburger menu");
//hide back arrow
if(getSupportActionBar()!=null)
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
//shows hamburger menu and prevents View.OnClickListener to be called
toggle.setDrawerIndicatorEnabled(true);
}
setTitle(title);
return this;
}
ПРИМЕЧАНИЕ: порядок вызываемых методов важен! Было бы лучше, если бы просто написать его в 2 строках, как это, но НЕ РАБОТАЕТ (по крайней мере для меня):
toggle.setDrawerIndicatorEnabled(!enableBackDrawerIcon);
getSupportActionBar().setDisplayHomeAsUpEnabled(enableBackDrawerIcon);
Если вам интересно, почему порядок вызова метода вызывает проблемы, посмотрите на реализацию этих методов.