Организовать блок навигации FragmentActivity

Приложение (целевой уровень API должно быть 7) имеет FragmentActivity который анализирует на onCreate ключ фрагмента, переданный как дополнительный.

Теперь необходимо переупорядочить фронт активности, которая уже создана с данным фрагментным ключом.

Скажем, FragmentActivity с разными ключами фрагмента – FA1 , FA2 и FA3 – каждый из них – это один экземпляр класса активности с различными фрагментами.

Теперь в стеке FA1 > FA2 > FA3 я хочу использовать намерение, а не кнопку возврата, чтобы добраться до FA2 , по умолчанию это дает:

FA1 > FA2 > FA3 > новый FA2 .

Я бы хотел получить FA1 > FA3 > FA2 поскольку FA3 может иметь некоторые ожидающие операции FA1 > FA2 не так хорошо, но определенно лучше, чем по умолчанию.

Если было несколько действий, я бы использовал флаг FLAG_ACTIVITY_REORDER_TO_FRONT для намерений, но это не работает для этого случая.


FA1, FA2, FA3, etc. – все экземпляры одного и того же класса MyFA , поэтому я не могу использовать флаг намерения, а FragmentManager, похоже, не поддерживается, пока не появится стандартный кэш глобальных фрагментов.


Решение Milestone (в настоящее время работает и должно быть улучшено). Одна вещь, которую я узнал сегодня, – это псевдоним активности, который позволил сделать несколько псевдонимов для одной и той же деятельности с различными дополнениями Intent, используемыми как id. Теперь с флагом REORDER_TO_FRONT он работает так, как я хотел.

Ответ на решение Решение не имеет операций на низком уровне, мне нравится намного больше, чем копать в задачах или back-stack. Теперь недостатком является то, что каждый из таких действий требует отдельного псевдонима с жестко заданным путем, мне это не очень нравится.

Требования (щедрость здесь) Тот, кто приходит с достойной оптимизацией, принимает 150 300 файлов cookie. Неплохо ? Также приветствуется любое другое твердое решение.

В настоящее время у меня есть 10 псевдонимов в манифесте приложения, например

  <activity android:name=".activity.FragmentActivity" android:configChanges="orientation" android:screenOrientation="portrait" > <intent-filter> <category android:name="android.intent.category.DEFAULT" /> <action android:name="com.company.name.intent.FragmentActivity" /> </intent-filter> </activity> <activity-alias android:name="com.company.name.intent.FragmentActivity.FragmentedOne" android:targetActivity=".activity.FragmentActivity" > <intent-filter> <action android:name="com.company.name.intent.FragmentActivity.FragmentedOne" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="fragment_key_extra" android:value="FragmentOne" /> </activity-alias> <activity-alias android:name="com.company.name.intent.FragmentActivity.FragmentedTwo" android:targetActivity=".activity.FragmentActivity" > <intent-filter> <action android:name="com.company.name.intent.FragmentActivity.FragmentedTwo" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="fragment_key_extra" android:value="FragmentTwo" /> </activity-alias> 

И тогда действия переупорядочиваются с помощью

 Intent intent = new Intent( "com.company.name.intent.FragmentActivity.FragmentedOne"); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(intent); 

Основываясь на вашей идее использования activity-alias для решения этой проблемы, я написал класс Historian, который будет делать следующее:

  • Сканируйте список активности в своем пакете для псевдонимов для своей конкретной деятельности.
  • Настройте таблицу поиска, которая отображает каждый псевдоним в намерение.
  • Предоставьте startActivity() и activityDestroyed() которые будут выполнять некоторую бухгалтерию, чтобы таблица поиска могла использоваться для динамического назначения псевдонима для выполняемой операции на основе намерения.

Вот пример того, как его использовать:

  • В AndroidManifest.xml

     <activity android:name=".MyFragmentActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity-alias android:name=".Alias0" android:targetActivity=".MyFragmentActivity" /> <activity-alias android:name=".Alias1" android:targetActivity=".MyFragmentActivity" /> <activity-alias android:name=".Alias2" android:targetActivity=".MyFragmentActivity" /> <activity-alias android:name=".Alias3" android:targetActivity=".MyFragmentActivity" /> <activity-alias android:name=".Alias4" android:targetActivity=".MyFragmentActivity" /> 
  • В вашем классе активности

     public class MyFragmentActivity extends FragmentActivity implements Historian.Host { private Historian<MyFragmentActivity> mHistorian; // ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHistorian = new Historian<MyFragmentActivity>(this); // ... } @Override protected void onDestroy() { super.onDestroy(); mHistorian.activityDestroyed(this); } @Override public boolean matchIntent(Intent intent0, Intent intent1) { if (intent0 == null || intent1 == null) return false; final String title0 = intent0.getStringExtra("title"); final String title1 = intent1.getStringExtra("title"); return title0.equals(title1); } // ... } 

Вместо того, чтобы запускать новый экземпляр вашей деятельности следующим образом:

  Intent intent = new Intent(this, MyFragmentActivity.class); intent.putExtra("title", newActivityTitle); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(intent); 

Ты делаешь это:

  Intent intent = new Intent(this, MyFragmentActivity.class); intent.putExtra("title", newActivityTitle); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); mHistorian.startActivity(this, intent); 

Таким образом, вам все равно нужно добавить несколько activity-alias в свой манифест вручную (для использования в качестве пула) и реализовать метод matchIntent() в вашей деятельности (чтобы определить, соответствуют ли вам два намерения), но остальные обрабатываются Динамически классом Historian.

