обработка заголовка actionbar со стеком фрагмента?

<p>у меня есть <code>activity</code>, где я загружаю <code>listfragment</code>, и при нажатии он детализирует уровень, и отображается новый тип <code>listfragment</code>, заменяя исходный (используя метод <code>showfragment</code> ниже). это размещено на заднем стеке.</p><p>вначале действие показывает заголовок по умолчанию на панели действий (т.е. он устанавливается автоматически на основе <code>android:label</code> приложения).</p><p>при отображении списка для следующего уровня в иерархии, название элемента, по которому щелкнули, должно стать заголовком панели действий.</p><p>однако при нажатии <kbd>back</kbd>я хотел бы восстановить исходный заголовок по умолчанию. это не то, о чем <code>fragmenttransaction</code> знает, поэтому название не восстанавливается.</p><p>я смутно читал о <code>fragmentbreadcrumbs</code>, но, похоже, для этого нужно использовать пользовательское представление. я использую actionbarsherlock и предпочел бы, чтобы у меня не было своего собственного пользовательского представления заголовка.</p><p>каков наилучший способ сделать это? возможно ли это без загрузки стандартного кода и необходимости отслеживать названия, показанные по пути?</p><hr><pre><code>protected void showfragment(fragment f) { fragmenttransaction ft = getsupportfragmentmanager().begintransaction(); ft.replace(r.id.fragment\_container, f); ft.addtobackstack(null); ft.commit(); } </code></pre>

