Fragment里边嵌套Fragment

一、如何切换Fragment

①、了解FragmentManager FragmentManager  fm = getSupportFragmentManager();

作用:管理Fragment的显示,存储。 FragmentManger中有三个容器。

第一个用来存储,Fragment的View,并控制View的显示

第二个用来存储,Fragment本身。 第三个用来存储,Fragment的回退栈。(就是使用android的back键的时候,返回上一个Fragment)

 

②、了解FragmentTransaction的方法

FragmentTransaction transaction = fm.benginTransatcion();

//开启一个事务 transaction.add()  往FragmentManager中添加一个Fragment,

并创建Fragment的View,添加到FrameLayout中显示。

注:用代码创建Fragment本身,并不会执行Fragment的生命周期,也就是 FirstFragment fragment = new FirstFragment();

这时候是不会执行Fragment的生命周期。其生命周期是由Activity来控制的。所以后面说到的销毁Fragment,

指的并不是销毁fragment实例也就是fragment == null,而是重新执行了Fragment的生命周期。

(可以这么理解new FirstFragment()只是创造了身体,灵魂的创造需要依靠Activity)

transaction.remove()  往FragmentManager中销毁一个Fragment,并销毁Fragment的View。

如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例会完全被移出FragmentManger。

否则只销毁Fragment的视图。(同detch()) transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体。

(注:如果remove()的Fragment不存在,不会影响add操作。相当于执行了add()操作 如果replace()的Fragment与当前显示的Fragment相同,则不执行replace()操作) transaction.hide() 隐藏当前的Fragment,仅仅是将View设为不可见,并不会销毁。 transaction.show():(Fragment必须存在于FragmentManager内,才能使用) 显示之前隐藏的Fragment。

detach() 移除Fragment的View,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach() 重建view视图,附加到UI上并显示。 transatcion.commit()//提交一个事务

 

③、三种实际中切换Fragment的方法

第一种:add()+remove() = replace():销毁当前显示的Fragment,添加需要显示的Fragment。

缺点:Fragment会被重新创建,导致View重绘。View重绘的同时也表示用户之前在该View上做的操作都消失了。

优点:节省内存空间。

使用场景:不要求保留用户操作。(用户操作,比如说:ListView滑动到第5个item,但是由于View的重绘,又会回到第一个Item) 示例:

public class MainActivity extends AppCompatActivity {

private FirstFragment mFirstFragment;

private SecondFragment mSecondFragment;

@Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initWidget();   }

private void initWidget(){

//初始化

Fragment mFirstFragment = new FirstFragment();

mSecondFragment = new SecondFragment();

//首先显示

FirstFragment showFragment(mFirstFragment); }

private void showFragment(Fragment fragment){

//直接replace()就可以了,不需要先add~~~ getSupportFragmentManager().beginTransaction() .replace(R.id.main_frame,fragment) .commit(); }

/** *使用了ActionBar的menu */

@Override public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.switch_fragment,menu);

return super.onCreateOptionsMenu(menu); }

@Override public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()){

case R.id.menu_first_fragment: showFragment(mFirstFragment);

break;

case R.id.menu_second_fragment: showFragment(mSecondFragment);

break; }

return super.onOptionsItemSelected(item);

     }    }

第二种:show()+hide():将Fragment视图隐藏之后在显示。

缺点:由于Fragment只是被隐藏了,并未被销毁,所以需要占用内存空间来保存。

优点:不用重新创建Fragment 使用场景;需要保留用户数据的情形。

示例:(与上面示例不同的部分) private void initWidget(){

//初始化Fragment mFirstFragment = new FirstFragment()

; mSecondFragment = new SecondFragment();

setUpFragment(); }

