眾所周知纹坐,多用Fragment能打造更靈活的程序。
本文通過(guò)一個(gè)淺顯的例子,來(lái)闡釋fragment
之間基于Argument
的數(shù)據(jù)交流决左。
簡(jiǎn)單說(shuō)一下要實(shí)現(xiàn)的目標(biāo):
本項(xiàng)目包含兩個(gè)活動(dòng)和分別依附于這兩個(gè)活動(dòng)的兩個(gè)Fragment。
簡(jiǎn)單起見(jiàn),這里分別為他們起名為:FirstActivity
哆窿、FirstFragment
链烈、SecondActivity
、SecondFragment
挚躯。
他們之間的關(guān)系是:
兩個(gè)活動(dòng)只負(fù)責(zé)容納(或者說(shuō)托管)其對(duì)應(yīng)的兩個(gè)Fragment强衡。而具體的顯示和與用戶交互則由Fragment負(fù)責(zé)。
為了突出重點(diǎn)码荔,這里只實(shí)現(xiàn)最簡(jiǎn)單的功能:
- 在
FirstFragment
中顯示一個(gè)ListView漩勤,這個(gè)ListView顯示一串編程語(yǔ)言的名稱。 - 當(dāng)用戶點(diǎn)擊其中的
item
時(shí)缩搅,會(huì)跳轉(zhuǎn)到SecondActivity
越败。 - 這時(shí)
SecondActivity
的onCreate()
方法啟動(dòng),在其中加載SecondFragment
硼瓣。 - 最后
SecondFragment
的TextView
控件根據(jù)傳過(guò)來(lái)的信息顯示相應(yīng)的編程語(yǔ)言的名字究飞。
如圖:
在代碼中實(shí)現(xiàn)時(shí),FirstActivity
和SecondActivity
甚至都不需要對(duì)應(yīng)的Layout
資源文件堂鲤。因?yàn)樗鼈兾ㄒ坏淖饔弥皇菫镕ragment提供容器亿傅,所以這里只需要在java代碼中為兩個(gè)Activity設(shè)置contentView即可:
setContentView(R.layout.common_fragment_container);
這個(gè)名為common_fragment_container
的布局文件提供了一個(gè)FrameLayout
來(lái)作為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" />
根據(jù)我們的構(gòu)想,當(dāng)用戶點(diǎn)擊FirstFragment
中的ListView的item時(shí)瘟栖,應(yīng)該跳轉(zhuǎn)到SecondActivity
葵擎。
為此,我們?cè)?code>SecondActivity中定義靜態(tài)方法:
private final static String
EXTRA_LANGUAGE_PICKED = "language_picked"; //鍵
//靜態(tài)方法半哟,提供從別的活動(dòng)跳轉(zhuǎn)到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的點(diǎn)擊回調(diào):
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);
//點(diǎn)擊回調(diào)
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Resources resources = getResources();
//得到資源文件中定義的字符串?dāng)?shù)組
String[] languages =
resources.getStringArray(R.array.languages);
String str = languages[position];
Intent intent = SecondActivity.newIntent(
getActivity(), str);
//啟動(dòng)SecondActivity
startActivity(intent);
}
});
return v;
}
}
當(dāng)FirstFragment
通過(guò)startActivity(intent)
啟動(dòng)SecondActivity
之后酬滤。
SecondActivity
并不直接與用戶交互。
它要做的是:
- 將傳入的
intent
中的用戶點(diǎn)擊的編程語(yǔ)言名稱
取出來(lái)寓涨; - 然后傳給
SecondFragment
盯串。由SecondFragment
將它顯示出來(lái)。
而SecondFragment
想從SecondActivity
那兒取到數(shù)據(jù)有兩種方式:
第一種比較直接:
SecondFragment
簡(jiǎn)單粗暴地通過(guò)getActivity()
方法得到托管自己的SecondActivity
戒良;
然后通過(guò)getIntent()
方法得到從FirstFragment
中傳過(guò)來(lái)的Intent對(duì)象嘴脾;
最后得到其中的extra
信息。
這種方式雖然簡(jiǎn)單蔬墩,但也有代價(jià)译打。那就是破壞了封裝。使得SecondFragment
不能被復(fù)用拇颅。因?yàn)榇藭r(shí)它還承擔(dān)了取
的工作奏司。
第二種方式比較復(fù)雜,但也更靈活:附加argument給Fragment:
要附加argument給Fragment樟插,需要調(diào)用Fragment.setArguments(Bundle)
方法韵洋。而且必須是在fragment創(chuàng)建后竿刁,添加給Activity之前。
因此搪缨,一般的慣用做法是在Fragment類中添加newInstance()
靜態(tài)方法食拜。
通過(guò)這個(gè)方法完成fragment實(shí)例以及Bundle對(duì)象的創(chuàng)建,
最后再把a(bǔ)rgument放入bundle對(duì)象中副编,并附加給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;
}
}
現(xiàn)在我們有了這個(gè)方法负甸,又得到了FirstFragment
傳入的Intent對(duì)象中的extra信息languagePicked
;
我們只需要在SecondActivity
的onCreate()
方法中痹届,將languagePicked
作為參數(shù)傳入SecondFragment.newInstance()
方法呻待;
即可實(shí)現(xiàn),在SecondFragment
創(chuàng)建之后队腐,被添加給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);
//因?yàn)槟壳皟蓚€(gè)Activity的布局中
//其實(shí)都只需要一個(gè)用于容納Fragment的frameLayout
//要想在Activity中創(chuàng)建Fragment柴淘,先要得到FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
if (fragment == null) {
//在firstActivity中通過(guò)Intent跳轉(zhuǎn)到secondActivity迫淹,
//SecondActivity創(chuàng)建之后,從傳入的Intent中得到extra信息为严,
//然后根據(jù)這個(gè)信息來(lái)創(chuàng)建secondFragment實(shí)例敛熬,
//得到的信息將用來(lái)作為參數(shù),傳入secondFragment的newInstance()方法
String languagePicked =
getIntent().getStringExtra(EXTRA_LANGUAGE_PICKED);
//SecondFragment.newInstance()方法
fragment = SecondFragment.newInstance(languagePicked);
fragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
//靜態(tài)方法梗脾,提供從別的活動(dòng)跳轉(zhuǎn)到自身的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
雖然需要得到數(shù)據(jù),但是它不再親自去取
盹靴,
而是由托管它的Activity(此處是SecondActivity)
來(lái)負(fù)責(zé)提供數(shù)據(jù)炸茧。
如此一來(lái),就實(shí)現(xiàn)了SecondActivity
的復(fù)用稿静。
倘若現(xiàn)在有一個(gè)ThirdActivity
也想要托管SecondFragment
梭冠,那它只要能提供數(shù)據(jù)(類似于SecondActivity
提供的languagePicked
),那它就一樣可以其onCreate()方法
中作出類似的實(shí)現(xiàn)改备。
-- end --
水平有限控漠,難免紕漏,如有錯(cuò)誤悬钳,歡迎指正盐捷。
諸君共勉:)