Я не тестировал код исчерпывающе, но, похоже, он отлично работает на некоторых простых тестах, которые я сделал. Идея на самом деле очень похожа на мой ответ на другой вопрос (просто нужно использовать FLAG_ACTIVITY_CLEAR_TOP вместо FLAG_ACTIVITY_REORDER_TO_FRONT там), но использование activity-alias вместо внутренних дочерних классов делает его намного более чистым 🙂

Этот ответ стоит 300 куки: -D Mmmmm

  1. Расширьте приложение, чтобы мы могли создать наш собственный список стека с глобальной областью.

    Import android.app.Application; Import android.content.Intent;

    Класс MyApp расширяет Application {

     //This is our very own back stack. private ArrayList<Intent> backStack = new ArrayList<Intent>(); public void reorderBackStack(){ //Do what you want to the order of your backstack //You can read the value of your intent extras from here. } public void addIntent(Intent i){ // this gets called from our activities onCreate backStack.add(i); } public void goBack(){ if(backStack.size() >= 2){ // can go back backStack.remove(backStack.size() - 1); // drop the last item in stack if you want. This is how Android does it. (no forward capability) this.startActivity(backStack.get(backStack.size() - 1)); } } 

    }

  2. Добавьте ссылку на наш пользовательский задний стек в нашей деятельности.

    Открытый класс MainActivity расширяет действие {MyApp myapp;

      @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myapp = ((MyApp)getApplicationContext()); myapp.addIntent(this.getIntent()); //add the intent for this instance to backStack. myapp.reorderBackStack(); // call this anytime we need to reorder the back stack. } @Override public void onNewIntent(Intent newIntent){ this.setIntent(newIntent); myapp.addIntent(this.getIntent()); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override // We won't be using the back stack we will be using our own list of intents as a back stack so we need to override the back button. public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { myapp.goBack(); return true; } return super.onKeyDown(keyCode, event); } 

    }

  3. Переверните кнопку «Назад», чтобы мы могли использовать наш собственный задний стек вместо Android.

редактировать

Я собрал доказательство концепции.

  • Pros
    • Нет псевдонимов
    • Полностью динамичный и многоразовый
    • Контроль низкого уровня
    • Прост в использовании (расширение CustomBackStackActivity вместо FragmentActivity или Activity)
  • Cons
    • Это не готовое решение. Требуется настройка и отладка.

.apk находится здесь, на моем диске Google.

Полная папка проекта находится здесь .

Почтовая загружаемая папка проекта находится здесь .

Пожалуйста, не жалуйтесь на мой код. Я знаю, что я использовал строки, где я должен был использовать константы, и я дублировал код, а не разделял его, и мой интервал не был совершенным, и я использовал чрезмерные циклы и объекты. Я не отсортировал сохраненный файлInstanceState. Опять же, это просто доказательство концепции. Это работает, и я думал, что это может помочь кому-то.

Вы можете изучить функциональность, предоставляемую FragmentManager . Он имеет такие функции, как popBackStack .

Вы также можете использовать FragmentTransaction для добавления фрагментов в стопку. Вы можете использовать addToBackStack для этого.

Таким образом, с этими двумя классами вы должны иметь возможность переупорядочивать свои фрагменты в стопке по мере необходимости.

Вот решение:

1. В FragmentActivity реализуется Interface для обратного вызова из фрагмента в действие.

2. Когда вы начинаете любой фрагмент, поместите его в стек обратно с аргументом или тегом (addToBackStack(String arg0)) с помощью которого вы можете всплывать с этим тегом.

3. Когда вам нужно изменить порядок фрагментов, вызовите метод интерфейса, который вы внедрили с соответствующими аргументами в соответствии с требованием, а затем используйте popBackStack(String arg1, String arg2) чтобы получить фрагмент с тегом, который вы поместили в стопку.

Я ответил на что-то подобное, но решение не лучшее, поскольку FA1> FA2> FA3 приведет вас к FA1> FA2, а не FA1> FA3> FA2.

Мой ответ был за вопрос: как отмечать активность

Во всяком случае, я немного изменил код, чтобы использовать «тег», который будет строкой, но я думаю, вы можете использовать основные решения, которые используют целочисленные индексы. Таким образом, это должно позволить вам fallBackToActivity с определенной TAG. Я не уверен, как каждая активность решит, каков будет ее тег, но опять же, в моем предыдущем ответе, это было простое инкрементное целое.

 package sherif.android.stack.overflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; public class SuperActivity extends FragmentActivity { private static String EXTRA_INDEX = "SUPER_INDEX"; private static int RESULT_FALLBACK = 0x123456; private String tag; //this is your tag @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(getIntent()!=null) { tag = getIntent().getStringExtra(EXTRA_INDEX, "default_tag"); } } protected final String getIndex() { return tag; } protected final void fallBackToActivity(String tag) { Intent intent = new Intent(); intent.putExtra(EXTRA_INDEX, tag); setResult(RESULT_FALLBACK, intent); finish(); } //@Override //public void startActivityForResult(Intent intent, int requestCode) { // intent.putExtra(EXTRA_INDEX, getIndex()); // super.startActivityForResult(intent, requestCode); //} @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == RESULT_FALLBACK) { if(!data.getStringExtra(EXTRA_INDEX, "default_tag").equals(getIndex())) { setResult(RESULT_FALLBACK, data); finish(); } } } }