Android里Fragment的相关知识(二)

我们在上一篇文章《Android里Fragment的相关知识(一)》中介绍了Fragment的存在意义以及基本使用。本篇文章将进一步介绍Fragment的其它一些相关知识。

Fragment与Actvitiy之间的交互

一般情况下,因为要考虑Fragment的复用性,所以都要尽量地减少Fragment与Actvitity的耦合。至于两个Fragment之间直接交互就更不应该了,因为这样会使得代码交差得凌乱和严重影响可维护性,失去了使用的Fragment的意义。在Fragment中可以通过getActivity()得到当前Fragment所在的Actvitiy实例。而在Actvitiy中可以通过findViewById()像正常情况下一样得到的Fragment里所有控件的View对象,而想要获得整个Fragment对象,则要通过getFragmentManager.findFragmentByTag()或者findFragmentById()来获取,获得了Fragment对象后,等于获得了Fragment所在类的对象,所以可以通过此对象可直接访问类里的public方法。例如在Activity中可以这样:

MyFragment myFragment =(MyFragment)getFragmentManager().findFragmentById(R.id.my_fragment);
myFragment.testFunction();

经典示例

新建一个title的fragment:

public class TitleFragment extends Fragmentimplements View.OnClickListener {
 
   public interface OnTitleSelectedListener {
       void onTitleSelected(CharSequence text);
    }
 
   private TextView mTxtTitle1;
   private TextView mTxtTitle2;
   private OnTitleSelectedListener mListener;
 
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_title, container, false);
 
       mTxtTitle1 = (TextView) view.findViewById(R.id.txt_title1);
       mTxtTitle1.setOnClickListener(this);
       mTxtTitle2 = (TextView) view.findViewById(R.id.txt_title2);
       mTxtTitle2.setOnClickListener(this);
 
       return view;
    }
 
   @Override
   public void onAttach(Activity activity) {
       super.onAttach(activity);
       try {
           mListener = (OnTitleSelectedListener) activity;
       } catch (ClassCastException e) {
           throw new ClassCastException(activity.toString() + " must implementOnTitleSelectedListener");
       }
    }
 
   @Override
   public void onClick(View v) {
       if (v.getId() == R.id.txt_title1) {
           mListener.onTitleSelected(mTxtTitle1.getText());
       } else if (v.getId() == R.id.txt_title2) {
           mListener.onTitleSelected(mTxtTitle2.getText());
       }
    }
}

<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="40dp"
   android:background="@color/title"
   android:orientation="horizontal">
 
   <TextView
       android:id="@+id/txt_title1"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:gravity="center"
       android:text="标题1"
       android:textSize="20sp"
       android:textColor="@color/title_text"
       android:layout_weight="1"/>
 
   <TextView
       android:id="@+id/txt_title2"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:gravity="center"
       android:text="标题2"
       android:textSize="20sp"
       android:textColor="@color/title_text"
       android:layout_weight="1"/>
 
</LinearLayout>

注意OnTitleSelectedListener这个接口,它是为了让TitleFragment所在的Activity实现的事件回调,当在onClick中把TitleFragment想要传递的信息传递给Activity。TitleFragment的onAttach()回调方法(系统在向Activity 添加Fragment时调用的方法)会通过转换传递到onAttach()中的Activity来实例化 OnTitleSelectedListener的实例。

 

新建一个content的fragment:

public class ContentFragment extendsFragment {
 
   private TextView mTxtContent;
 
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_content, container,false);
 
       mTxtContent = (TextView) view.findViewById(R.id.txt_content);
       return view;
    }
 
   public void setContent(CharSequence text) {
       mTxtContent.setText(text);
    }
}
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/content">
 
    <TextView
       android:id="@+id/txt_content"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_centerInParent="true"
       android:textSize="30dp"
       android:text="我是ContentFragment" />
 
</RelativeLayout>

ContentFragment比较简单,布局就是在屏幕中间有一个TextView,而代码中定义了一个public的方法,用于所在Activity在接收到TitleFragment传递的信息后再次传递给ContentFragment,然后更新屏幕中间的TextView内容。

 

最后我们来看看Actvitity的实现:

public class MainActivity extends Activityimplements TitleFragment.OnTitleSelectedListener {
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
    }
 
   @Override
   public void onTitleSelected(CharSequence text) {
       ContentFragment myFragment =(ContentFragment)getFragmentManager().findFragmentById(R.id.fragment_content);
       myFragment.setContent(text);
    }
}
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
 
   <fragment
       android:id="@+id/fragment_title"
       android:name="project.test.com.myapplication.TitleFragment"
       android:layout_width="match_parent"
       android:layout_height="40dp" />
 
   <fragment
       android:id="@+id/fragment_content"
       android:name="project.test.com.myapplication.ContentFragment"
       android:layout_below="@id/fragment_title"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
 
</RelativeLayout>

Activity继承了TitleFragment的OnTitleSelectedListener接口,实现了onTitleSelected方法,它像一个中介的角色,将TitleFragment中的信息最终传递到ContentFragment中去。

Fragment的返回栈

Fragment与Actvitiy之间存在一个主要的区别:当Actvitiy进入后台时,会被放到backstack中。这样当用户按back按钮时,Actvitiy可以恢复。但是,Fragment在进入后台时不会被自动放到back stack中。要实现这一目的,需要在Fragment调用commit()之前调用addToBackStack()方法。这样便可使Fragment任务添加到返回栈,该返回栈由Activity管理,允许用户通过按返回按钮返回上一Fragment状态,直到所有Fragment都从回退栈中弹出,则就会退出当前Actvitity。

示例

修改MainActivity:

public class MainActivity extends Activity{
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
 