<p>в каждом фрагменте и каждом действии я меняю название следующим образом. таким образом, активный заголовок всегда будет правильным:</p><pre><code>@override public void onresume() { super.onresume(); // set title getactivity().getactionbar() .settitle(r.string.thetitle); } </code></pre><p>в некоторых случаях onresume не вызывается внутри фрагментов. в некоторых из этих случаев мы можем использовать:</p><pre><code>public void setuservisiblehint(boolean isvisibletouser) { super.setuservisiblehint(isvisibletouser); if(isvisibletouser) { // set title getactivity().getactionbar() .settitle(r.string.thetitle); } } </code></pre>

<p>поскольку первоначальный ответ довольно старый, это также может помочь. как указано в документации, можно зарегистрировать <code>listener</code> для прослушивания изменений в стеке хоста <code>activity</code>:</p><pre><code>getsupportfragmentmanager().addonbackstackchangedlistener( new fragmentmanager.onbackstackchangedlistener() { public void onbackstackchanged() { // update your ui here. } }); </code></pre><p>затем определите ситуацию в методе обратного вызова и установите правильный заголовок, не обращаясь к <code>actionbar</code> из <code>fragment</code>.</p><p>это более элегантное решение, так как <code>fragment</code> не обязательно должна знать о существовании <code>actionbar</code>, а <code>activity</code> обычно является местом, которое управляет backstack, поэтому его обработка там кажется более подходящей. <code>fragment</code> всегда следует рассматривать только по своему содержанию, а не по окружению.</p><p>больше по теме в <a href="http://developer.android.com/training/implementing-navigation/temporal.html#back-fragments">документация</a> .</p>

<p>пусть контролирующая деятельность выполняет всю работу следующим образом:</p><p>прослушивать события backstack (в oncreate () активности):</p><pre><code>// change the title back when the fragment is changed getsupportfragmentmanager().addonbackstackchangedlistener(new fragmentmanager.onbackstackchangedlistener() { @override public void onbackstackchanged() { fragment fragment = getfragment(); settitlefromfragment(fragment); } }); </code></pre><p>получить текущий фрагмент из контейнера:</p><pre><code>/** * returns the currently displayed fragment. * @return * fragment or null. */ private fragment getfragment() { fragment fragment = getsupportfragmentmanager().findfragmentbyid(r.id.container); return fragment; } </code></pre><p>установите фрагмент в представлении содержимого:</p><pre><code>private void setfragment(fragment fragment, boolean addtobackstack) { // set the activity title settitlefromfragment(fragment); . . . } </code></pre>

<p>варпзит прав. это также решает проблему названия при изменении ориентации устройства. также, если вы используете поддержку v7 для панели действий, вы можете получить панель действий из фрагмента следующим образом:</p><pre><code>@override public void onresume() { super.onresume(); ((actionbaractivity)getactivity()).getsupportactionbar().settitle("home"); } </code></pre>

<p>лучше всего, чтобы ос выполняла как можно больше работы. предполагая, что каждый фрагмент правильно назван с использованием .addtobackstack ("title"), вы можете переопределить onbackpressed что-то вроде этого для достижения желаемого поведения:</p><pre><code>// this example uses the appcompat support library // and works for dynamic fragment titles @override public void onbackpressed() { fragmentmanager fragmentmanager = getsupportfragmentmanager(); int count = fragmentmanager.getbackstackentrycount(); if (count <= 1) { finish(); } else { string title = fragmentmanager.getbackstackentryat(count-2).getname(); if (count == 2) { // here i am using a navigationdrawer and open it when transitioning to the initial fragment // a second back-press will result in finish() being called above. mdrawerlayout.opendrawer(mnavigationdrawerfragment.getview()); } super.onbackpressed(); log.v(tag, "onbackpressed - title="+title); getsupportactionbar().settitle(title); } } </code></pre>

<p>я использую аналогичное решение для <a href="https://stackoverflow.com/a/33716380/5004910">подход ли</a> , но вместо этого заменяю метод <code>onbackstackchanged()</code>.</p><p>сначала я устанавливаю имя фрагмента при добавлении транзакции в задний стек.</p><pre><code>getsupportfragmentmanager().begintransaction() .replace(r.id.frame\_content, fragment) .addtobackstack(fragmenttitle) .commit(); </code></pre><p>затем я перезаписываю метод <code>onbackstackchanged()</code> и вызываю <code>settitle()</code> с именем последней записи backstack.</p><pre><code>@override public void onbackstackchanged() { int lastbackstackentrycount = getsupportfragmentmanager().getbackstackentrycount() - 1; fragmentmanager.backstackentry lastbackstackentry = getsupportfragmentmanager().getbackstackentryat(lastbackstackentrycount); settitle(lastbackstackentry.getname()); } </code></pre>

<p>используйте метод фрагменты:</p><pre><code>@override public void oncreateoptionsmenu(menu menu, menuinflater inflater) </code></pre><p>он вызывается при каждом появлении фрагмента, а onresume - нет.</p>

<p>вы можете решить с onkeydown! у меня есть bool mainisopen = true <- основной фрагмент виден другим фрагмент mainisopen = false</p><p>и вот мой код:</p><pre><code>public boolean onkeydown(int keycode, keyevent event) { if (keycode == keyevent.keycode\_back && mainisopen == false) { mainisopen = true; homefrag fragment = new homefrag(); fragmenttransaction fragmenttransaction = getsupportfragmentmanager().begintransaction(); fragmenttransaction.replace(r.id.fragmet\_cont, fragment); fragmenttransaction.commit(); navigationview = (navigationview) findviewbyid(r.id.nav\_view); navigationview.getmenu().finditem(r.id.nav\_home).setchecked(true); navigationview.setnavigationitemselectedlistener(this); this.settitle("digi - home"); //here set the title back return true; } else { if (keycode == keyevent.keycode\_back && mainisopen == true) { alertdialog.builder builder = new alertdialog.builder(this); builder.setmessage("wollen sie die app schliessen!"); builder.setcancelable(true); builder.setpositivebutton("ja!", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { system.exit(1); } }); builder.setnegativebutton("nein!", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { toast.maketext(getapplicationcontext(), "applikation wird fortgesetzt", toast.length\_short).show(); } }); alertdialog dialog = builder.create(); dialog.show(); return true; } return super.onkeydown(keycode, event); } } </code></pre>

<p>наилучшим подходом является использование предоставленного android метода интерфейса onbackstackchangedlistener onbackstackchanged ().</p><p>допустим, у нас есть навигационный ящик с 4 опциями, к которым пользователь может перейти. в этом случае у нас будет 4 фрагмента. давайте сначала посмотрим код, а затем я объясню работу.</p><pre><code> private int mpreviousbackstackcount = 0; private string[] title\_name = {"frag1","frag2","frag3","frag4"}; stack<string> mfragpositiontitledisplayed; public class mainactivity extends actionbaractivity implements fragmentmanager.onbackstackchangedlistener @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); .... .... .... getsupportfragmentmanager().addonbackstackchangedlistener(this); mfragpositiontitledisplayed = new stack<>(); } public void displayfragment() { fragment fragment = null; string title = getresources().getstring(r.string.app\_name); switch (position) { case 0: fragment = new fragment1(); title = title\_name[position]; break; case 1: fragment = new fragment2(); title = title\_name[position]; break; case 2: fragment = new fragment3(); title = title\_name[position]; break; case 3: fragment = new fragment4(); title = title\_name[position]; break; default: break; } if (fragment != null) { fragmentmanager fragmentmanager = getsupportfragmentmanager(); fragmentmanager.begintransaction() .replace(r.id.container\_body, fragment) .addtobackstack(null) .commit(); getsupportactionbar().settitle(title); } } @override public void onbackstackchanged() { int backstackentrycount = getsupportfragmentmanager().getbackstackentrycount(); if(mpreviousbackstackcount >= backstackentrycount) { mfragpositiontitledisplayed.pop(); if (backstackentrycount == 0) getsupportactionbar().settitle(r.string.app\_name); else if (backstackentrycount > 0) { getsupportactionbar().settitle(mfragpositiontitledisplayed.peek()); } mpreviousbackstackcount--; } else{ mfragpositiontitledisplayed.push(title\_name[position]); mpreviousbackstackcount++; } } </code></pre><p>в показанном коде у нас есть метод displayfragment (). здесь я отображаю фрагмент на основе опции, выбранной из навигационной панели. переменная позиция соответствует позиции элемента, щелкнувшего из listview или recyclerview в навигационной панели. я установил заголовок панели действий соответственно с помощью getsupportactionbar.settitle (title), где заголовок хранит соответствующее название заголовка.</p><p>всякий раз, когда мы щелкаем элемент в навигационном ящике, отображается фрагмент в зависимости от того, на каком элементе щелкнул пользователь. но на стороне сервера этот фрагмент добавляется в backstack, и метод onbackstachchanged () получает удар. что я сделал, так это то, что я создал переменную mpreviousbackstackcount и инициализировал ее как 0. я также создал дополнительный стек, который будет хранить названия заголовков панели действий. всякий раз, когда я добавляю новый фрагмент в backstack, я добавляю имя соответствующего заголовка в свой созданный стек. с другой стороны, всякий раз, когда я нажимаю кнопку "назад", вызывается onbackstackchanged (), и я вытаскиваю последнее название заголовка из моего стека и устанавливаю для заголовка имя, полученное с помощью метода peek () стека.</p><p>пример:</p><p>допустим, наш android бэкстек пуст:</p><p>нажмите "выбор 1" из навигационной панели: вызывается onbackstachchanged (), фрагмент 1 добавляется в android backstack, backstackentrycount устанавливается в 1, а frag1 помещается в мой стек, а размер mfragpositiontitledisplayed становится равным 1.</p><p>нажмите choice 2 из панели навигации: вызывается onbackstachchanged (), фрагмент 2 добавляется в android backstack, backstackentrycount устанавливается в 2, а frag2 помещается в мой стек, а размер mfragpositiontitledisplayed становится равным 2.</p><p>теперь у нас есть 2 элемента как в стеке android, так и в моем стеке. когда вы нажимаете кнопку "назад", вызывается onbackstackchanged () и значение backstackentrycount равно 1. код входит в часть if и выскакивает последнюю запись из моего стека. итак, в бэкстеке android есть только 1 фрагмент - "фрагмент 1", а в моем стеке только 1 заголовок - "frag1". теперь я просто смотрю () заголовок из своего стека и устанавливаю панель действий для этого заголовка.</p><p>помните: чтобы установить название действия bat, используйте peek (), а не pop (), иначе ваше приложение будет зависать, когда вы откроете более 2 фрагментов и попытаетесь вернуться назад, нажав кнопку назад.</p>