简体   繁体   English

组织FragmentActivity导航堆栈

[英]Organize FragmentActivity navigation stack

The application(target API level must be 7th) has FragmentActivity which analyzes at onCreate the fragment key passed as an extra. 该应用程序(目标API级别必须为7)必须具有FragmentActivity ,该片段在onCreate分析作为额外传递的片段密钥。

Now what is needed is to reorder to front the activity that is already created with the given fragment key. 现在,需要重新排序以使用给定的片段密钥创建已经创建的活动。

Let's say the FragmentActivity with different fragment keys are FA1 , FA2 and FA3 - each is the same activity Class instance with different fragments. 假设具有不同片段密钥的FragmentActivityFA1FA2FA3每个都是具有不同片段的相同活动类实例。

Now in the stack FA1 > FA2 > FA3 i want to use the intent rather than the back button to get to FA2 , by default that gives: 现在在堆栈FA1 > FA2 > FA3我想使用意图而不是使用后退按钮进入FA2 ,默认情况下,它给出:

FA1 > FA2 > FA3 > new FA2 . FA1 > FA2 > FA3 >新的FA2

I'd like to get either FA1 > FA3 > FA2 as the FA3 might have some pending operations, FA1 > FA2 is not as good but definitely better than default. 我想得到FA1 > FA3 > FA2因为FA3可能有一些待处理的操作, FA1 > FA2并不好,但肯定比默认值要好。

If there were several activities I'd use the FLAG_ACTIVITY_REORDER_TO_FRONT flag for intents, but that does not work for this case. 如果有多个活动,则可以将FLAG_ACTIVITY_REORDER_TO_FRONT标志用于意图,但是在这种情况下不起作用。


FA1, FA2, FA3, etc. are all the instances of the same class MyFA , that's why I'm not able to use the intent flag and the FragmentManager seems to be out of help until there's a standard global fragments cache. FA1, FA2, FA3, etc.都是同一类MyFA所有实例,这就是为什么我无法使用intent标志的原因,并且在没有标准的全局片段缓存之前,FragmentManager似乎无济于事。


Milestone (currently working and to be improved) solution One thing I've learned today is activity-alias which allowed to make several aliases for the same activity with the different Intent extras used as id's. 里程碑(当前正在工作并有待改进)解决方案我今天了解到的一件事是活动别名,该活动别名可以为同一个活动创建多个别名,并使用不同的Intent附加项作为id。 Now with the REORDER_TO_FRONT flag it works as I wanted. 现在带有REORDER_TO_FRONT标志,它可以按我的要求工作。

Solution feedback The solution has no low-level operations, I like a lot more than digging at the tasks or back-stacks. 解决方案反馈该解决方案没有低级操作,除了挖掘任务或返回堆栈外,我更喜欢。 Now the drawback is that each of such activities needs a separate alias with the hardcoded path, I don't really like it. 现在的缺点是,每个这样的活动都需要一个带有硬编码路径的单独别名,我不太喜欢。

Requirements (bounty is here) Whoever comes with a decent optimization takes 150 300 cookies. 要求(赏金在这里)进行体面优化的人都需要150 300个Cookie。 Not bad ? 不错 ? Any other solid solution is also highly appreciated. 还高度赞赏任何其他固溶体。

Currently I have like 10 aliases at application manifest, eg 目前,我在应用程序清单中有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>

And then the activities are reordered with 然后将活动重新排序

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

