Fragment

2020-10-14  本文已影响0人  Anwfly

一、 Fragment概述

1.Fragment概述

Fragment,也就是碎片,是一种可以嵌入在活动中的UI片段,他能让程序更加合理和充分的利用大屏幕空间。

2.Fragment设计初衷

Fragment从安卓3.0开始引入,本意是为了适配大屏幕的安卓设备而生的。
运行Android的设备繁多,屏幕大小更是多种多样。针对不同屏幕尺寸,通常情况下,开发者都是先针对手机开发一套源代码,然后拷贝一份,修改布局以适应大屏幕设备,或平板,电视等。为了决解这样的麻烦,Google推出了Fragment。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全由不同的Fragment组成,Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。

3. 特点

Fragment 轻量级,切换灵活,它能让你的app在现有基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍,"单Activity + 多Fragment架构"和"多模块Activity + 多Fragment架构"应运而生!
特点:

a.Fragment是依赖于Activity的,不能独立存在的。
b.一个Activity里可以有多个Fragment。
c.一个Fragment可以被多个Activity重用。
d.Fragment有自己的生命周期,并能接收输入事件。
e.我们能在Activity运行时动态地添加或删除Fragment。

二、 Fragment生命周期

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:

image.png

Fragment生命周期方法很重要,一共11个方法,activity是7个,需要大家必须掌握。我们看到Fragment比Activity多了几个额外的生命周期回调方法:

//当Fragment与Activity发生关联时调用。
onAttach(Activity)
//创建该Fragment的视图
onCreateView(LayoutInflater, ViewGroup,Bundle)
//当Activity的onCreate方法返回时调用
onActivityCreated(Bundle)
//与onCreateView相对应,当该Fragment的视图被移除时调用
onDestoryView()
//与onAttach相对应,当Fragment与Activity关联被取消时调用
onDetach()

三、Frament的具体使用

注意:所有Fragment对象,统一使用support.v4包下的Fragment或者androidx下的Fragment

1. 静态的使用Fragment

这是使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤:

①继承Fragment,重写onCreateView决定Fragemnt的布局

public class FragmentA extends Fragment{
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View inflate = inflater.inflate(R.layout.fragment_a, null, false);
        return inflate;
    }
}

②在Activity中声明此Fragment,就当和普通的View一样

 <LinearLayout 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:orientation="vertical"
    tools:context="com.edu.cumulus.fragment.MainActivity">

    <fragment
        android:id="@+id/fragment_a"
        android:name="com.edu.cumulus.fragment.FragmentA"
        android:layout_width="match_parent"
        android:layout_height="300dp"/>
    <fragment
        android:id="@+id/fragment_b"
        android:name="com.edu.cumulus.fragment.FragmentB"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

③可以通过FragmentManager找到

FragmentManager manager = getSupportFragmentManager();
Fragment fragmentA = manager.findFragmentById(R.id.fragment_a);

缺点:静态使用不够灵活

2. 动态的使用Fragment

通过动态加载的方式来加载Framgnt

//1.获得碎片管理器
FragmentManager manager = getFragmentManager();

//2.开始事务
FragmentTransaction tr = manager.beginTransaction();

//3.构建Fragment对象
SoundFragment fragment = new SoundFragment();

//4.替换容器中内容 参数1 右边容器的id,参数2 fragment对象
tr.replace(R.id.container, fragment);

//5.提交事务
tr.commit();

四、 Fragment常用方法

1. Fragment add remove replace

a、获取FragmentManager的方式:
getFragmentManager() // v4中,getSupportFragmentManager
在使用Fragment的时候,会用到FragmentManager,叫碎片管理器。主要作用就是负责管理Fragment对象。比如开启事务、添加、删除、隐藏、显示Fragment,还有回退栈,都有FragmentManager来去管理。还可以获取任务栈里所有Fragment对象。
b、主要的操作都是FragmentTransaction的方法

FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
transaction.add() 
往Activity中添加一个Fragment
transaction.remove() 
从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
transaction.replace()
使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
transaction.hide()
隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show()
显示之前隐藏的Fragment
detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach()
重建view视图,附加到UI上并显示。
transatcion.commit()//提交一个事务

关于事务:
事务的作用:包含一组操作,其中所有操作都成功,才能认为事务操作成功。 如果其中任何一个步骤失败,认为整个事务操作失败,需要进行回滚,将前面成功操作,还原

转账过程:A --> B 转100
{
   1.从A的账户减去100
   2.将100转入B的账户
}

回退栈的管理:

1.调用transaction.addToBackStack(null);可将Fragment加入回退栈,按返回时可返回到上一个Fragment

FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.container,new Fragment2(),"fragment2");
transaction.hide(this);
//将Fragment加入回退栈
transaction.addToBackStack(null);
transaction.commit();