private void setUpFragment(){

//首先将所有的Fragment添加到FragmentManager中

,并隐藏,只显示当前需要的

FragmentgetSupportFragmentManager().beginTransaction().add(R.id.main_frame,mFirstFragment) .add(R.id.main_frame,mSecondFragment) .hide(mSecondFragment) .commit();

//设定当前的Fragment,知道下一次hide那个

Fragment mCurrentFragment = mFirstFragment; } private void showFragment(Fragment fragment){

if (mCurrentFragment != fragment){

getSupportFragmentManager().beginTransaction() .hide(mCurrentFragment) .show(fragment) .commit();

        mCurrentFragment = fragment;//设定当前的Fragment
} }
第三种:attach()+detch():保留Fragment(就是不会重复执行Fragment的生命周期),但删除Fragment的View(就是第一种的改良版)
使用场景:如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach
  private void initWidget(){
        //初始化Fragment
        mFirstFragment = new FirstFragment();
        mSecondFragment = new SecondFragment();
        setUpFragment();
    }
 
    private void setUpFragment(){
        //首先将所有的Fragment添加到FragmentManager中,并删除View,只显示当前需要的Fragment
        getSupportFragmentManager().beginTransaction()
                .add(R.id.main_frame,mFirstFragment)
                .add(R.id.main_frame,mSecondFragment)
                .detach(mSecondFragment)
                .commit();
        //设定当前的Fragment,知道下一次hide那个Fragment
        mCurrentFragment = mFirstFragment;
    }
 
 
    private void showFragment(Fragment fragment){
        if (mCurrentFragment != fragment){
            getSupportFragmentManager().beginTransaction()
                    .detach(mCurrentFragment)
                    .attach(fragment)
                    .commit();
            mCurrentFragment = fragment;//设定当前的Fragment
        }
    }
形式上跟第二种方法差不多~~~~,但是内容上却是第一种方法的改进
二、如何保证不发生Fragment重影
①、发生重影的原理:当将app缩小到后台,由于资源回收,系统会回收后台的资源(比如说Activity),并会调用onSaveInstanceState()保存当前状态。然后当app返回到前台的时候,系统会重建被被回收的资源。系统会将onSaveInstanceState()保存的Bundle对象传递给当前重建的Activity。就是onCreate(Bundle saveInstance)中的saveInstance参数。也会调用onRestoreInstanceState(Bundle saveInstance)来接收bundle对象。(详细请参考异常生命周期下的资源重建)

举例说明:
有ABC三个Fragment在FragmentManager中,且A正在显示在Activity上。然后将app切换到后台,Activity被回收了。FramgentManager就会调用onSaveInstanceState()方法保存ABC三个Fragment到Bundle中。再重新将app切换到前台,然后Activity进行。FragmentManager就会在onCreatre()前将ABC三个Fragment加入到FragmentManager中,并将A显示到Activity上。(未查过源码,有一种说法是在onAttach()方法中建立的。)然后Activity继续调用OnCreate()方法就如刚才的示例方法,再次调用setUpFragment()再次添加ABC到FragmentManager中,并将A显示。这就发生了重影。()
②、解决办法(推荐使用第一种)
第一种:(如果FragmentManger已经存在要创建的Fragment,那么直接通过Tag从FragmentManager获取需要的Fragment实例)
  private static final String TAG_FIRST = "FirstFragment";
    private static final String TAG_SECOND = "SecondFragment";
 
    private FirstFragment mFirstFragment;
    private SecondFragment mSecondFragment;
 
    private Fragment mCurrentFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWidget(savedInstanceState);
 
    }
 
    private void initWidget(Bundle savedInstanceState){
        if(savedInstanceState ==null){
            //初始化Fragment
            mFirstFragment = new FirstFragment();
            mSecondFragment = new SecondFragment();
            setUpFragment();
        }
        else {
            //由于FragmentManager已经存在了这些Fragment直接获取就可以了
            mFirstFragment = (FirstFragment) getSupportFragmentManager()
                    .findFragmentByTag(TAG_FIRST);
            mSecondFragment = (SecondFragment) getSupportFragmentManager()
                    .findFragmentByTag(TAG_SECOND);
        }
 
    }
 
    private void setUpFragment(){
        //第一步:为每个Fragment添加Tag
        getSupportFragmentManager().beginTransaction()
                .add(R.id.main_frame,mFirstFragment,TAG_FIRST)
                .add(R.id.main_frame,mSecondFragment,TAG_SECOND)
                .detach(mSecondFragment)
                .commit();
        //设定当前的Fragment,知道下一次hide那个Fragment
        mCurrentFragment = mFirstFragment;
    }