       if (savedInstanceState == null) {
           FragmentManager fragmentManager = getFragmentManager();
           FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
           fragmentTransaction.add(R.id.fragment_main, new FragmentA(),"A");
           fragmentTransaction.commit();
       }
 
       Log.d("FragmentTest", "MainActivity onCreate");
    }
 
   @Override
   protected void onDestroy() {
       super.onDestroy();
       Log.d("FragmentTest", "MainActivity onDestroy");
    }
}
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
 
   <FrameLayout
       android:id="@+id/fragment_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
 
</RelativeLayout>

MainActivity在onCreate中动态加载了FragmentA

接着新建FragmentA:

public class FragmentA extends Fragment {
 
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_a, container, false);
 
       Button btnGoto = (Button) view.findViewById(R.id.btn_goto);
       btnGoto.setOnClickListener(newView.OnClickListener() {
           @Override
           public void onClick(View v) {
                FragmentManager fragmentManager= getFragmentManager();
                FragmentTransactionfragmentTransaction = fragmentManager.beginTransaction();
               fragmentTransaction.replace(R.id.fragment_main, new FragmentB(),"B");
               fragmentTransaction.addToBackStack(null);
                fragmentTransaction.commit();
           }
       });
 
       Log.d("FragmentTest","FragmentA onCreateView");
 
       return view;
    }
 
   @Override
   public void onDestroyView() {
       super.onDestroyView();
       Log.d("FragmentTest", "FragmentA onDestroyView");
    }
}
<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/content">
 
   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="这是Fragment_A" />
 
   <Button
       android:id="@+id/btn_goto"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="跳转去B"/>
 
</LinearLayout>

FragmentA中按钮点击后跳转至FragmentB。

再接着新建FragmentB:

public class FragmentB extends Fragment {
 
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_b, container, false);
 
       Button btnGoto = (Button) view.findViewById(R.id.btn_goto);
       btnGoto.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
                FragmentManager fragmentManager= getFragmentManager();
                FragmentTransactionfragmentTransaction = fragmentManager.beginTransaction();
               //fragmentTransaction.replace(R.id.fragment_main, new FragmentC(),"C");
               fragmentTransaction.add(R.id.fragment_main , new FragmentC(),"C");
               fragmentTransaction.addToBackStack(null);
                fragmentTransaction.commit();
           }
       });
 
       Log.d("FragmentTest", "FragmentB onCreateView");
 
       return view;
    }
 
   @Override
   public void onDestroyView() {
       super.onDestroyView();
 
       Log.d("FragmentTest", "FragmentB onDestroyView");
    }
}
<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/content">
 
   <TextView
       android:layout_width="wrap_content"
        android:layout_height="wrap_content"
       android:text="这是Fragment_B" />
 
   <Button
       android:id="@+id/btn_goto"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="跳转去C"/>
 
</LinearLayout>

FragmentB中按钮点击后跳转至FragmentC。

最后加新建FragmentC:

public class FragmentC extends Fragment {
 
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_c, container, false);
 
       Log.d("FragmentTest", "FragmentC onCreateView");
 
       return view;
    }
 
   @Override
   public void onDestroyView() {
       super.onDestroyView();
 
       Log.d("FragmentTest", "FragmentC onDestroyView");
    }
}
<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/content">
 
   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="这是Fragment_C" />
 
</LinearLayout>

当程序运行后,我们可以看到日志打印如下:

FragmentTest: MainActivity onCreate

FragmentTest: FragmentA onCreateView

FragmentTest: FragmentA onDestroyView

FragmentTest: FragmentB onCreateView

FragmentTest: FragmentC onCreateView 

到了展示FragmentC后,我们点击返回按钮会返回到FragmentB,继续点击返回按钮会返回到FragmentA,再继续点击返回按钮就退出程序,日志打印如下:

FragmentTest: FragmentC onDestroyView

FragmentTest: FragmentB onDestroyView

FragmentTest: FragmentA onCreateView

FragmentTest: FragmentA onDestroyView

FragmentTest: MainActivity onDestroy 

这里来仔细看看FragmentA和FragmentB在日志上的区别,FragmentA在跳转到FragmentB时,它会调到onDestroyView,然后到最后返回到FragmentA时,也会重新调到onCreateView。而FragmentB由始到终都是只调一次onCreateView和onDestroyView。原因就是FragmentA在跳转FragmentB时使用了replace()方法。我们在上节提到,replace()是会remove()再add(),而FragmentB在跳转FragmentC时使用了add()方法,等于FragmentC只是盖在FragmentB的上面,FragmentB一直都还存在。我们在实际开始中可以根据实际情况来决定使用replace()还是add()方法。

Fragment跳转中传递参数

我们知道,一个Activity跳转到另一个Activity时,可以通过Intent的putExtra来附加参数。而Fragment也是可以做到的,实现是Fragment的setArguments方法。如上示例FragmentA跳转FragmentB,我们来修改一下代码看看。

FragmentA:

FragmentManager fragmentManager =getFragmentManager();
FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
// 增加的代码--------------------------------
Bundle bundle = new Bundle();
bundle.putString("fragmment_parameter","Hello Fragment");
FragmentB fragmentB = new FragmentB();
fragmentB.setArguments(bundle);
// 增加的代码--------------------------------
fragmentTransaction.replace(R.id.fragment_main,fragmentB, "B");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
FragmentB:

@Override
public View onCreateView(LayoutInflaterinflater, ViewGroup container, Bundle savedInstanceState) {
   View view = inflater.inflate(R.layout.fragment_b, container, false);
 
   // 增加的代码--------------------------------
   Bundle bundle = getArguments();
   if (bundle != null) {
       String parameter = bundle.getString("fragmment_parameter");
       Toast.makeText(getActivity(), parameter, Toast.LENGTH_SHORT).show();
    }
   // 增加的代码--------------------------------
   ……
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值