This answer is worth 300 cookies :-D Mmmmm 这个答案价值300饼干:-D Mmmmm

  1. Extend Application so we can create our own back stack list with a global scope. 扩展应用程序,以便我们可以创建一个具有全局范围的后退列表。

    import android.app.Application; 导入android.app.Application; import android.content.Intent; 导入android.content.Intent;

    class MyApp extends Application { 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. Add a reference to our custom back stack in our activity. 在活动中添加对自定义后退堆栈的引用。

    public class MainActivity extends Activity { MyApp myapp; 公共类MainActivity扩展了Activity {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. Override the back button so we can use our own back stack instead of Android's. 覆盖后退按钮,以便我们可以使用自己的后退堆栈而不是Android的后退堆栈。

Edit 编辑

I put together a proof of concept. 我整理了概念证明。

  • Pros 优点
    • No Aliases 没有别名
    • Completely dynamic and reusable 完全动态且可重用
    • Low level control 低电平控制
    • Easy to use (Extend CustomBackStackActivity instead of FragmentActivity or Activity) 易于使用(扩展CustomBackStackActivity而不是FragmentActivity或Activity)
  • Cons 缺点
    • Not an out-of-the-box solution. 不是开箱即用的解决方案。 Will require tweaking and debugging. 将需要调整和调试。

The .apk is Here on my google drive. .apk在我的Google驱动器上。

The complete project folder is here . 完整的项目文件夹在这里

Zipped downloadable project folder is here . 压缩的可下载项目文件夹在此处

Please don't complain about my code. 请不要抱怨我的代码。 I know I used strings where I should have used constants and I duplicated code instead of seperating it and my spacing isn't perfect and I used excessive loops and objects. 我知道我在应该使用常量的地方使用了字符串,并且我复制了代码而不是将其分开,并且我的间距不够完美,并且使用了过多的循环和对象。 I haven't sorted out the savedInstanceState. 我还没有整理出savedInstanceState。 Again, this is just a proof of concept. 同样,这只是概念的证明。 It works and I thought it might help someone. 它有效,我认为这可能会对某人有所帮助。

You can look into using the functionality provided by the FragmentManager . 您可以研究使用FragmentManager提供的功能。 It has functions such as popBackStack . 它具有诸如popBackStack功能。

You can also use FragmentTransaction to add fragments to the backstack. 您还可以使用FragmentTransaction将片段添加到Backstack。 You can use addToBackStack to accomplish this. 您可以使用addToBackStack完成此操作。

So with these two classes you should be able to reorder your fragments in the backstack as necessary. 因此,使用这两个类,您应该能够根据需要在后堆栈中重新排序片段。

Here is the solution: 解决方法如下:

1. In your FragmentActivity implement a Interface for the callback from Fragment to Activity. 1.FragmentActivity为从Fragment到Activity的回调实现一个Interface

2. When you start any Fragment put it in back stack with the argument or tag (addToBackStack(String arg0)) with the help of which you can pop up with that tag. 2.启动任何Fragment时,将其与自变量或标记(addToBackStack(String arg0))放回堆栈中,并借助该标记或标签弹出该标记。

3. When you need to reorder the fragments then call the method of the interface which you implemented with proper arguments as per requirement and then use popBackStack(String arg1, String arg2) to get the fragment with the tag you put it in the backstack. 3.当您需要对片段重新排序时,请根据需要使用适当的参数调用实现的接口方法,然后使用popBackStack(String arg1, String arg2)获得带有将其放入后退堆栈的标签的片段。

I have answered something similar, but the solution is not the best as FA1 > FA2 > FA3 will take you to FA1 > FA2 rather than FA1 > FA3 > FA2. 我已经回答了类似的问题,但是解决方案并不是最好的解决方案,因为FA1> FA2> FA3会将您带到FA1> FA2,而不是FA1> FA3> FA2。

My answer was for question : How to tag Activity 我的答案是一个问题:如何标记活动

Anyway, I edited the code a bit to use a "tag" which will be a string, but I think you can use the main solutions that uses integer indices. 无论如何,我对代码进行了一些编辑,以使用将是字符串的“标签”,但是我认为您可以使用使用整数索引的主要解决方案。 In summary, this should allow you to fallBackToActivity with a certain TAG. 总之,这应该允许您使用某个TAG进行fallBackToActivity I am not sure how will each Activity decide what will its tag be, but again, in my previous answer it was a simple matter of an incremental integer. 我不确定每个Activity如何决定其标签是什么,但是再次,在我以前的回答中,这只是一个递增整数的简单问题。

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();
            }
        }
    }
}

Based on your idea of using activity-alias to solve this issue, I wrote a Historian class that will do the following: 基于您使用activity-alias解决此问题的想法,我编写了一个Historian类 ,它将执行以下操作:

  • Scan the Activity list in your package for aliases to your specific Activity. 扫描软件包中的“活动”列表,以查找特定活动的别名。
  • Set up a lookup table that maps each alias to an Intent. 设置一个查找表,将每个别名映射到一个Intent。
  • Provide a startActivity() and activityDestroyed() methods that will do some bookkeeping so the lookup table can be used to dynamically assign an alias to a running Activity based on the Intent. 提供一个startActivity()activityDestroyed()方法,这些方法可以做一些簿记工作,因此可以使用查询表根据Intent为正在运行的Activity动态分配别名。

Here's an example on how to use it: 这是有关如何使用它的示例:

  • in AndroidManifest.xml 在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" /> 
  • within your Activity class 在您的活动课中

     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); } // ... } 

Instead of starting a new instance of your activity like this: 而不是像这样启动活动的新实例:

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

You do this: 你做这个:

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

So you still need to add a few activity-alias into your manifest manually (to be used as a pool) and implement the matchIntent() method in your Activity (to help detect whether two Intents are equal to you) but the rest is handled dynamically by the Historian class. 因此,您仍然需要手动在清单中添加一些activity-alias (用作池),并在Activity中实现matchIntent()方法(以帮助检测两个Intent是否等于您),但其余的要处理由历史学家阶级动态地改变。

I haven't tested the code exhaustively, but it seems to work fine on some simple tests that I did. 我还没有对代码进行详尽的测试,但是在我做过的一些简单测试中似乎还可以正常工作。 The idea is actually very similar to my answer on the other question (just need to use FLAG_ACTIVITY_CLEAR_TOP instead of FLAG_ACTIVITY_REORDER_TO_FRONT there) but using the activity-alias instead of the inner child classes make it much cleaner :) 这个想法实际上与另一个问题上的回答非常相似(只需要在那里使用FLAG_ACTIVITY_CLEAR_TOP而不是FLAG_ACTIVITY_REORDER_TO_FRONT ),但是使用activity-alias而不是内部子类会使它更简洁:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM