17

我有一个具有主/详细布局的应用程序(1 个活动、1 个 ListView 片段和 1 个详细片段)。当用户单击 ListView 中的项目时,片段事务会在右侧窗格中实例化一个详细片段,其中包括与该项目对应的信息。当显示细节片段时,我隐藏了初始操作栏按钮/项目并显示 3 个新的 AB 项目(完成/删除/取消)。用户可以通过按下后退按钮或按下 3 个 AB 项之一来清理右窗格并返回到初始 UI 状态。

我遇到的问题是,当用户选择应用程序的主页图标(即“向上导航”)时,活动会重新加载(即指示活动正在启动的动画可以被视为操作栏和UI 已重绘)。该问题仅在按下应用程序主页图标时发生。如果用户按下后退按钮或取消/完成/删除操作栏按钮,片段会简单地从右窗格中删除,并且 UI 会返回初始状态而无需任何“重新加载”。

活动的 XML 布局如下(在 LinearLayout 内;美化隐藏了该行):

<fragment class="*.*.*.ListFragment"
        android:id="@+id/titles" android:layout_weight="1"
        android:layout_width="0px"
        android:layout_height="match_parent" />

<FrameLayout android:id="@+id/details" android:layout_weight="2"
        android:layout_width="0px"
        android:layout_height="match_parent" />

DetailsFragement 在其 onCreate 方法中有 actionBar.setDisplayHomeAsUpEnabled 语句:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);


    ActionBar actionBar = getSherlockActivity().getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);

}

对于 ListView 片段和 Detail 片段,onCreateOptionsMenu() 和 onOptionsItemSelected() 方法都在片段中实现。在详细信息片段的代码下方:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.edit_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    // some variable statements...

    switch (item.getItemId()) {
        case android.R.id.home:
            //Toast.makeText(getSherlockActivity(), "Tapped home", Toast.LENGTH_SHORT).show();
            onHomeSelectedListener.onHomeSelected();
            return true;

        case R.id.menu_edit_item_done:
            editedTask.setTitle(editedTaskTitle);
            onTaskEditedListener.onTaskEdited(editedTask, UPDATE_TASK, true);
            return true;

        default:
            return super.onOptionsItemSelected(item);

    }
}

在主机活动中,我实现了 onHomeSelectedListner 来处理应用主页图标按下(即“向上导航”:

public void onHomeSelected(){

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();
    TaskFragment taskFragment = (TaskFragment)getSupportFragmentManager().findFragmentById(R.id.details);
    ft.remove(taskFragment);
    ft.commit();
    manager.popBackStack();

}

负责处理所有其他操作栏按钮(即完成/删除/取消)的活动侦听器是 onTaskEditedListener,除了处理某些数据的其他代码之外,它具有与上面所示相同的片段事务。

更新(1/24) 根据 tyczj 和 straya 的反馈,我在活动的 onCreate()、onResume()、onPause() 中放置了日志语句,以确定 onHomeSelected 和 onTaskEdited 侦听器之间的差异。我可以确认在“向上导航”事件(即 onHomeSelected)期间调用了 onPause()、onCreate() 和 onResume()。而在 onTaskEdited 调用期间(即后退按钮或完成/删除/取消按下)没有调用这些事件。

更新 (1/25) 根据 Mark Murphy 的建议,我在“case android.R.id.home”语句中注释掉了 onHomeSelected 方法调用,只是为了看看 Activity 会做什么。当时的想法是,应用程序不会做任何事情,因为 are no 语句。事实证明并非如此。即使没有调用侦听器方法(即删除片段),活动也会重新启动,并且从片段容器中删除详细片段。

更新 (2/28) 我暂时解决了我的主要活动通过禁用窗口动画重新启动的事实(如我自己的答案中突出显示的那样)。然而,通过进一步的测试,我发现了一个错误。感谢 Wolfram Rittmeyer 的示例代码,我能够找出我的活动在向上导航期间重新启动(在主/详细单一布局中)的真正原因:1)虽然我使用这个“onHomeSelectedListener”来正确删除片段从后台堆栈中,我仍然在 ListView 片段的 onOptionsItemSelected 中有一些残余代码,这些代码正在创建一个新的意图来启动托管活动。这就是为什么按下应用程序的主页图标会重新启动活动。2)在我的最终实现中(如我自己的答案所示),

4

4 回答 4

8

