Android Fragment之間的數據交流

Android Fragment Argument

眾所周知,多用Fragment能打造更靈活的程序。
本文通過一個淺顯的例子,來闡釋fragment之間基于Argument的數據交流。

簡單說一下要實現的目標:
本項目包含兩個活動和分別依附于這兩個活動的兩個Fragment。
簡單起見,這里分別為他們起名為:FirstActivity、FirstFragment、SecondActivity、SecondFragment。
他們之間的關系是:
兩個活動只負責容納(或者說托管)其對應的兩個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 --

水平有限,難免紕漏,如有錯誤,歡迎指正。
諸君共勉:)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容