首页 > 代码库 > 处女男学Android(十)---Fragment完结篇之Fragment通信和ListFragment

处女男学Android(十)---Fragment完结篇之Fragment通信和ListFragment



一、前言



前两篇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;

	@Override
	protected 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;

	@Override
	protected 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;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		View view = inflater.inflate(R.layout.left, null);
		button = (Button) view.findViewById(R.id.btn_left);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				fm = getFragmentManager();
				// 得到右边的Fragment
				Fragment 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;
	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.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;

	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		super.onAttach(activity);

	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		View v = inflater.inflate(R.layout.fragment_list, null);
		lv = (ListView) v.findViewById(R.id.lv_hero_list);
		lv.setOnItemClickListener(this);
		return v;
	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
	}

	@Override
	public 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;

	@Override
	protected 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();
	}

	@Override
	public 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;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.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);
	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
	}

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		// TODO Auto-generated method stub
		Toast.makeText(getActivity(), adapter.getItem(position).toString(),
				Toast.LENGTH_SHORT).show();
	}

}

下面是效果图:




四、总结



关于Fragment的介绍到这里就全部结束了,后续还会继续完善这几篇Fragment的文章,本系列的《处女男学Android》到此也告一段落,后面继续去学什么我还在思考中,现在感觉什么都会一点,又什么都不会,很是烦躁,准备制定一个详细的可行的学习方案和规划,再回来继续写这一系列的blog。要学的东西还有很多,继续加油,Raito!

处女男学Android(十)---Fragment完结篇之Fragment通信和ListFragment