经过大量研究和探索,事实证明,我的活动在主/详细配置的“向上导航”期间重新启动的唯一原因是因为我在 ListView Fragment 的 onOptionsItemSelected 中留下了一些代码,该代码另外创建了启动主要活动的意图到我在别处的完整片段交易代码。下面是我在手机(多个活动)和平板电脑(单个活动/多窗格)配置上正常工作的“向上导航”的最终实现。感谢Wolfram Rittmeyer在他的代码中提供的一些提示(评论部分中的链接)帮助我查明我的问题!

主活动:托管片段并执行其他一些特定于应用程序的操作

ListView Fragment:处理表格配置中的“向上导航”

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            if(mDualPane){
                FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
                FragmentTransaction ft = manager.beginTransaction();
                DetailFragment detailFragment = (DetailFragment)manager.findFragmentById(R.id.details);
                ft.remove(detailFragment);
                ft.commit();
                manager.popBackStack();
                getSherlockActivity().getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSherlockActivity().getSupportActionBar().setHomeButtonEnabled(false);
            }
            return true;

        // Other case statements...

        default:
            return super.onOptionsItemSelected(item);
    }
}

详细信息片段:处理手机配置中的导航

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);

    // Sets "up navigation" for both phone/tablet configurations
    ActionBar actionBar = getSherlockActivity().getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {
        case android.R.id.home:
            if(!mDualPane){
                Intent parentActivityIntent = new Intent(getSherlockActivity(), MainActivity.class);
                parentActivityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(parentActivityIntent);
                getSherlockActivity().finish();
            }
            return true;

        // Other case statements...

        default:
            return super.onOptionsItemSelected(item);
    }

}
于 2013-02-27T02:40:36.537 回答
3

如果您查看导航设计模式,您会发现您希望在按下主页按钮时返回到起始活动。

因此,假设您有 2 个活动调用它们A1并且A2. 单击 A1 中的某些内容会将您带到 A2。如果用户点击主页按钮,您应该将它们返回到 A1 清除堆栈中的所有内容,直到像这样的活动

Intent intent = new Intent(this, A1.class);  
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

这就是旗帜的Intent.FLAG_ACTIVITY_CLEAR_TOP作用

如果设置,并且正在启动的活动已经在当前任务中运行,那么不会启动该活动的新实例,而是关闭它上面的所有其他活动,并且此 Intent 将被传递到(现在顶部)作为新意图的旧活动。

例如,考虑一个由以下活动组成的任务:A、B、C、D。如果 D 调用 startActivity() 的 Intent 解析为活动 B 的组件,则 C 和 D 将完成,B 接收给定的 Intent ,导致堆栈现在是:A,B。

上面示例中当前运行的活动 B 实例将在其 onNewIntent() 方法中接收您在此处启动的新意图,或者自身完成并使用新意图重新启动。如果它已声明其启动模式为“multiple”(默认)并且您没有在同一意图中设置 FLAG_ACTIVITY_SINGLE_TOP,那么它将完成并重新创建;对于所有其他启动模式,或者如果设置了 FLAG_ACTIVITY_SINGLE_TOP,则此 Intent 将被传递到当前实例的 onNewIntent()。

这种启动模式也可以与 FLAG_ACTIVITY_NEW_TASK 结合使用,效果很好:如果用于启动任务的根活动,它将将该任务的任何当前正在运行的实例带到前台,然后将其清除为根状态。这尤其有用,例如,当从通知管理器启动活动时。

于 2013-01-23T04:42:59.923 回答
0

不要:中断然后返回 super.onOptionsItemSelected(item),而只是:返回 true;

更新:

所以你说活动是根据你看到的视图发生的情况“重新启动”的,但是你能通过使用各种生命周期方法来确认活动(和片段)可能会或可能不会发生什么吗?这样,您可以在继续诊断之前确定当前(错误)行为是什么。

更新:

好的,很高兴确定行为:) 现在关于您的问题“为主/详细布局(1 个活动/2 个片段)实现“向上导航”的正确方法是什么?“:典型的方法是 2 个片段得到添加在单个 FragmentTransaction 中,您只需 popBackStack 删除它们并返回到以前的状态。我认为您通过手动删除 FragmentTransaction 中的 Fragment 然后弹出回栈来加倍。试试popBackStack。哦,为了确定和一致,因为您使用的是 ActionBarSherlock 和 support.v4,您是在使用 FragmentActivity(而不是 Activity)和 SherlockFragment 吗?

于 2013-01-23T04:46:50.993 回答
0

我认为你应该只在活动内部处理向上按钮。如果您在手机中,向上按钮将由充当该片段包装器的活动处理,在平板电脑(主/详细模式)中您无论如何都不想要它

于 2013-02-21T01:42:01.037 回答