一、前言
前两篇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); } }
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!