本节前言
上一节我们介绍了Fragement的横竖切换屏的用法,今天我们就介绍一下Fragment的基本用法
示例说明用法
一、常见的两种加载方式
方式一:FragmentTranscation
并使用FragmentManager管理下的FragmentTranscation来进行Fragment或者Fragment列表的加载(add)、替换(replace)、删除(remove)等操作,此时的Fragment所在的容器一般选择用FrameLayout。
//将一个fragment实例添加到Activity的最上层
add(int containerViewId, Fragment fragment, String tag);
//将一个fragment实例从Activity的fragment队列中删除
remove(Fragment fragment);
//替换containerViewId中的fragment实例,注意,它首先把containerViewId中所有fragment删除,然后再add进去当前的fragment
replace(int containerViewId, Fragment fragment);
Demo
private void removeFragment2() {
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentByTag("fragment2");
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(fragment);
transaction.commit();
}
【关于FragmentManager和FragmentTranscation】
FragmentManager:Activity中有个FragmentManager,其内部维护fragment队列,以及fragment事务的回退栈。在Fragment被创建、并由FragmentManager管理时,FragmentManager就把它放入自己维护的fragment队列中。
FragmentTransaction:知道了FragmentManger可以管理和维护Fragment,那么FragmentManager是直接去绑定Fragment然后把它set进自己的队列中吗?不是的,而是用FragmentTransaction(Fragment事务),FragmentManager调用beginTransaction()方法返回一个新建的事务,用于记录对于Fragment的add、replace等操作,最终将事务commit回FragmentManager,才开始启动执行事务的内容,实现真正的Fragment显示。
代码如下
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
MyFragmentA fragmentA = new MyFragmentA();
getSupportFragmentManager().beginTransaction().add(R.id.frame_root_fragment,fragmentA).commit();
}
其实是这样写的缩减
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment1 fragment1 = new Fragment1();
transaction.add(R.id.fragment_container, fragment1);
transaction.commit();
有关回滚——FragmentTransaction
1、FragmentTransaction事务回滚使用方法:
上部分,我们讲了有关添加、删除Fragment的操作,想将上一次commit的操作返回时,要怎么做呢。这就需要FragmentTransaction的回滚功能了。
要使用回滚功能,只需要要使用下面两个代码:
在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中。
transaction.addToBackStack(String tag);
在需要回退时,使用popBackStack()将最上层的操作弹出回退栈。
manager.popBackStack();
这里的popBackStack()是弹出默认的最上层的栈顶内容。
当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。函数如下:
void popBackStack(int id, int flags);
void popBackStack(String name, int flags);
参数int id是当提交变更时transaction.commit()的返回值。
参数string name是transaction.addToBackStack(String tag)中的tag值;
至于int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层;
当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈;
除了这几个函数,还有下面几个函数:有关他们的使用,我们在这小部分结尾时会提到
popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag)
方式二:使用ViewPager
使用ViewPager等容器去装载Fragment列表并通过他们自己的页面切换能力去切换Fragment。
【关于ViewPager适配器相关类:FragmentPagerAdapter与FragmentStatePagerAdapter】
FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。
FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragment从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。总结:使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。
第一步布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
……
/>
<android.support.v4.view.ViewPager
android:id="@+id/vp_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
第二步布局Fragment.xml以及代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_white"
android:orientation="vertical">
<TextView
android:id="@+id/txt_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="呵呵"
android:textColor="@color/text_yellow"
android:textSize="20sp" />
</LinearLayout>
MyFragment1.java:
public class MyFragment1 extends Fragment {
public MyFragment1() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fg_content, container, false);
TextView txt_content = (TextView) view.findViewById(R.id.txt_content);
txt_content.setText("第一个Fragment");
return view;
}
}
另一个照葫芦画瓢,换点东西就好!
第三步自定义FragmentPagerAdapter
public class FragAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragments;
public FragAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
// TODO Auto-generated constructor stub
mFragments=fragments;
}
@Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return mFragments.get(arg0);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mFragments.size();
}
}
这里有三个函数,根据第一部分的官方文档,可知,对于FragmentPagerAdapter的派生类,只重写getItem(int)和getCount()就可以了。
对于构造函数,这里申请了一个Fragment的List对象,用于保存用于滑动的Fragment对象,并在创造函数中初始化:
public FragAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
// TODO Auto-generated constructor stub
mFragments=fragments;
}
然后在getItem(int arg0)中,根据传来的参数arg0,来返回当前要显示的fragment,下面是getItem的官方解释,难度不大,不再细讲。
第四部、主Activity的实现
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构造适配器
List<Fragment> fragments=new ArrayList<Fragment>();
fragments.add(new Fragment1());
fragments.add(new Fragment2());
FragAdapter adapter = new FragAdapter(getSupportFragmentManager(), fragments);
//设定适配器
ViewPager vp = (ViewPager)findViewById(R.id.viewpager);
vp.setAdapter(adapter);
}
}
这里有一个特别注意的两点
1.问题:在MainActivity中,当写到这句:fragments.add(new Fragment1()); 向Fragment列表中添加Fragement对象实例时,会提示“无法将Fragment1()转换为fragment”
解决办法 :这是因为导入包不一致,一般的问题在于:在Fragment1中导入的是android.app.Fragment, 而在这里导入类确是:android.support.v4.app.Fragment,包不同当然无法转换,统一导入为android.support.v4.app.Fragment
2.当我们第一步仍没有办法解决办法,或者说。v4包里并没有该类,比如继承PreferenceFragment,
import android.preference.PreferenceFragment;
解决办法:这时候我们就需要修改FragmentPagerAdapter的包,把
import android.support.v4.app.FragmentPagerAdapter;
修改成import android.support.v13.app.FragmentPagerAdapter;
这里的原因我也不是很清楚,有兴趣的可以研究下