處女男學Android(十)---Fragment完結篇之Fragment通訊和ListFragment,androidfragment
一、前言
前兩篇blog介紹了fragment的基本用法
1.Fragment API簡介:如FragmentManager、FragmentTransaction等。
2.定義方式:繼承Fragment類並重寫onCreat、onCreatView、onPause等。
3.引用方式:通過定義<fragment>標籤或者通過FragmentTrasaction執行個體的add()動態添加Fragment這兩種。
4.Fragment回退棧與Fragment生命週期。
關於Fragment剩餘的重要知識點差不多就剩下這幾個:
1.Fragment與Activity、Fragment與Fragment之間的通訊。
2.ListFragment的使用方法。
3.其它相關的零碎知識點。
本篇blog就著手記錄一下以下上面提到的這三點內容,關於Fragment的介紹到本篇就結束了。
二、Fragment通訊
a.Fragment與Activity通訊
我們通常都是在Activity中去管理Fragment的,所以一般Activity中都應當有其附屬的所有Fragment的引用,那麼直接在Activity通過執行個體化的Fragment對象即可訪問該Fragment的方法了。看一段簡單的代碼:
package com.example.fragmenttransdemo;import android.app.Activity;import android.app.FragmentManager;import android.app.FragmentTransaction;import android.os.Bundle;public class MainActivity extends Activity {private FragmentManager fm;private FragmentTransaction ftx;private FragmentLeft fLeft;private FragmentRight fRight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);fm = getFragmentManager();ftx = fm.beginTransaction();fLeft = new FragmentLeft();fRight = new FragmentRight();ftx.add(R.id.fm_left, fLeft, "left");ftx.add(R.id.fm_right, fRight, "right");ftx.commit();}}
那麼如果在Activity中沒有Fragment的引用怎麼辦,比如我們是通過<fragment>標籤在Activity的布局檔案中引入fragment的,答案也很簡單,在第一篇的API中也簡單的提到過,每一個fragment都可以指定一個id或tag,通過FragmentManager的執行個體方法:findFragmentById或findFragmentByTag即可找到指定的Fragment了。比如下面這個例子:
布局檔案代碼
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.fragmentbasedemo.MainActivity" > <fragment android:id="@+id/fm_one" android:name="com.example.fragmentbasedemo.FragmentOne" android:layout_width="match_parent" android:layout_height="match_parent" android:tag="fragment_one_tag" /></RelativeLayout>
Activity代碼
package com.example.fragmentbasedemo;import android.app.Activity;import android.app.FragmentManager;import android.os.Bundle;import android.util.Log;import android.widget.Toast;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private FragmentManager fm;private FragmentOne fOne;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.i(TAG, "onCreate");setContentView(R.layout.activity_main);fm = getFragmentManager();fOne = (FragmentOne) fm.findFragmentById(R.id.fm_one);String fOneTag = fOne.getTag();Toast.makeText(MainActivity.this, "fOneTag:\n" + fOneTag,Toast.LENGTH_LONG).show();}}可以看到我們在布局檔案中為fragment定義了id和tag,而在代碼中通過findFragmentById即可找到該Fragment,並能成功得到Fragment的tag資訊:
看完了在Activity中操作Fragment之後,那麼再看一下如何在Fragment中去操作其所屬的Activity,很簡單,在Fragment中通過調用getActivity()即可得到當前Fragment綁定的Activity並進行操作。
除了基本的互動之外,可能還需要進行資料傳遞,同樣的分兩種情況:
1.Activity向Fragment傳遞資料(by Bundle)
在Activity中建立Bundle資料包,並調用Fragment的setArguments(Bundle b)方法Bundle資料包傳遞給Fragment。
2.Fragment向Activity傳遞資料(回調介面)
關於回調介面在下面再細說。
b.Fragment與Fragment通訊
最簡單的解決方案在上面已經提到過了,同樣是通過FragmentManager的執行個體方法findFragmentByTag即可通過這個Tag找到同級的任意Fragment。同樣的給一小段代碼的例子:
package com.example.fragmenttransdemo;import android.app.Fragment;import android.app.FragmentManager;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;public class FragmentLeft extends Fragment {private Button button;private FragmentManager fm;@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubView view = inflater.inflate(R.layout.left, null);button = (Button) view.findViewById(R.id.btn_left);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubfm = getFragmentManager();// 得到右邊的FragmentFragment fRight = fm.findFragmentByTag("right");View view = fRight.getView();EditText et = (EditText) view.findViewById(R.id.et_right);String rightInfo = et.getText().toString();Toast.makeText(getActivity(), rightInfo, Toast.LENGTH_LONG).show();}});return view;}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();}}
很簡單,在FragmentLeft中,首先通過tag找到FragmentRight,然後擷取了該Fragment的布局中的值並彈出。
除了簡單的資料尋找之外,有時我們也會有操作的需求,比如上一篇blog的例子,FragmentOne放了一個ListView,當點擊item時把當前Fragment替換掉,上一篇blog中我的做法是直接在ListView的OnItemClick事件去執行FragmentManager的replace方法去替換的,注意這樣做是不可取的,不管在Android的參考書上,還是鴻洋的部落格中都提過這一點:Fragment都應當由Activity管理。所以一般比較標準的做法是:如果需要操作一個Fragment,首先應在其內部定義一個回調介面,然後在Activity中實現這個介面並實現介面的抽象方法,當需要進行Fragment的操作時,在Fragment中回調Activity的實現即可。
下面就完善一下上一篇blog的例子,同樣是兩個Fragment,FragmentOne放了一個ListView,當點擊Item時替換Fragment並傳值到新的Fragment,首先是Fragment的代碼:
package com.example.fragmentbasedemo;import android.app.Activity;import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ListView;public class FragmentTwo extends Fragment implements OnItemClickListener {private ListView lv;@Overridepublic void onAttach(Activity activity) {// TODO Auto-generated method stubsuper.onAttach(activity);}@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubView v = inflater.inflate(R.layout.fragment_list, null);lv = (ListView) v.findViewById(R.id.lv_hero_list);lv.setOnItemClickListener(this);return v;}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();}@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {// 判斷Activity是否實現了這個介面if (getActivity() instanceof MOnItemClickListener)((MOnItemClickListener) getActivity()).onItemClick(view);}public interface MOnItemClickListener {void onItemClick(View view);}}
再看看Activity中的代碼:
package com.example.fragmentbasedemo;import com.example.fragmentbasedemo.FragmentTwo.MOnItemClickListener;import android.app.Activity;import android.app.FragmentManager;import android.app.FragmentTransaction;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.TextView;public class MainActivity extends Activity implements MOnItemClickListener {private static final String TAG = "MainActivity";private FragmentManager fm;private FragmentTransaction ftx;private FragmentTwo fTwo;private FragmentThree fThree;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.i(TAG, "onCreate");setContentView(R.layout.activity_main);fm = getFragmentManager();ftx = fm.beginTransaction();fTwo = new FragmentTwo();ftx.add(R.id.fl_container, fTwo, "fragment_two");ftx.commit();}@Overridepublic void onItemClick(View view) {Bundle b = new Bundle();TextView tv = (TextView) view;b.putCharSequence("text", tv.getText());if (fThree == null) {fThree = new FragmentThree();fThree.setArguments(b);}fm = getFragmentManager();ftx = fm.beginTransaction();ftx.replace(R.id.fl_container, fThree, "fragment_three");ftx.commit();}}
這樣做的好處是顯而易見的,比如:
1.降低了Activity與Fragment的耦合性,提高了Fragment的複用率。
2.在Activity中可以直接管理任意Fragment。
3.增加了程式的靈活性,這也算是解耦的好處之一,需要處理實現介面,不需要處理就不實現。
4.方便傳值,比如那個參數view。
三、ListFragment
ListFragment通常適合用於只放一個ListView的Fragment,之所以有這個要求是因為ListFragment並不需要布局,只需要提供一個Adapter,並通過ListFragment的setListAdapter(ListAdapter adapter)即可實現,非常簡單,不做過多解釋了,貼一段範例程式碼(Activity代碼就不貼了,ListFragment也當Fragment去操作即可):
package com.wl.lfd;import java.util.ArrayList;import java.util.List;import android.app.ListFragment;import android.os.Bundle;import android.view.View;import android.widget.ArrayAdapter;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.Toast;public class MyListFragment extends ListFragment {private ListAdapter adapter;@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);List<String> datas = new ArrayList<String>();for (int i = 0; i < 30; i++) {datas.add("druid" + i);}adapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1, datas);setListAdapter(adapter);}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();}@Overridepublic void onListItemClick(ListView l, View v, int position, long id) {// TODO Auto-generated method stubToast.makeText(getActivity(), adapter.getItem(position).toString(),Toast.LENGTH_SHORT).show();}}
下面是:
四、總結
關於Fragment的介紹到這裡就全部結束了,後續還會繼續完善這幾篇Fragment的文章,本系列的《處女男學Android》到此也告一段落,後面繼續去學什麼我還在思考中,現在感覺什麼都會一點,又什麼都不會,很是煩躁,準備制定一個詳細的可行的學習方案和規劃,再回來繼續寫這一系列的blog。要學的東西還有很多,繼續加油,Raito!