详解Fragment的传值问题

Fragment,碎片,是Android 3.0之后加入的一个非常重要的概念。每个Fragment都有相应的Activity对它进行托管。一个Activity中可以有多个Fragment,这很自然的给大屏幕的适配提供了很便捷的方案。现在大家在开发中都必不可上的用上Fragment。本文总结了Fragment在不同情况下的传值方法,包括不同Activity下的Fragment的传值,相同Acitvity托管下不同Fragment的传值。同一界面不同Fragment传值并实时变化的情况。了解了这些,基本上Fragment的通信就不会再有问题了。接下来分部分介绍

一,不同Acitivity托管下的Frament如何传值

相信大家对不同Activity间的传值都很熟悉,其实Fragment的传值就与Acitvity一样简单。传值情况如下:


下面通过一个demo来讲解,界面很简单,firstFragment中有一个EditText,点击按钮实现将值传到secondFragment中显示在TextView上。界面的布局就不再贴出。


定义layout_main.xml如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/fragmentContain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
</FrameLayout>
很简单,用来放fragment。接下来定义供继承的frameActivity类

public abstract class SingleFragment extends FragmentActivity {
    protected  abstract Fragment createFragment();
    protected int getLayoutResId(){
        return R.layout.activity_main;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResId());
        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragmentContain);
        if(fragment  == null){
            fragment = createFragment();
            fm.beginTransaction().add(R.id.fragmentContain,fragment).commit();
        }
    }
}
相信大家都知道这是用与被Activity继承的类,把重复的代码定义在基类中,减少冗余代码,这是适配器设计模式。

接下来就完成FirstActivity,不能再简单了。

public class FirstActivity extends SingleFragment {
    @Override
    protected Fragment createFragment() {
        return new FirstFragment();
    }
}
FristFragment通过调用Fragment.startActivity(intent)将值传到SecondActivity中。具体代码如下:
public class FirstFragment extends Fragment {
    Button btn;
    EditText edit;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.firstfragment,container,false);
        initView(v);
        return v;
    }
    public void initView(View v){
        btn = (Button)v.findViewById(R.id.btn);
        edit = (EditText)v.findViewById(R.id.edit1);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(),SecondActivity.class);
                intent.putExtra(SecondFragment.EXTRA_STRING, FirstFragment.this.edit.getText().toString());
                startActivity(intent);
                getActivity().finish();
            }
        });
    }
}
这样就把值传到了SecondActivity,其实跟Acitvity一样。接下来就是如何在SecondFragment中获得SecondActivity的值。有两种方法,第一种如下:

        String str = getActivity().getIntent().getStringExtra(EXTRA_STRING);
        txt.setText(str);
直接通过getIntent获取值,但这样做就破坏的Fragment的独立性。因为此时SecondFragment总需要被SecondActivity托管,而不能用于其他Activity中,否则就可能因获取不到intent而报错。

正常的设托管模式是Activity知道Fragment的具体情况,但Fragment不能也不应该知道Activity中的具体情况。所以一般采用以下第二种方法。

每个fragment都有一个Bundle对象。第二种方法就是把传过来的值存到bundle中。bundle可以添加argument(key-value对象),在给fragment添加bundle时要注意,Fragment.setArguments(bundle)需要在fragment创建后,添加到activity前完成。然后通过acitivity在建立fragment时传入值来实现fragment的独立性。修改SecondFragment代码如下:

public class SecondFragment extends Fragment {
    public static final String EXTRA_STRING="DATA";
    TextView txt;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.secondfragment,container,false);
        txt = (TextView)v.findViewById(R.id.txt2);
        String str = getArguments().getString(EXTRA_STRING);
        txt.setText(str);
        return v;
    }
    public static Fragment newInstance(String s){
        Bundle args = new Bundle();
        args.putString(EXTRA_STRING,s);
        SecondFragment fragment = new SecondFragment();
        fragment.setArguments(args);
        return fragment;
    }
}
然后在SecondActivity中将值传入

public class SecondActivity extends SingleFragment {

    @Override
    protected Fragment createFragment() {
       String str = getIntent().getStringExtra(SecondFragment.EXTRA_STRING);
        return SecondFragment.newInstance(str);
    }
}
就可以发现传值成功了。
二,相同activity托管的两个Fragment传值问题。

在上面的例子上进行修改,在SecondFragment加入一个按钮打开一个dialogFragment,传值进去,dialog销毁后返回值。


在SecondFragment中打开dialogFragment,代码修改如下:

 btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager fm = getActivity().getSupportFragmentManager();
                MyDialog myDialog = (MyDialog) MyDialog.newInstance(SecondFragment.this.txt.getText().toString());
                myDialog.setTargetFragment(SecondFragment.this,0);
                myDialog.show(fm,"Data");
            }
        });
最主要的是在把SecondFragment设为是myDialog的目标Fragment.使两者建立联系,这样目标Fragment就交给了FragmentManage管理,方便之后获取目标Fragment.
接下来是完成dialogFragment,代码如下:

public class MyDialog extends DialogFragment {
    public static final String EXTRADATA = "DIALOG";
    EditText dialogEdit;
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        View v = getActivity().getLayoutInflater().inflate(R.layout.dialogfragment,null);
        dialogEdit= (EditText)v.findViewById(R.id.edit);
        dialogEdit.setText(getArguments().getString(EXTRADATA));
        return new AlertDialog.Builder(getActivity()).setTitle("").setView(v)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        sendResult(Activity.RESULT_OK);
                    }
                }).create();
    }
    public static Fragment newInstance(String s){
        Bundle args = new Bundle();
        args.putString(EXTRADATA,s);
        MyDialog fragment = new MyDialog();
        fragment.setArguments(args);
        return fragment;
    }
    public void sendResult(int s){
        if(getTargetFragment() == null){
            return;
        }else{
            Intent i = new Intent();
            i.putExtra(EXTRADATA,dialogEdit.getText().toString());
            getTargetFragment().onActivityResult(getTargetRequestCode(),s,i);
        }
    }
}
newInstance不用讲,跟上面的原理一样。主要是sendResult,在sendResult中调用父Fragment的回调方法将修改后的值返回到父Fragment中。

最后在SecondFragment中添加回调方法处理返回值。

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
       if(resultCode != Activity.RESULT_OK){
           return;
       }else{
           String str = data.getStringExtra(MyDialog.EXTRADATA);
           txt.setText(str);
       }
    }
就完成了同个Activity托管的不同Fragment间的传值。
三,同一界面修改一Fragment另一个Fragment实时改变
现在有这样一个需求,一个界面同时有两个Fragment,右边的Fragment里的EditText变化会引起左边的实时变化。修改上面的例子,在FirstFragment中添加一个按钮打开一个新的界面,实现上面的功能。


首先分析下思路。其实最简单的做法就是在EditText的Fragment中监听edit的变化,然后直接创建FragmentManger,获得另一个Fragment并动态的改变其中的内容。这是最直接最简单的方法,但是还是那句话,Fragment的独立性很重要,如果这样做就要求EditText的Fragment知道TextView所在的Fragment的相关细节。最好的方法就是用回调。

新建一个thirdfragment.xml文件。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:orientation="horizontal">
    <FrameLayout
        android:id="@+id/frame1"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"/>
    <FrameLayout
        android:id="@+id/frame2"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"/>
</LinearLayout>
新建一个ForthFragment,它的布局文件就只需要一个EditText。

public class ForthFragment extends Fragment{
    EditText edit;
    private Callbacks mCallbacks;
    public interface Callbacks{
        void onChangeText(String s);
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fourfragment,container,false);
        initView(v);
        return v;
    }
    public void initView(View v){
        edit = (EditText)v.findViewById(R.id.edit1);
        edit.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                String str = edit.getText().toString();
                mCallbacks.onChangeText(str);
            }
            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mCallbacks = (Callbacks)activity;
    }
}
在fragment中定义了回调接口,回调接口定义了fragment委托给托管activity处理的工作。任何托管这个fragment都要实现这个接口。在onAttach方法中,将activity强制转换成callbacks并赋值给Callbacks变量。这样在onTextChange中调用接口的onChangeText()相当于在Activity中调用。接下来看看托管两个Fragment的Activity。

public class ThirdActivity extends FragmentActivity implements ForthFragment.Callbacks {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.thirdfragment);
        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment1 = fm.findFragmentById(R.id.frame1);
        Fragment fragment2 = fm.findFragmentById(R.id.frame2);
        if(fragment1 == null){
            fragment1 = new ForthFragment();
            fm.beginTransaction().add(R.id.frame1,fragment1).commit();
        }
        if(fragment2 == null){
            fragment2 = new FiveFragment();
            fm.beginTransaction().add(R.id.frame2,fragment2).commit();
        }
    }

    @Override
    public void onChangeText(String s) {
        FragmentManager fm = getSupportFragmentManager();
        FiveFragment listFragment= (FiveFragment)fm.findFragmentById(R.id.frame2);
        listFragment.update(s);
    }
}

代码很简单,分两次加载不同的Fragment。并实现回调接口。在接口中获得另一个fragment调用其update()方法。最后就只剩下在FiveFragment中实现update()了。

public class FiveFragment extends Fragment {
    TextView txt;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fivefragment,container,false);
        txt = (TextView)v.findViewById(R.id.txt2);

        return v;
    }
    public void update(String str){
        txt.setText("数据是:"+str);
    }
}
        }
    }

    @Override
    public void onChangeText(String s) {
        FragmentManager fm = getSupportFragmentManager();
        FiveFragment listFragment= (FiveFragment)fm.findFragmentById(R.id.frame2);
        listFragment.update(s);
    }
}
这样就实现了界面的实时变化。

到这里fragment的几种传值方式就讲完了,demo做的很简单,只是为了讲解用,但再复杂的传值也是基于这些方式。就讲到这吧。

代码下载








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值