第二种:(阻止FragmentManager调用onSaveInstanceState()保存Fragment)
Activity 中的 onSaveInstanceState() 里面有一句super.onSaveInstanceState(outState);,Google 对于这句话的解释是 “Always call the superclass so it can save the view hierarchy state”,大概意思是“总是执行这句代码来调用父类去保存视图层的状态”。通过注释掉这句话,这样主 Activity 因为种种原因被回收的时候就不会保存之前的 fragment state,也可以成功解决重叠的问题。
三、Fragment与Activity的交互
①、Fragment中调用Activity的方法

错误的使用方法:

在MainActivity添加获取Data的方法

    /**
     * 获取数据
     */
    public int getData(){
        return 100;
    }
在FirstFragment直接调用该方法
//在FirstFragment中 
   @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //直接调用Activity的方法
        int data = ((MainActivity)getActivity()).getData();
        Log.d("TAG",data+" ");
    }
这样使用的缺点:该Fragment被MainActivity捆绑了。也就是说只能被MainActivity使用,不能被其他Activity使用了。如果有TestActivity使用FirstFragment,就会报错,因为((MainActivity)getActivity()).getData();这行代码不成立。


最佳实践:(通过使用监听器,实现Activity与Fragment之间的解耦)


public class FirstFragment extends Fragment {
    private OnFirstFragmentListener mListener;
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        //第二步:将Activity转换成监听器
        try {
            mListener = (OnFirstFragmentListener) getActivity();
            Log.d("Tag",mListener.getData()+"");
        }catch (ClassCastException e){
            throw new ClassCastException("must implement OnFirstFragmentListener");
        }
    }
 
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_first,container,false);
        return view;
    }
 
    //第一步:创建监听器
    public interface OnFirstFragmentListener{
        int getData();
    }
}
之后Activity实现接口:

public class MainActivity extends AppCompatActivity implements FirstFragment.OnFirstFragmentListener {
    public int getData(){
        return 100;
    }
}


②、Activity调用Fragment
1、向Fragment传递初始化Fragment所必要的数据

需求:有时候我们需要根据Activity掌握的数据创建Fragment。(MainActivity传递文章的id给ArticleActivity,ArticleActivity又需要将id传递给ArticleFragment,然后让ArticleFragment通过加载数据)

最佳实践:()

为Fragment设置创建Fragment的方法,并使用Bundle传递数据,使用getArgument获取数据。


public class FirstFragment extends Fragment{ 
private static final String ARGS_ID = "id";
  @Override  public void onCreate(@Nullable Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);    
  //获取数据     
  Bundle bundle = getArguments();    
  int article_id = bundle.getInt(ARGS_ID);  
    Log.d("TAG",article_id+"");  } 

public static FirstFragment newInstance(int id) { 
Bundle args = new Bundle(); 
args.putInt(ARGS_ID,id);
 FirstFragment fragment = new FirstFragment(); 
fragment.setArguments(args); return fragment; }} 

2、Activity与Fragment之间的交互 因为Activity掌握了Fragment的实例,所以直接通过调用Fragment的public方法就可以了。
 ③、Fragment与Fragment之间的交互 在Activity中,通过Fragment setTargetFragment(Fragment fragment,int flag);
将需要进行交互的Fragment传递给当前Fragment。(int flag为传递的Fragment设定id)
 例: public class MainActivity extends AppCompatActivity{ @Override protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main)
 mFirstFragment = new FirstFragment(); 
mSecondFragment = new SecondFragment();  
//将SecondFragment的实例传递给
FirstFragment mFirstFragment.setTargetFragment(mSecondFragment,0); } }
 然后在FirstFragment中获取SecondFragment:
 public class FirstFragment extends Fragment {
 @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); 
//首先判断获取的是哪个Fragment switch (getTargetRequestCode()){
 case 0: SecondFragment secondFragment = (SecondFragment) getTargetFragment(); 
//...之后进行交互的操作 break; } } } 
四、Fragment的回退栈  回退栈的作用: 如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
 如何添加回退栈:addBackTo(String name);
//name表示该Fragment在栈中的标识,不需要可以设置为null 代码:
 private void showFragment(Fragment fragment){ 
if (mCurrentFragment != fragment){
 getSupportFragmentManager().beginTransaction() .detach(mCurrentFragment) .attach(fragment) .addToBackStack(null)
//将当前framgent加入回退栈 
.commit(); mCurrentFragment = fragment;//设定当前的Fragment 
} 
  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值