Fragment
如何使用Fragment?
注意,使用的V4包中的Fragment!在这里我们全部使用android-support-v4.jar包里Fragment,不用系统自带的Fragment;这两个基本一样,但V4包中的相对功能更强大一些。
至于MainActivity,由于我们使用的V4包,必须将MainActivity派生自FragmentActivity,否则根本无法启动程序!因为系统的Activity只能用来盛装系统自带的Fragment,而无法盛装V4包中的Fragment,因为系统的Activity根本无法识别V4包中的Fragment,因为这根本就不是一块的代码!如果不使用V4包,使用系统自带的Fragment则不必将MainActivity派生自FragmentActivity。
1.XML静态添加Fragment(不推荐,所以以下不做详细解析)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<fragment
android:id="@+id/fragment1"
android:name="com.harvic.com.harvicblog2.Fragment1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment2"
android:name="com.harvic.com.harvicblog2.Fragment2"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
2.动态添加Fragment
-
1.在Activity的XML布局里面添加一个FrameLayout来盛装要添加的Fragment
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
-
2.定义你自己的Fragment布局fragment.xml,并创建你的 Fragment
public class Fragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}
-
3.在Activiy中动态添加Fragment主要分为4步:
- 1.获取到FragmentManager,在V4包中通过getSupportFragmentManager,在系统中原生的Fragment是通过getFragmentManager获得的。
- 2.开启一个事务,通过调用beginTransaction方法开启。
- 3.向容器内加入Fragment,一般使用add或者replace方法实现,需要传入容器的id和Fragment的实例。
-
4.提交事务,调用commit方法提交。
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment1 fragment1 = new Fragment1();
/*
arg1:是要将fragment盛装的container,就是Activity中XML添加的FrameLayout
arg3:当传进去这个TAG,它就会跟这个fragment关联起来,当我们通过findFragmentByTag()时,根据这个TAG就能找到这个Fragment实例。进而对它进行操作
*/
transaction.add(R.id.fragment_container, fragment1);
//在通过transaction对Fragment操作完以后,一定要记得调用transaction.commit(),这样才会将操作提交到系统中,这里的代码才会最终起作用。
transaction.commit();
FragmentManager
要管理activity中的fragments,你就需要使用FragmentManager。通过getFragmentManager()或getSupportFragmentManager()获得 常用的方法有:
manager.findFragmentById(); //根据ID来找到对应的Fragment实例,主要用在静态添加fragment的布局中,因为静态添加的fragment才会有ID
manager.findFragmentByTag();//根据TAG找到对应的Fragment实例,主要用于在动态添加的fragment中,根据TAG来找到fragment实例
manager.getFragments();//获取所有被ADD进Activity中的Fragment
FragmentTransaction
一般用来对当前的Fragment进行管理。 常用的针对Fragment的方法有:
-
add()、replace()、remove()方法,需要注意的是:系统BUG——add()和replace()千万不要共用!!!
//将一个fragment实例添加到Activity的最上层 add(int containerViewId, Fragment fragment); add(int containerViewId, Fragment fragment, String tag); //将一个fragment实例从Activity的fragment队列中删除 remove(Fragment fragment); //替换containerViewId中的fragment实例,注意,它首先把containerViewId中所有fragment删除,然后再add进去当前的fragment replace(int containerViewId, Fragment fragment); replace(int containerViewId, Fragment fragment, String tag);<span style="line-height: 22.4px;"> </span>
-
hide()、show()使用方法示例
public FragmentTransaction hide(Fragment fragment);//将指定的fragment隐藏不显示 public FragmentTransaction show(Fragment fragment);//将以前hide()过的fragment显示出来
如果我们使用replace来切换页面,那么在每次切换的时候,Fragment都会重新实例化,重新加载一边数据,这样非常消耗性能和用户的数据流量。 这是因为replace操作,每次都会把Container中的现有的fragment实例清空,然后再把指定的fragment添加进去,就就造成了在切换到以前的fragment时,就会重新实例会fragment。 正确的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个。 这样就能做到多个Fragment切换不重新实例化:(基本算法如下)
FragmentManager manager = getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); Fragment oneFragment = manager.findFragmentByTag("oneFragment"); Fragment twoFragment = manager.findFragmentByTag("twoFragment"); switchContent(oneFragment,twoFragment); public void switchContent(Fragment oneFragment, Fragment twoFragment) { if (!twoFragment.isAdded()) { // 先判断是否被add过 transaction.hide(oneFragment).add(R.id.content_frame, twoFragment).commit(); // 隐藏当前的fragment,add下一个到Activity中 } else { transaction.hide(oneFragment).show(twoFragment).commit(); // 隐藏当前的fragment,显示下一个 } }
大家可能觉得这里有个问题,如果我们要show()的fragment不在最顶层怎么办?如果不在ADD队列的队首,那显然show()之后是不可见的;那岂不影响了APP逻辑。大家有这个想法是很棒的,但在APP中不存在这样的情况,因为我们的APP的fragment是一层层ADD进去的,而且我们的fragment实例都是唯一的,用TAG来标识,当退出的时候也是一层层剥离的,所以当用户的动作导致要添加某个fragment时,那说明这个fragment肯定是在栈顶的。
fragment回退栈
-
1、FragmentTransaction事务回滚使用方法:
-
上部分,我们讲了有关添加、删除Fragment的操作,想将上一次commit的操作返回时,要怎么做呢。这就需要FragmentTransaction的回滚功能了。 要使用回滚功能,只需要要使用下面两个代码:
/添加进栈 //在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中。 transaction.addToBackStack(String tag); //回退 //在需要回退时,使用popBackStack()将最上层的操作弹出回退栈。 manager.popBackStack();
这里的popBackStack()是弹出默认的最上层的栈顶内容。 当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。函数如下:
/* 参数int id是当提交变更时transaction.commit()的返回值。 参数string name是transaction.addToBackStack(String tag)中的tag值; 至于int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE; 当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层; 当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈; */ void popBackStack(int id, int flags); void popBackStack(String name, int flags);
-
-
2、Transaction事务回退的原则
这里我们着重讲一下,回退是以commit()提交的一次事务为单位的,而不是以其中的add,replace等等操作为单位回退的,即,如果我们在一次提交是添加了fragment2,fragment3,fragment4,那么回退时,会依据添加时的顺序,将它们一个个删除,返回到没有添加fragment4,fragment3,fragment2的状态。
fragment值传递
-
fragment参数传递方法一
-
1.在Activity中定义一个字段、然后添加set和get方法、代码如下、mTitle就是要传递的参数、如果是传递对象、可以把mTitle换成一个对象即可
public class DemoActivity { private String mTitle; public String getmTitle() { return mTitle; } public void setmTitle(String title) { this.mTitle = title; } }
-
2.Fragment调用方法、需要注意的是在设值的时候要进行强转一下
((DemoActivity)getActivity()).getmTitle();
-
fragment参数传递方法二
-
1.在fragment1中操作以下代码:
Fragment2 fragment = new Fragment2(); Bundle args = new Bundle(); args.putString("param", "把值从fragment1传递给fragment2"); fragment.setArguments(args); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.add(R.id.main_layout, fragment2); transaction.addToBackStack(null); transaction.commit();
-
2.然后在Fragment2的OnCreateView的时候再从arguments中获取参数:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment2, container, false); if (getArguments() != null) { String mParam1 = getArguments().getString("param"); TextView tv = (TextView)view.findViewById(R.id.textview); tv.setText(mParam1); } return view; }
-
3.如果还从Fragment2向Fragment1回传参数,那就需要到接口回调了。
与activity通讯
Fragment可以调用getActivity()方法很容易的得到它所在的activity的对象,然后就可以查找activity中的控件们(findViewById())还有activity中的方法。例如:
ViewlistView =getActivity().findViewById(R.id.list);
同样地,activity可以通过从FragmentManager获得一个Fragment的引用来调用fragment中的方法, 使用findFragmentByTag()或者findFragmentById().
ExampleFragment fragment =(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment)
activity响应fragment的事件
有时,你可能需要fragment与activity共享事件。一个好办法是在fragment中定义一个回调接口,然后在activity中实现之。
例如, 新闻APP用,通常在activity中有2个fragment – 一个在左边用来显示文章列表(ListFramgent), 另一个在右边显示文章内容(Contentfragment) – 然后ListFramgent必须告诉activity何时一个list item被选中,然后它可以告诉Contentfragment去显示文章.
-
1、ContentFragment设置textView函数:
public class ContentFragment extends Fragment { private TextView mTv; ………… public void setText(String text) { mTv.setText(text); } }
-
2、ListFramgent中的处理方式:
-
(1)、定义接口及变量
由于是用回调,所以要先定义一个接口及对应的变量:
private titleSelectInterface mSelectInterface; public interface titleSelectInterface{ public void onTitleSelect(String title); }
-
(2)、接口变量赋值 接口是给activity用的,所以要在activity中给这里的接口变量赋值,可以有很多方法,当然可以选择写一个setXXX()函数来赋值,但如果用户忘了怎么办?所以我们要强制用户赋值。所以采用强转的方式,在fragment与activity相关联时,进行强转赋值:
public void onAttach(Activity activity) { super.onAttach(activity); try { mSelectInterface = (titleSelectInterface) activity; } catch (Exception e) { throw new ClassCastException(activity.toString() + "must implement OnArticleSelectedListener"); } }
采用强转的方式的问题在于,如果用户的activity没有implements titleSelectInterface,就会抛出错误,所以在调试过程中就会发现。
-
(3)、调用接口变量 下一步就是在ListFramgent中在用户点击listView的item的时候,将结果回传给Activity了,代码如下:
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); listView = (ListView) getView().findViewById(R.id.list);//获取自己视图里的控件引用,方法二 ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings); listView.setAdapter(arrayAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String str = mStrings[position]; mSelectInterface.onTitleSelect(str); } }); }
-
(4)、在Activity中实现titleSelectInterface接口 首先是MainActivity必须实现titleSelectInterface接口,然后结果会在onTitleSelect(String title)中返回,在结果返回后利用ContentFragment.setText()操作textView;代码如下:
public class MainActivity extends FragmentActivity implements Fragment1.titleSelectInterface { …… @Override public void onTitleSelect(String title) { FragmentManager manager = getSupportFragmentManager(); ContentFragment contentFragment = (ContentFragment)manager.findFragmentById(R.id.content_fragment); contentFragment.setText(title); } }
在上面代码中可以看出,在结果返回后,通过findFragmentById()来获得ContentFragment的实例,这里首次出现了findFragmentById()函数的用法,这个函数主要用来静态添加的fragment中,通过fragment的ID值来获取它的实例。在获得ContentFragment的实例以后,通过调用我们写好了setText()方法来将结果显示在textView中。
ContentFragment contentFragment = (ContentFragment)manager.findFragmentById(R.id.content_fragment);
-
大部分内容摘自: http://blog.csdn.net/harvic880925/article/details/44927375 harvic博客