8.自定义BACK按键

8.1 问题

应用程序要以自己的方式来处理用户按下物理BACK按键后的行为。

8.2 解决方案

(API Level 5)
可以在Activity中使用onBackPressed()回调方法或者在Fragment中操作回退栈。

8.3 实现机制

如果想要用户在你的Activity上按下BACK按键时可以得到相应通知,可以覆写onBackPressed()方法,如下所示:

    @Override
    public void onBackPressed() {
        //实现自定义返回功能

        //调用super以进行常规处理(例如销毁Activity)
        super.onBackPressed();
    }

这个方法的默认实现会将当前回退栈中的Fragment弹出并且销毁Activity。如果不打算改变这个流程,只需要确保调用父类的实现来保存这种常规的处理方式。

警告:
覆写物理按键事件时应保持慎重。在Android系统中,所有的物理按键都有一致的功能,如果这些按键的功能变化太大,会让用户感到困惑和不满。

###BACK操作和Fragment
当UI中包含Fragment时,可以进一步自定义设备的BACK按键的行为。默认情况下,在UI中添加或替换Fragment的操作并不会在任务的回退栈中添加相应的Fragment,因此当用户按下BACK按键后,并不能够回退这些动作。但是,所有的FragmentTransaction都可以作为条目通过简单地调用addToBackStack()(在事务提交前)添加到回退栈中。
默认情况下,当用户按下BACK按键后,Activity会调用FragmentManager.popBackStackImmediate(),这样每个通过这种方式添加的FragmentTransaction都会在每次点击时弹出,直到栈中一个不剩,然后Activity会被销毁。另外,这个方法还有一些变体,允许直接跳到栈中的某个位置。让我们看一下代码:

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Go Home"
        android:onClick="onHomeClick" />
    <FrameLayout
        android:id="@+id/container_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

自定义Fragment回退栈的Activity

public class MyActivity extends ActionBarActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();        
        ft.add(R.id.container_fragment, MyFragment.newInstance("First Fragment"));
        ft.commit();
        
        ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_fragment, MyFragment.newInstance("Second Fragment"));
        ft.addToBackStack("second");
        ft.commit();
        
        ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_fragment, MyFragment.newInstance("Third Fragment"));
        ft.addToBackStack("second");
        ft.commit();
        
        ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_fragment, MyFragment.newInstance("Fourth Fragment"));
        ft.addToBackStack("fourth");
        ft.commit();
    }
    
    public void onHomeClick(View v) {
        getSupportFragmentManager().popBackStack("second", FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }
    
    public static class MyFragment extends Fragment {
        private CharSequence mTitle;
        
        public static MyFragment newInstance(String title) {
            MyFragment fragment = new MyFragment();
            fragment.setTitle(title);
            
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            TextView text = new TextView(getActivity());
            text.setText(mTitle);
            
            return text;
        }
        
        public void setTitle(CharSequence title) {
            mTitle = title;
        }
    }
}

注意:
这里我们使用了支持库,允许Android3.0之前的版本使用Fragment。如果应用程序的目标API Level为11或更高版本,可以将FragmentActivity用Activity替换,将getSupportFragmentManager()用getFragmentManager()替换。

这个示例会向栈中放入4个自定义的Fragment实例,所以在应用程序运行时会显示最后添加的那个Fragment实例。对于每个事务,我们调用addToBackStack(),同时传入一个标记名称来标记这个事务。如果不想直接跳转到栈中某个位置,那么不需要执行以上操作,直接传入null即可。每次按下BACK按键后,会移除一个Fragment实例,直到只剩下第一个Fragment实例,这时再按下BACK按键,Activity就会被正常销毁。
注意,第一个事务并没有添加到栈中,这是因为我们希望第一个Fragment作为根视图。如果将它也加入到回退栈中,会导致它在Activity销毁前被弹出栈,这就使UI会有空白状态出现。
这个应用程序还要“Go Home”按钮,它可以让用户无论在哪个界面都可以立即回到根Fragment。这是通过调用FragmentManager的popBackStack()方法,同时传入希望跳转的事务的标记名称实现的。我们还传入了POP_BACK_STACK_INCLUSIVE标识来告诉管理器在栈中移除我们标识的事务。如果没有这个标识,这个示例就会跳转到第二个Fragment,而不是根视图。

注意:
Android会跳转到第一个匹配给定标记的事务。如果同一个标记被使用多次,则会跳到第一个添加此标记的事务,而不是最新的事务。

我们不能使用这个方法直接跳到根视图,因为我们无法引用那个事务在回退栈中的标记。这个方法还有一个使用唯一事务的ID(ID为FragmentTransaction的commit()方法的返回值)的版本。使用这个方法,无须包括标记也可以直接跳到根视图。

猜你喜欢

转载自blog.csdn.net/qq_41121204/article/details/82977178