2.若使用的是FragmentManager manager=getFragmentManager(),Activity继承的是AppCompatActivity,在某些版本api下按返回键直接退出了Activity,需要手动添加回退逻辑,重写onBackPressed():

@Override
public void onBackPressed() {
    FragmentManager manager = getFragmentManager();
    int backStackEntryCount = manager.getBackStackEntryCount();
    if (backStackEntryCount>0){
        manager.popBackStack();
    }else {
        finish();
    }
}

六、Fragment 和Activity交互

1. Fragment传递数据给Activity

①Fragment调用Activity或传递参数
方法1:调用Fragment的getActivity()方法即可返回它所在的Activity实例,之后就可调用Activity中的方法或者成员变量

MainActivity activity = (MainActivity) getActivity();
activity.show("");

方法2:使用接口回调的机制,也就是在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity类实现这个接口,这样Fragment就能够调用这个回调方法,将数据传给Activity中

//Fragment中
//定义接口类型的变量
public DataCallBack mDataCallBack;
//给变量赋值
public void setDataCallBack(DataCallBack dataCallBack) {
    mDataCallBack = dataCallBack;
}
//Fragment中定义接口
public interface DataCallBack{
    void setData(String str);
}

//Fragment中按钮的点击监听
 @Override
public void onClick(View v) {
    switch (v.getId()) {
        default:
            break;
        case R.id.btn_change:
            mDataCallBack.setData("我是fragment通过接口回调传过来的值");//回调方法
            break;
    }
}
//Activity中
//给mHomeFragment设置接口的匿名内部类对象,供回调使用
private void initView() {
    mHomeFragment.setDataCallBack(new HomeFragment.DataCallBack() {
        @Override
        public void setData(String str) {
            mTv.setText(str);//赋值
        }
    });
}

2. Activity传递数据给Fragment

在Activity中创建Bundle数据包,并调用Fragment对象的setArguments(Bundle bundle)方法
(也就是说我们得要首先创建一个Fragment对象之后才能够用这个对象调用setArgument(Bundle)方法)即可将Bundle数据包传递给该Fragment对象;之后在Fragment类中调用getArguments()方法就能够获得这个Bundle包。

这个方法一般用在Activity加载一个Fragment的时候,由于当我们加载一个Fragment的时候就想要将一些数据传给Fragment对象,来对Fragment对象进行初始化,所以在Fragment中的onCreate()或者onCreateView()方法中调用getArgument()方法来获得这个Bundle对象。

//Activity中
mHomeFragment = new HomeFragment();//创建fragment对象
Bundle bundle = new Bundle();//创建Bundle对象
bundle.putString("data","activity数据");//封装数据
mHomeFragment.setArguments(bundle);//把bundle传递给fragment
mFm.beginTransaction().add(R.id.rl,mHomeFragment).commit();
//Fragment中
Bundle bundle = getArguments();//获得Bundle对象
String data = (String) bundle.get("data");//获得封装的数据
mChange.setText(data);//显示数据

3. Fragment与Fragment传递数据

方法1:通过FragmentManager找到对应Id或者Tag的Framgment,然后获取里面的数据或方法

//Firstfragment中
FragmentManager fm = getChildFragmentManager();//获得管理器
//获得SecondFragment对象
SecondFragment sf = (SecondFragment)fm.findFragmentById(R.id.fragment_second);
sf.setText(str);//调用SecondFragment中的方法

方法2:通过它们所在的Activity作为桥梁,可以使用getActivity()或者接口回调,达到获取另一个Fragment数据的目的。

MainActivity activity = (MainActivity)getActivity();//获得所在的Activity
SecondFragment secondFragment = activity.mSecondFragment;//通过Activity获得另一个Fragment
secondFragment.setData("我是第一个Fragment的数据");//操作另一个Fragment

七、 RadioGroup底部导航集合Fragmentr切换

1.主界面布局 使用RadioGroup来布局底部按钮

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <RelativeLayout
        android:id="@+id/myrl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
    <!-- RadioGroup单选控件  radioButton 具体子控件 -->
    <RadioGroup
        android:id="@+id/rg"
        android:layout_width="match_parent"
        android:layout_height="66dp"
        android:background="#0ff"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/rb1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:checked="true"
            android:drawableTop="@drawable/main_selector"
            android:gravity="center"
            android:text="首页" />
        <RadioButton
            android:id="@+id/rb2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:drawableTop="@drawable/know_selector"
            android:gravity="center"
            android:text="知识" />
    </RadioGroup>
</LinearLayout>

2.核心代码:

