1. 程式人生 > >介面實現Fragment、Activity間互調解耦

介面實現Fragment、Activity間互調解耦

    fragment之間及Fragment與Activity互調在實際應用中極為常見,比如官方API介紹Fragment給出的平板新聞客戶端例子,UI左邊是標題Fragment,UI右邊是內容Fragment,點選不同的標題,顯示不同的新聞內容,這是一個簡單的Fragment間互調。根據Fragment互調是否解耦,是否貫徹面向物件程式設計思想,可以分為野路子做法及正規軍做法,當然二者都能實現目的。本文通過具體的實現及改進過程,逐步降低Fragment互調的耦合度。

一、野路子用法

1、Fragment/Activity---呼叫--->Fragment

    每個Fragment都有一個唯一的TAG或者ID,可以通過getFragmentManager.findFragmentByTag(),或者findFragmentById()獲得任何Fragment例項,然後進行操作。

2、Fragment--呼叫-->Activity

    通過getActivity得到當前繫結的Activity的例項,然後進行操作。

    上面兩種程式設計思想一目瞭然,在程式設計中也極為常見,具體到新聞客戶端例子,就是在TitleFragment中通過程式碼直接呼叫ContentFragment,這樣做TitleFragment就與ContentFragment高度耦合,TitleFragment程式碼被寫死,直接破壞Fragment的重用思想。

    下面是應用傳統野路子實現過程:

ContentFragment.java
public class ContentFragment extends Fragment {
	String[] news = new String[]{
			"日企員工如何跟老闆請假9.3日",
			"手撕鬼子",
			"9月9日憶山東兄弟",
	};
	private int dataId;
	private TextView tv;
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		tv = new TextView(getActivity());
		Bundle bundle = getArguments();
		dataId = bundle.getInt("dataId");
		tv.setText(news[dataId]);
		return tv;
	}
	
	public void setData(int dataId){
		this.dataId = dataId;
		tv.setText(news[dataId]);
	}
}

TitleFragment.java
public class TitleFragment extends ListFragment {

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		//初始化資料
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, data);
		setListAdapter(adapter);
		
		fm = getFragmentManager();
		getListView().setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				//顯示contentFragment
				//傳資料給ContentFragment,修改那邊的結果
				f = fm.findFragmentByTag("content");
				if(f == null){
					//先建立ContentFragment,然後新增到佈局裡面。
					FragmentTransaction ft = fm.beginTransaction();
					f = new ContentFragment();
					//給fragment如何賦初始值
					Bundle bundle = new Bundle();
					bundle.putInt("dataId",0);
					f.setArguments(bundle);
					ft.add(R.id.fg_content, f, "content");
					ft.commit();
				}else{
					ContentFragment cf = (ContentFragment) f;			
                                        f.getArguments().putInt("dataId",position);//獲取bundle
				}
}

二、正規軍用法:

    Fragment互調使用介面通訊。考慮Fragment的重用,所以必須降低Fragment與Activity的耦合,而且Fragment更不應該直接操作別的Fragment,畢竟Fragment操作應該由它的管理者Activity來決定。

    改進一:

TitleItemClickListener.java
public interface TitleItemClickListener {	
	void onItemClick(int position );

}
</pre><pre code_snippet_id="1587149" snippet_file_name="blog_20160224_7_886404" name="code" class="html">MainActivity.java
public class MainActivity extends Activity implements TitleItemClickListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		FragmentManager fm = getFragmentManager();
		TitleFragment tf = (TitleFragment) fm.findFragmentById(R.id.fg_title);
	}

	public void setNewsTitle(int  position){
		TextView tv = (TextView) findViewById(R.id.tv);
		tv.setText("鬼子"+position);
	}

	@Override
	public void onItemClick(int position) {
		setNewsTitle(position);
	}
}

TitleFragment.java
public class TitleFragment extends ListFragment {

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		//初始化資料
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, data);
		setListAdapter(adapter);
		
		fm = getFragmentManager();
		getListView().setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				TitleItemClickListener listener = (TitleItemClickListener) getActivity();
				listener.onItemClick(position);
}
}
Fragment之間通過Activity通訊,降低了耦合性,但TitleFragment的重用性有待加強,因為TitleFragment的程式碼:
				TitleItemClickListener listener = (TitleItemClickListener) getActivity();
這段程式碼涉及到getActivity(),所以只能被Activity重用,這是一個限制,所以可以進一步改進。

改進二:

TitleFragment.java
public class TitleFragment extends ListFragment {

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		//初始化資料
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, data);
		setListAdapter(adapter);
		
		fm = getFragmentManager();
		getListView().setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				listener.onItemClick(position);
}

	public void setTitleItemClickListener(TitleItemClickListener listener){
		this.listener = listener;
	}

}

MainActivity.java
public class MainActivity extends Activity implements TitleItemClickListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		FragmentManager fm = getFragmentManager();
		TitleFragment tf = (TitleFragment) fm.findFragmentById(R.id.fg_title);
		tf.setTitleItemClickListener(this);
	}

	public void setNewsTitle(int  position){
		TextView tv = (TextView) findViewById(R.id.tv);
		tv.setText("鬼子"+position);
	}

	@Override
	public void onItemClick(int position) {
		setNewsTitle(position);
	}
}
至此,耦合完全解除。