Android—— Fragment使用浅析(一)

1. 基本概述

  • Fragment是一种可以嵌入在活动中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其看成一个小型Activity,又称作Activity片段
  • 使用Fragment可以把屏幕划分成几块,然后进行分组,进行一个模块化管理。Fragment不能够单独使用,需要嵌套在Activity中使用,其生命周期也受到宿主Activity的生命周期的影响。

官方的定义如下:

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

从官方的定义可得:

  • Fragment是依赖于Activity的,不能独立存在的。
  • 一个Activity里可以有多个Fragment。
  • 一个Fragment可以被多个Activity重用。
  • Fragment有自己的生命周期,并能接收输入事件。
  • 我们能在Activity运行时动态地添加或删除Fragment

Fragment的优势

  • 模块化(Modularity):我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中。
  • 可重用(Reusability):多个Activity可以重用一个Fragment。
  • 可适配(Adaptability):根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,这样用户体验更好。

在这里插入图片描述

1.1 Fragment生命周期

在这里插入图片描述
在这里插入图片描述
简单解释:

  • onAttach()`:Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

Fragment生命周期会经历:运行、暂停、停止、销毁。

  • 运行状态:碎片可见时,关联活动处于运行状态,其也为运行状态
  • 暂停状态:活动进入暂停状态,相关联可见碎片就会进入暂停状态
  • 停止状态:活动进入停止状态,相关联碎片就会进入停止状态,或者通过FragmentTransaction的remove()replace()方法将碎片从从活动中移除,但如果在事务提交之前调用addToBackStack()方法,这时的碎片也会进入到停止状态。
  • 销毁状态:当活动被销毁,相关联碎片进入销毁状态。或者调用FragmentTransaction的remove()replace()方法将碎片从活动中移除,但在事务提交之前并没有调用addToBackStack()方法,碎片也会进入到销毁状态。

1.7 核心类与几个扩展子类

核心类

  • Fragment:Fragment的基类,任何创建的Fragment都需要继承该类。
  • FragmentManager:管理和维护Fragment。他是抽象类,具体的实现类是FragmentManagerImpl。
  • FragmentTransaction:对Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是BackStackRecord。

扩展子类

  • 对话框:DialogFragment
  • 列表:ListFragment
  • 选项设置:PreferenceFragment
  • WebView界面:WebViewFragment

备注:开发Fragment不建议使用android.app下的Fragment而应是android:support.v4.app,因为support库是不断更新的。

2. 基本使用

2.1 静态加载Fragment

其实现流程如下:
在这里插入图片描述
静态加载Fragment最大的缺点是一旦添加就不能在运行时删除

简单使用示例

  • 定义Fragment的布局,新建left_fragment.xml和right_fragment.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:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Button" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#00ff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="this is Fragment" />

</LinearLayout>
  • 自定义Fragment类,继承Fragment或其子类,重写onCreateView(),在方法中调用inflater.inflate()方法加载Fragment布局文件,接着返回加载的view对象
public class LeftFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment, container,false);
        return view;
    }

}
public class RigthFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }
}
  • 在需要加载Fragment的Activity对应的布局文件中添加framgnt标签,name属性是全限定类名,包含包名
<fragment
    android:id="@+id/left_fragment"
    android:name="com.vivo.a11085273.secondfragmenttest.LeftFragment"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1" />
<fragment
    android:id="@+id/right_fragment"
    android:name="com.vivo.a11085273.secondfragmenttest.RigthFragment"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    />
  • Activity在onCreate()方法中调用setContentView()加载布局文件即可

2.2 动态加载Fragment

简单流程如下:
在这里插入图片描述
备注:上图中的第一步getFragmentManager()替换为getSupportFragmentManager()

  • 新建another_right_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ffff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="This is the another right fragment"/>

</LinearLayout>
  • 新建AnotherRightFragment
public class AnotherRightFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.another_right_fragment, container, false);
        return view;
    }
}
  • 修改前述的activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.vivo.a11085273.secondfragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
    <FrameLayout
        android:id="@+id/right_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />
</LinearLayout>
  • 在代码中向FrameLayout里添加内容,修改MainActivity中代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        replaceFragment(new RigthFragment());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                replaceFragment(new AnotherRightFragment());
                break;
            default:
                break;
        }
    }

    private void replaceFragment(Fragment fragment) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();	// 开启一个事务
        transaction.replace(R.id.right_layout, fragment);
        transaction.commit();
    }
}

2.3 Fragment使用注意点

、Fragment有很多可以复写的方法,其中最常用的就是onCreateView(),该方法返回Fragment的UI布局,需要注意的是inflate()的第三个参数是false,因为在Fragment内部实现中,会把该布局添加到container中,如果设为true,那么就会重复做两次添加,则会抛如下异常

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

、如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据

我们可以在Fragment的onAttach()中通过getArguments()获得传进来的参数,并在之后使用这些参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象。

public class Fragment1 extends Fragment{
    private static String ARG_PARAM = "param_key";
    private String mParam;
    private Activity mActivity;
    public void onAttach(Context context) {
        mActivity = (Activity) context;
        mParam = getArguments().getString(ARG_PARAM);  //获取参数
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_1, container, false);
        TextView view = root.findViewById(R.id.text);
        view.setText(mParam);
        return root;
    }
    public static Fragment1 newInstance(String str) {
        Fragment1 frag = new Fragment1();
        Bundle bundle = new Bundle();
        bundle.putString(ARG_PARAM, str);
        fragment.setArguments(bundle);   //设置参数
        return fragment;
    }
}

,在动态加载Fragment中,FragmentTransaction 类提供了方法来完成添加、删除、替换片段等操作。一旦完成了操作,调用 FragmentTransaction.commit()方法来提交修改

  • transaction.add():往Activity里面添加一个片段
    该方法有三个参数,第一个用于指定要添加的视图ID,第三个是自定义标签,可用于方法Fragment findFragmentByTag(String tag) 来获取片段,然后也有不需要指定tag的重载方法来添加片段。
  • transaction.remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
  • transaction.replace():使用另一个Fragment替换当前的,实际上是remove()然后add()的合体
  • transaction.hide():隐藏当前Fragment,仅不可见,不会销毁
  • transaction.show():显示之前隐藏的Fragment
  • detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护
  • attach():重建view视图,附加到UI上并显示。

commit方法一定要在Activity.onSaveInstance()之前调用
commit()操作是异步的,内部通过mManager.enqueueAction()加入处理队列。对应的同步方法为commitNow()commit()内部会有checkStateLoss()操作,如果开发人员使用不当(比如commit()操作在onSaveInstanceState()之后),可能会抛出异常,而commitAllowingStateLoss()方法则是不会抛出异常版本的commit()方法,但是尽量使用commit(),而不要使用commitAllowingStateLoss()。

,FragmentManager拥有回退栈(BackStack),类似于Activity的任务栈,如果添加了该语句,就把该事务加入回退栈,当用户点击返回按钮,会回退该事务(回退指的是如果事务是add(frag1),那么回退操作就是remove(frag1));如果没添加该语句,用户点击返回按钮会直接销毁Activity

,Fragment有个常见的异常:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

该异常出现的原因是:commit()onSaveInstanceState()后调用。首先,onSaveInstanceState()在onPause()之后,onStop()之前调用。onRestoreInstanceState()在onStart()之后,onResume()之前。

因此避免出现该异常的方案有:

  • 不要把Fragment事务放在异步线程的回调中,比如不要把Fragment事务放在AsyncTask的onPostExecute(),因此onPostExecute()可能会在onSaveInstanceState()之后执行。
  • 逼不得已时使用commitAllowingStateLoss()

2.4 Activity与Fragment联动执行生命周期顺序

我们这里举个例子来理解Fragment生命周期方法。功能如下:共有两个Fragment:F1和F2,F1在初始化时就加入Activity,点击F1中的按钮调用replace替换为F2。
在查看日志之前,看一下github关于二者生命周期的神图:
在这里插入图片描述
其图片地址为github上生命周期神图
完整的Logcat可以参见:Fragment生命周期
当Frgment在Activity的onCreate()中被添加时,生命周期如下:

1.Activity super.onCreate执行完毕.

  • Fragment onAttach
  • Fragment onCreate
  • Fragment onCreateView
  • Fragment onViewCreated

2.Activity super.onStart执行中…

  • Fragment onActivityCreated
  • Fragment onViewStateRestored
  • Fragment onStart
    2.Activity super.onStart执行完毕
    3.Activity super.onPostCreate执行完毕
    4.Activity super.onResume执行完毕
    5.Activity super.onPostResume执行中…
    5.Activity super.onResumeFragments执行中…
  • Fragment onResume
    5.Activity super.onResumeFragments执行完毕
    5.Activity super.onPosResume执行完毕
    6.Activity onAttachedToWindow执行完毕
    7.Activity onCreateOptionsMenu执行完毕
  • Fragment onCreateOptionsMenu
    8.Activity onPrepareOptionsMenu执行完毕
  • Fragment onPrepareOptionsMenu
    9.Activity onWindowFocusChanged执行完毕

2.暂停的生命周期
1.Activity super.onPause执行中…

  • Fragment.onPause
    1.Activity super.onPause执行完毕
    2.Activity super.onSaveInstanceState执行…
  • Fragment onSaveInstanceState
    2.Activity super.onSaveInstanceState执行完毕
    3.Activity super.onStop执行…
  • Fragment onStop
    3.Activity super.onStop执行完毕

3.销毁的生命周期
1.Activity super.onDestroy执行中…

  • Fragment onDestroyView
  • Fragment onDestroy
  • Fragment onDetach
    Activity super.onDestroy执行完毕

4.重启的生命周期
1.Activity super.onRestart执行完毕
2.Activity super.onStart执行中…

  • Fragment onStart
    2.Activity super.onStart执行完毕
    3.Activity super.onResume执行完毕
    4.Activity super.onPostResume执行中…
    5.Activity super.onResumeFragments执行中…
  • Fragment onResume
    5.Activity super.onResumeFragments执行完毕
    4.Activity super.onPosResume执行完毕
    6.Activity onWindowFocusChanged执行完毕

另添加到ViewPager中的Fragment的调用,默认初始化三个
每一次可见与不可见都会调用setUserVisibleHint;

初始化时:setUserVisibleHint -->onResume

静态加载Fragment生命周期总结

  • 可以看出在静态加载Fragment时,加载时

    FragmentonAttach()->onCreate()->onCreateView()-都是在Activity的onStart()之前调用的。

    FragmentonActivityCreated()->onStart()都是在Activity的super.onStart()执行中调用的

    FragmentonResume()是在Activity的onResume()之后调用

  • 暂停生命周期时

    FragmentonPause()是在Activity的super.onPause()执行中调用的。

    FragmentonStop()是在Activity的super.onStop()执行期间调用

  • 销毁生命周期

    FragmentonDestroyView()->onDestroy()->onDetach() 是在Activity的super.onDestroy()执行期间调用

  • 重启生命周期

    FragmentonStart()在Activity的super.onStart()执行期间调用,Fragment的onResume()在Activity的onResume()执行之后调用

3. Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。
一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

示例:

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout, fragment);
    transaction.addToBackStack(null);	//添加进回退栈
    transaction.commit();
}

replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用transaction.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryViewonCreateView

如果不希望视图重绘,可以将原来的Fragment视图隐藏

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.hide(this);
    transaction.add(R.id.right_layout, fragment);
    transaction.addToBackStack(null);	//添加进回退栈
    transaction.commit();
}

3. Fragment与Activity交互

在这里插入图片描述
Fragment与Activity的通信大致归纳如下

  • 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
  • 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作
  • 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

备注:如果在Fragment中需要Context,可以通过getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity.getApplicationContext();

需要考虑到Fragment的重复使用,需降低Fragment与Activity的耦合,且Fragment不应该直接操作别的Fragment,Fragment操作应由它的管理者Activity决定

Activity传递数据给Fragment

  • 在Activity中创建Bundle数据包,调用Fragment实例的setArguments(),将Bundle数据包传给Fragment
  • Fragment调用getArguments()获得Bundle对象,然后进行解析就可以
//创建Fragment对象,并通过Bundle对象传递值(在onCreate方法中)
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("key", values);
fragment.setArguments(bundle);
//(在Fragment类中的onCreateView方法中)
Bundle bundle = this.getArguments();
    if (bundle != null)
    {
        String str = bundle.getString("key");
    }
    TextView textView = new TextView(getActivity());
    textView.setText("上上下下的享受");//是电梯,别误会
  • Fragment传递数据给Activity:在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口,Fragment你可以通过回调接口传数据。

    • 首先在Fragment中定义一个回调接口(接口中定义抽象方法,要传什么类型数据,参数设为何种类型)
    /*接口*/  
    public interface Mylistener{
        public void thanks(String code);
    }
    
    • 定义该接口(在Fragment类中)
    private Mylistener listener;
    
    • 在onAttach方法中,将定义的该接口强转为activity类型便可与Activity有交际
    @Override
        public void onAttach(Activity activity) {
            // TODO Auto-generated method stub
            listener=(Mylistener) activity;
            super.onAttach(activity);
        }
    
    • 在Activity中只需要实现该接口并重写里面的方法即可
    @Override
        public void thanks(String code) {
            // TODO Auto-generated method stub
            Toast.makeText(this, "已收到Fragment的消息:--"+code+"--,客气了", Toast.LENGTH_SHORT).show();;
        }
    

除了使用接口回调,比较常用的方法是用EventBus进行交互通信,可以查看EventBus的使用

4. Fragment之间的交互

4.1 利用setArguments()

Fragment之间可以用setArguments来传递数据:

示例:

  • 在Fragment B中新建一个函数:newInstance(String text)接收传过来的参数
    public static Fragment2 newInstance(String text) {
        Fragment2 fragment = new Fragment2();
        Bundle args = new Bundle();
        args.putString("param", text);
        fragment.setArguments(args);
        return fragment;
    }
  • 在Fragment B的onCreateView从获取参数
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view =  inflater.inflate(R.layout.fragment2, container, false);
        if (getArguments() != null) {
            String mParam1 = getArguments().getString("param");
            TextView tv =  (TextView)view.findViewById(R.id.textview);
            tv.setText(mParam1);
        }
        return view;
    }
  • 在Fragment A中,调起Fragment B时,通过调用newInstance函数获取实例并传递参数:
public class Fragment1 extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment1, container, false);
        Button btn = (Button)view.findViewById(R.id.load_fragment2_btn);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(final View view) {
                Fragment2 fragment2 = Fragment2.newInstance("从Fragment1传来的参数");
 
                FragmentTransaction transaction = getFragmentManager().beginTransaction();
                transaction.add(R.id.main_layout, fragment2);
                transaction.addToBackStack(null);
                transaction.commit();
            }
        });
        return view;
    }
}

4.2 同一Activity不同Container的Fragment间交互

效果图如下:
在这里插入图片描述

  • 方法一:直接在Activity中操作
    直接在Activity中找到对应控件的实例,然后直接操控即可
    这里比较简单,不过这样做使用Fragmnt没有什么用处,每个Fragment应该负责自己控件操作

  • 方法二:直接在Fragment中操作
    这里主要涉及两个问题:一是如何获取自己控件的引用;而是如何获取其他fragment页控件的引用

    • 首先获取自己控件引用
      一种方法是在onCreateView()中获取,例如:
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment1, container, false);
        listView = (ListView)rootView.findViewById(R.id.list);//获取自己视图里的控件引用,方法一
        return rootView;
    }
    

    由于在onCreateView()中,还没有创建视图,所以在这里如果使用getView()方法将返回空。所以如果要获取其实图中指定控件的引用,只用用inflater.inflate()返回的rootView;在这个rootView()中用findViewById来查找

    另一种方法是在onActivityCreated()中获取:onActivityCreated()回调会在Activity的OnCreate()执行完成后再执行,则Activity和各个Fragment视图也都创建完成。

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
     
        listView = (ListView) getView().findViewById(R.id.list);//获取自己视图里的控件引用,方法二
    } 
    
    • 获得其他Fragment页面控件引用方法
      要获取Activity中的资源,就必须等Acitivity创建完成以后,所以必须放在onActivityCreated()回调函数中
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
     
        mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);//获取其它fragment中的控件引用的唯一方法!!!
     
    }
    

    总的实现方法如下:

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
     
        mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);//获取其它fragment中的控件引用的唯一方法!!!
        listView = (ListView) getView().findViewById(R.id.list);//获取自己视图里的控件引用,方法二
     
        ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings);
        listView.setAdapter(arrayAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String str = mStrings[position];
                mFragment2_tv.setText(str);
            }
        });
    }
    
  • 方法三:在各自Fragment中操作
    方法二中直接在Fragment A 中操作了Fragment B,这样违背了模块分离思想,应该各自处理各自代码,通过Activity将其分离。
    在Activity中可以直接通过FragmentManager:findFragmentById()获取Fragment实例

方法三简单示例

  • Fragment2设置textView函数:
public class Fragment2 extends Fragment {
    private TextView mTv;
    …………
    public void setText(String text) {
        mTv.setText(text);
    }
}
  • Fragment1中处理方式:

    • 定义接口与变量
    private titleSelectInterface mSelectInterface;
     
    public interface titleSelectInterface{
        public void onTitleSelect(String title);
    }
    
    • 接口变量赋值
      接口是给Activity用,要在Activity中给这里的接口变量赋值,这里采用强转的方式,在fragment与activity关联时,进行强转赋值。
    public void onAttach(Activity activity) {
        super.onAttach(activity);
     
        try {
            mSelectInterface = (titleSelectInterface) activity;
        } catch (Exception e) {
            throw new ClassCastException(activity.toString() + "must implement OnArticleSelectedListener");
        }
    }
    

    如果activity没有implements titleSelcetInterface,就会抛出错误。

    • 调用接口变量
      在fragment1中用户点击listView的item时,将结果传回给Activity
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
     
        listView = (ListView) getView().findViewById(R.id.list);//获取自己视图里的控件引用,方法二
        ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings);
        listView.setAdapter(arrayAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String str = mStrings[position];
                mSelectInterface.onTitleSelect(str);
            }
        });
    }
    
    • 在Activity中实现titleSelectInterface接口
      首先Activity必须实现titleSelectInterface接口,然后结果会在onTitleSelect(String title) 中返回,在结果返回后利用fragment2.setText()操作textView
    public class MainActivity extends FragmentActivity implements Fragment1.titleSelectInterface {
     
        ……
        
        @Override
        public void onTitleSelect(String title) {
            FragmentManager manager = getSupportFragmentManager();
            Fragment2 fragment2 = (Fragment2)manager.findFragmentById(R.id.fragment2);
            fragment2.setText(title);
        }
    }
    

猜你喜欢

转载自blog.csdn.net/weixin_43499030/article/details/90057140