3

应用程序(目标 API 级别必须为第 7 级)在作为额外传递的片段密钥FragmentActivity处进行分析。onCreate

现在需要重新排序到前面已经用给定的片段键创建的活动。

假设FragmentActivity具有不同片段键的 FA1FA2-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 似乎没有帮助,直到有一个标准的全局片段缓存。


里程碑(目前正在工作并有待改进)解决方案我今天学到的一件事是活动别名,它允许为同一个活动创建多个别名,并将不同的 Intent 附加项用作 id。现在使用 REORDER_TO_FRONT 标志,它可以按我的意愿工作。

解决方案反馈该解决方案没有低级操作,我更喜欢挖掘任务或回栈。现在的缺点是每个这样的活动都需要一个带有硬编码路径的单独别名,我不太喜欢它。

要求(赏金在这里)进行了不错的优化的人需要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);
4

5 回答 5

4

这个答案值 300 块饼干 :-D Mmmmm

  1. 扩展应用程序,以便我们可以创建自己的全局范围的后退堆栈列表。

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

    类 MyApp 扩展应用程序 {

    //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 扩展 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. 覆盖后退按钮,这样我们就可以使用我们自己的后退堆栈而不是 Android 的。

编辑

我整理了一个概念证明。

  • 优点
    • 没有别名
    • 完全动态且可重用
    • 低电平控制
    • 易于使用(扩展 CustomBackStackActivity 而不是 FragmentActivity 或 Activity)
  • 缺点
    • 不是开箱即用的解决方案。将需要调整和调试。

.apk在我的谷歌驱动器上。

完整的项目文件夹在这里

压缩的可下载项目文件夹在这里

请不要抱怨我的代码。我知道我在应该使用常量的地方使用了字符串,并且我复制了代码而不是分开它,而且我的间距并不完美,而且我使用了过多的循环和对象。我还没有整理保存的InstanceState。同样,这只是一个概念证明。它有效,我认为它可能会帮助某人。

于 2012-10-24T02:40:20.770 回答
2

您可以考虑使用FragmentManager提供的功能。它具有诸如popBackStack.

您还可以使用FragmentTransaction将片段添加到后台堆栈。您可以使用addToBackStack来完成此操作。

因此,使用这两个类,您应该能够根据需要重新排序 backstack 中的片段。

于 2012-08-03T14:08:59.333 回答
2

这是解决方案:

1.在您FragmentActivity实现Interface从 Fragment 到 Activity 的回调。

2.当您启动任何片段时,将其与参数或标签一起放入后堆栈(addToBackStack(String arg0)),您可以借助该标签弹出该标签。

3.当您需要对片段进行重新排序时,请根据要求使用适当的参数调用您实现的接口的方法,然后使用popBackStack(String arg1, String arg2)您将其放入后台堆栈的标签获取片段。

于 2012-10-26T13:26:42.980 回答
1

我已经回答了类似的问题,但解决方案不是最好的,因为 FA1 > FA2 > FA3 会将您带到 FA1 > FA2 而不是 FA1 > FA3 > FA2。

我的回答是针对问题:如何标记活动

无论如何,我稍微编辑了代码以使用将是字符串的“标签”,但我认为您可以使用使用整数索引的主要解决方案。总而言之,这应该允许您fallBackToActivity使用某个 TAG。我不确定每个 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();
            }
        }
    }
}
于 2012-10-29T10:24:28.323 回答
1

根据您activity-alias用于解决此问题的想法,我编写了一个Historian 类,它将执行以下操作:

  • 扫描包中的 Activity 列表以查找特定 Activity 的别名。
  • 设置一个查找表,将每个别名映射到一个 Intent。
  • 提供一个startActivity()activityDestroyed()方法来做一些记账,这样查找表就可以用来根据 Intent 为正在运行的 Activity 动态分配一个别名。

这是有关如何使用它的示例:

  • 在 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()在 Activity 中实现该方法(以帮助检测两个 Intent 是否与您相同),但其余部分由 Historian 类动态处理。

我没有对代码进行详尽的测试,但它似乎在我所做的一些简单测试中运行良好。这个想法实际上与另一个问题的回答非常相似(只需要使用FLAG_ACTIVITY_CLEAR_TOP而不是FLAG_ACTIVITY_REORDER_TO_FRONT那里),但是使用activity-alias内部子类而不是内部子类可以使它更干净:)

于 2012-11-01T05:42:22.977 回答