private void initFragment() {
    //创建两个fragment对象
    mainFragment = new MainFragment();
    knowFragment = new KnowFragment();
    //获取fragment管理器
    fm = getSupportFragmentManager();
    //开启事务
    FragmentTransaction ft = fm.beginTransaction();
    //通过事物把两个fragment添加到界面中
    ft.add(R.id.myrl,mainFragment);
    ft.add(R.id.myrl,knowFragment);
    //默认显示首页,所以知识对应的knowFragment要默认隐藏
    ft.hide(knowFragment);
    //提交事务
    ft.commit();
}

3.底部按钮点击切换页面

private void initRg() {
    //开启事务
    FragmentTransaction fragmentTranscation = fm.beginTransaction();
    mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId){
                    case R.id.rb1:
                        //切换到第一个页面
                        fragmentTranscation.show(mainFragment);
                        //隐藏第二个页面
                        fragmentTranscation.hide(knowFragment);
                        //提交事务
                        fragmentTranscation.commit();
                        break;
                    case R.id.rb2:
                        //切换到第二个页面
                        fragmentTranscation.show(knowFragment);
                        //隐藏第一个页面
                        fragmentTranscation.hide(mainFragment);
                        fragmentTranscation.commit();
                        break;
              }
          }
     });
}

八、 FragmentPagerAdapter

1. FragmentPagerAdapter介绍

FragmentPagerAdapter是PagerAdapter中的其中一种实现。它将每一个页面表示为一个 Fragment,并且每一个Fragment都将会保存到fragment manager当中。而且,当用户没可能再次回到页面的时候,Fragment manager才会将这个Fragment销毁。这种pager十分适用于有一些静态Fragment,例如一组tabs,的时候使用。

//需要重写的方法有:
//构造方法,第一个参数是Fragment管理器,第二个参数是存放Fragment的集合
public VpFragmentAdapter(@NonNull FragmentManager fm, ArrayList<Fragment> fs)
public Fragment getItem(int position) //获得对应的Fragment对象,position表示索引
public int getCount() //获得Fragment的总数量

2. RadioGroup+ViewPager+Fragment切换

核心代码:

//适配器
public class VpFragmentAdapter extends FragmentPagerAdapter {
    private ArrayList<Fragment> fs;
    
    public VpFragmentAdapter(@NonNull FragmentManager fm, ArrayList<Fragment> fs) {
        super(fm);
        this.fs = fs;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {//设置当前显示的Fragment
        return fs.get(position);
    }

    @Override
    public int getCount() {//获得Fragment的数量
        return fs.size();
    }
}
//Activity中
mMainFragment = new MainFragment();//创建MainFragment
mKnowFragment = new KnowFragment();//创建KnowFragment
fs = new ArrayList<>();//创建集合收集Fragment
fs.add(mMainFragment);
fs.add(mKnowFragment);
mAdapter = new VpFragmentAdapter(getSupportFragmentManager(), fs);//创建适配器
mVp.setAdapter(mAdapter);//设置适配器
//添加RadioGroup的切换监听
mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        if(checkedId == R.id.rb1){//选择首页单选框,设置ViewPager当前页为第一页
            mVp.setCurrentItem(0);
        }else {//选择知识单选框,设置ViewPager当前页为第二页
            mVp.setCurrentItem(1);
        }
    }
});

补充要求:

  1. 首页Fragment用RecyclerView显示网络数据;
  2. 点击条目把点击的数据保存到数据库;
  3. 在知识Fragment中显示保存到数据库的数据。

九、 FragmentStatePagerAdapter

1.FragmentStatePagerAdapter的用法

FragmentStatePagerAdapter也是PagerAdapter中的其中一种实现,用法和FragmentPagerAdapter相同。使用FragmentPagerAdapter时,当页面不可见的时候,view hierarchy将会被销毁。这样子会导致应用程序占有太多资源。当页面数量比较大的时候,建议使用 FragmentStatePagerAdapter。

//需要重写的方法有:
//构造方法,第一个参数是Fragment管理器,第二个参数是存放Fragment的集合
public VpFragmentStateAdapter(@NonNull FragmentManager fm, ArrayList<Fragment> fs)
public Fragment getItem(int position) //获得对应的Fragment对象,position表示索引
public int getCount() //获得Fragment的总数量

2. FragmentStatePagerAdapter与FragmentPagerAdapter区别★★

十、 ViewPager加不同Fragment页面显示案例

1. ViewPager 复用Fragment思路

当ViewPager和Fragment结合时,如果每个Fragment结构相同,只是数据不同的情况下,可以复用一个Fragment类,创建多个对象。不同的页面具有不同的数据参数,通过setArguments()传入参数进行区分。

2. ViewPager加载不同页面思路

当ViewPager和Fragment结合时,如果每个Fragment结构相差很大,需要创建多个Fragment类,创建不同的界面,最后通过每个类创建自己的对象,单独使用。

上一篇 下一篇

猜你喜欢

热点阅读