Android Fragment之间的数据交流

作者: 醉破方知酒有权 | 来源:发表于2017-12-26 16:45 被阅读0次
    Android Fragment Argument

    众所周知,多用Fragment能打造更灵活的程序。
    本文通过一个浅显的例子,来阐释fragment之间基于Argument的数据交流。

    简单说一下要实现的目标:
    本项目包含两个活动和分别依附于这两个活动的两个Fragment。
    简单起见,这里分别为他们起名为:FirstActivityFirstFragmentSecondActivitySecondFragment
    他们之间的关系是:
    两个活动只负责容纳(或者说托管)其对应的两个Fragment。而具体的显示和与用户交互则由Fragment负责。

    为了突出重点,这里只实现最简单的功能:

    • FirstFragment中显示一个ListView,这个ListView显示一串编程语言的名称。
    • 当用户点击其中的item时,会跳转到SecondActivity
    • 这时SecondActivityonCreate()方法启动,在其中加载SecondFragment
    • 最后SecondFragmentTextView控件根据传过来的信息显示相应的编程语言的名字。

    如图:


    就是这个意思

    在代码中实现时,FirstActivitySecondActivity甚至都不需要对应的Layout资源文件。因为它们唯一的作用只是为Fragment提供容器,所以这里只需要在java代码中为两个Activity设置contentView即可:

    setContentView(R.layout.common_fragment_container);
    

    这个名为common_fragment_container的布局文件提供了一个FrameLayout来作为Fragment的容器:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

    根据我们的构想,当用户点击FirstFragment中的ListView的item时,应该跳转到SecondActivity
    为此,我们在SecondActivity中定义静态方法:

    
        private final static String 
        EXTRA_LANGUAGE_PICKED = "language_picked"; //键
    
        
    //静态方法,提供从别的活动跳转到SecondActivity
    public static Intent newIntent(Context packageContext, String languagePicked) {
            Intent intent = new Intent(packageContext, SecondActivity.class);
            intent.putExtra(EXTRA_LANGUAGE_PICKED, languagePicked);
            return intent;
        }
    

    FirstFragment中ListView item的点击回调:

    public class FirstFragment extends Fragment {
    
        ListView mList;
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragment_first, container, false);
            mList = v.findViewById(R.id.list);
    //点击回调
            mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Resources resources = getResources();
    //得到资源文件中定义的字符串数组
                    String[] languages = 
    resources.getStringArray(R.array.languages);
                    String str = languages[position];
                    Intent intent = SecondActivity.newIntent(
    getActivity(), str);
    //启动SecondActivity
                    startActivity(intent);
                }
            });
            return v;
    
        }
    }
    

    FirstFragment通过startActivity(intent)启动SecondActivity之后。
    SecondActivity并不直接与用户交互。

    它要做的是:

    • 将传入的intent中的用户点击的编程语言名称取出来;
    • 然后传给SecondFragment。由SecondFragment将它显示出来。

    SecondFragment想从SecondActivity那儿取到数据有两种方式:

    第一种比较直接:

    SecondFragment简单粗暴地通过getActivity()方法得到托管自己的SecondActivity
    然后通过getIntent()方法得到从FirstFragment中传过来的Intent对象;
    最后得到其中的extra信息。

    这种方式虽然简单,但也有代价。那就是破坏了封装。使得SecondFragment不能被复用。因为此时它还承担了的工作。

    第二种方式比较复杂,但也更灵活:附加argument给Fragment:

    要附加argument给Fragment,需要调用Fragment.setArguments(Bundle)方法。而且必须是在fragment创建后,添加给Activity之前。
    因此,一般的惯用做法是在Fragment类中添加newInstance()静态方法。
    通过这个方法完成fragment实例以及Bundle对象的创建,
    最后再把argument放入bundle对象中,并附加给fragment:

    //SecondFragment
    public class SecondFragment extends Fragment {
    
    private static final String 
    ARG_LANGUAGE_PICKED = "arg_language_picked";
    
        TextView mText;
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            
            View v = inflater.inflate(R.layout.fragment_second, container, false);
            mText = v.findViewById(R.id.language_picked);
            String languagePicked 
    = getArguments().getString(ARG_LANGUAGE_PICKED);
            mText.setText(languagePicked);
            return v;
        }
    
    //newInstance()方法
        public static Fragment newInstance(String languagePicked) {
            Bundle bundle = new Bundle();
            bundle.putSerializable(ARG_LANGUAGE_PICKED, languagePicked);
    
            Fragment SecondFragmentInstance = new SecondFragment();
            SecondFragmentInstance.setArguments(bundle);
            return SecondFragmentInstance;
        }
    
    }
    

    现在我们有了这个方法,又得到了FirstFragment传入的Intent对象中的extra信息languagePicked
    我们只需要在SecondActivityonCreate()方法中,将languagePicked作为参数传入SecondFragment.newInstance()方法;
    即可实现,在SecondFragment创建之后,被添加给SecondActivity之前;
    SecondFragment装载argument

    //SecondActivity
    public class SecondActivity extends AppCompatActivity {
    
    private final static String 
    EXTRA_LANGUAGE_PICKED = "language_picked";
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    //使用通用的Fragment容器,
    setContentView(R.layout.common_fragment_container);
    //因为目前两个Activity的布局中
    //其实都只需要一个用于容纳Fragment的frameLayout
           
            //要想在Activity中创建Fragment,先要得到FragmentManager
            FragmentManager fragmentManager = getSupportFragmentManager();
            Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
    
            if (fragment == null) {
                //在firstActivity中通过Intent跳转到secondActivity,
                //SecondActivity创建之后,从传入的Intent中得到extra信息,
                //然后根据这个信息来创建secondFragment实例,
                //得到的信息将用来作为参数,传入secondFragment的newInstance()方法
                String languagePicked = 
    getIntent().getStringExtra(EXTRA_LANGUAGE_PICKED);
    //SecondFragment.newInstance()方法
                fragment = SecondFragment.newInstance(languagePicked);
    
                fragmentManager.beginTransaction()
                        .add(R.id.fragment_container, fragment)
                        .commit();
            }
    
    
        }
    
        //静态方法,提供从别的活动跳转到自身的Intent
        public static Intent newIntent(Context packageContext, String languagePicked) {
            Intent intent = new Intent(packageContext, SecondActivity.class);
            intent.putExtra(EXTRA_LANGUAGE_PICKED, languagePicked);
            return intent;
        }
    }
    

    这一做法的灵活之处就在于:
    SecondFragment虽然需要得到数据,但是它不再亲自去
    而是由托管它的Activity(此处是SecondActivity)来负责提供数据。
    如此一来,就实现了SecondActivity的复用。
    倘若现在有一个ThirdActivity也想要托管SecondFragment,那它只要能提供数据(类似于SecondActivity提供的languagePicked),那它就一样可以其onCreate()方法中作出类似的实现。
    -- end --

    水平有限,难免纰漏,如有错误,欢迎指正。
    诸君共勉:)

    相关文章

      网友评论

        本文标题:Android Fragment之间的数据交流

        本文链接:https://www.haomeiwen.com/subject/ovfjgxtx.html