Android中最最常用—Fragment实战篇最详解
- 2019 年 10 月 7 日
- 笔记
前言
Hi,各位花粉们,上一节Android中最最常用—Fragment基础篇最详解,我们详细的介绍了 Fragment
的基本原理及使用、 Fragment
中的常用方法等。在这一节,将结合具体的使用场景,来更加全面的介绍 Fragment
的日常使用。
示例一: RadioButton
+ Fragment
在之前介绍的你不能错过的RadioButton实践一文中,我们详细介绍了 RadioButton
的使用,在示例:实现微信底部Tab效果中,只是实现了底部导航的效果切换,那怎么使导航上面的内容页面随着底部Tab的切换而改变呢?下面就结合 Fragment
实现这个效果。
1.修改原 Activity
的主布局页面,新增 FrameLayout
作为 Fragment
的容器。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".MyFragmentActivity"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/radioGroup" app:layout_constraintTop_toTopOf="parent" /> <RadioGroup android:id="@+id/radioGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingTop="10dp" android:paddingBottom="10dp" app:layout_constraintBottom_toBottomOf="parent"> <RadioButton android:id="@+id/rbHome" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:button="@null" android:checked="true" android:drawableTop="@drawable/tab_home_selector" android:gravity="center_horizontal" android:text="首页" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbDiscovery" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_discovery_selector" android:gravity="center_horizontal" android:text="发现" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbContacts" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_contacts_selector" android:gravity="center_horizontal" android:text="通讯录" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbMe" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_me_selector" android:gravity="center_horizontal" android:text="我" android:textColor="@drawable/tab_text_selector" /> </RadioGroup> </androidx.constraintlayout.widget.ConstraintLayout>
2.创建4个 Fragment
,然后创建对应的4个布局(根据自己情况而定)。
以下创建了4个示例 Fragment
:FragmentHome(首页)、FragmentDiscovery(发现)、FragmentContacts(通讯录)、FragmentMine(我的)。
3.在 Activity
中实例化各个 Fragment
和 RadioButton
和 RadioParent
的控件,设置好监听器。
4.关联 RadioButton
和 Fragment
,通过 switchFragment()
的方法,控制 Fragment
的显示和隐藏。
public class MyFragmentActivity extends AppCompatActivity { private RadioGroup rg; private RadioButton mRbHome; private RadioButton mRbDiscovery; private RadioButton mRbContacts; private RadioButton mRbMe; private Fragment mFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_fragment); //初始化View initView(); //初始化Fragment initFragment(); //RadioGroup切换监听 rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { switch (checkedId) { case R.id.rbHome: switchFragment(FragmentHome.newInstance("首页Fragment")); break; case R.id.rbDiscovery: switchFragment(FragmentDiscovery.newInstance("发现Fragment")); break; case R.id.rbContacts: switchFragment(FragmentContacts.newInstance("通讯录Fragment")); break; case R.id.rbMe: switchFragment(FragmentMy.newInstance("我的Fragment")); break; default: break; } } }); } /** * 加载默认的Fragment */ private void initFragment() { mFragment = FragmentHome.newInstance("首页Fragment"); getSupportFragmentManager().beginTransaction() .add(R.id.container, mFragment).commit(); } private void switchFragment(Fragment fragment) { //判断当前显示的Fragment是不是切换的Fragment if (mFragment != fragment) { //判断切换的Fragment是否已经添加过 if (!fragment.isAdded()) { //如果没有,则先把当前的Fragment隐藏,把切换的Fragment添加上 getSupportFragmentManager().beginTransaction().hide(mFragment) .add(R.id.container, fragment).commit(); } else { //如果已经添加过,则先把当前的Fragment隐藏,把切换的Fragment显示出来 getSupportFragmentManager().beginTransaction() .hide(mFragment).show(fragment).commit(); } mFragment = fragment; } } /** * 动态设置四个tab的样式 */ private void initView() { rg = findViewById(R.id.radioGroup); mRbHome = findViewById(R.id.rbHome); mRbContacts = findViewById(R.id.rbContacts); mRbDiscovery = findViewById(R.id.rbDiscovery); mRbMe = findViewById(R.id.rbMe); setStyle(R.drawable.tab_home_selector, mRbHome); setStyle(R.drawable.tab_contacts_selector, mRbContacts); setStyle(R.drawable.tab_discovery_selector, mRbDiscovery); setStyle(R.drawable.tab_me_selector, mRbMe); } /** * 动态设置每个tab的图片宽高以及文字间距 * * @param selector RadioButton的样式选择器 * @param rb RadioButton的样式选择器 */ private void setStyle(int selector, RadioButton rb) { Drawable drawableHome = getResources().getDrawable(selector); drawableHome.setBounds(0, 0, 80, 80); rb.setCompoundDrawables(null, drawableHome, null, null); } }
在 switchFragment()
的方法中,判断切换的 Fragment
是否已经添加过,避免每一次切换 Fragment
的时候都调用 add()
或者 replace()
,而是通过 hide()
和 show()
,减少频繁地创建新的实例。
mFragment:用于记录当前加载的 Fragment
,用户切换时隐藏。
5.最后效果如下。

示例二: ViewPager
+ Fragment
上面初步实现了一个APP的底部导航栏效果,但细心地读者可能会发现,微信的四个主页面是可以左右滑动切换的,而上面的效果只能是点击底部导航Tab进行切换。要实现左右页面滑动切换,就要使用我们接下来需要介绍的控件 ViewPager
了。
ViewPager
是 support v4
库中提供界面滑动的类,继承自 ViewGroup
。PagerAdapter
是 ViewPager
的适配器类,为 ViewPager
提供界面。但是一般来说,通常都会使用 PagerAdapter
的两个子类:FragmentPagerAdapter
和 FragmentStatePagerAdapter
作为 ViewPager
的适配器,他们的特点是界面是 Fragment
。
默认, ViewPager
会缓存当前页相邻的界面,比如当滑动到第2页时,会初始化第1页和第3页的界面(即 Fragment
对象,且生命周期函数运行到 onResume()
),可以通过 setOffscreenPageLimit(count)
设置离线缓存的界面个数。
FragmentPagerAdapter
和 FragmentStatePagerAdapter
需要重写的方法都一样,常见的重写方法如下:
publicFragmentPagerAdapter(FragmentManagerfm)
:构造函数,参数为FragmentManager
。如果是嵌套Fragment
场景,子PagerAdapter
的参数传入getChildFragmentManager()
。FragmentgetItem(intposition)
:返回第position位置的Fragment
,必须重写。intgetCount()
:返回ViewPager
的页数,必须重写。ObjectinstantiateItem(ViewGroupcontainer,intposition)
:container是ViewPager
对象,返回第position个位置的Fragment
。voiddestroyItem(ViewGroupcontainer,intposition,Objectobject)
:container是ViewPager
对象,object是Fragment
对象。getItemPosition(Objectobject)
:object是Fragment
对象,如果返回POSITIONUNCHANGED,则表示当前Fragment
不刷新,如果返回POSITIONNONE,则表示当前Fragment需要调用destroyItem()
和instantiateItem()
进行销毁和重建。默认情况下返回POSITION_UNCHANGED。
FragmentPagerAdapter
和 FragmentStatePagerAdapter
的区别:
FragmentPagerAdapter
该类内的每一个生成的Fragment
都将保存在内存之中,因此适用于那些相对静态的页,数量也比较少的那种。FragmentStatePagerAdapter
其内部不断重建和销毁,适合处理有很多页,并且数据动态性较大、占用内存较多的情况。
修改原代码,新增 ViewPager
关联 RadioButton
和 Fragment
,步骤如下:
1.修改 Activity
布局文件,替换 FrameLayout
为 ViewPager
。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".MyFragmentActivity"> <!--替换FrameLayout为ViewPager--> <androidx.viewpager.widget.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/radioGroup" app:layout_constraintTop_toTopOf="parent" /> <RadioGroup android:id="@+id/radioGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingTop="10dp" android:paddingBottom="10dp" app:layout_constraintBottom_toBottomOf="parent"> <RadioButton android:id="@+id/rbHome" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:button="@null" android:checked="true" android:drawableTop="@drawable/tab_home_selector" android:gravity="center_horizontal" android:text="首页" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbDiscovery" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_discovery_selector" android:gravity="center_horizontal" android:text="发现" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbContacts" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_contacts_selector" android:gravity="center_horizontal" android:text="通讯录" android:textColor="@drawable/tab_text_selector" /> <RadioButton android:id="@+id/rbMe" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:button="@null" android:drawableTop="@drawable/tab_me_selector" android:gravity="center_horizontal" android:text="我" android:textColor="@drawable/tab_text_selector" /> </RadioGroup> </androidx.constraintlayout.widget.ConstraintLayout>
2.新建 MyViewPagerAdapter
,继承自 FragmentPagerAdapter
。
public class MyViewPagerAdapter extends FragmentPagerAdapter { private List<Fragment> mFragments; public MyViewPagerAdapter(FragmentManager fm, List<Fragment> mFragments) { super(fm); this.mFragments = mFragments; } @Override public Fragment getItem(int i) { return mFragments.get(i); } @Override public int getCount() { return mFragments == null ? 0 : mFragments.size(); } }
3.初始化 ViewPager
和 MyViewPagerAdapter
,并进行关联。
//添加Fragment到集合中 List<Fragment> mFragmentList = new ArrayList<>(); mFragmentList.add(FragmentHome.newInstance("首页Fragment")); mFragmentList.add(FragmentDiscovery.newInstance("发现Fragment")); mFragmentList.add(FragmentContacts.newInstance("通讯录Fragment")); mFragmentList.add(FragmentMy.newInstance("我的Fragment")); //关联ViewPager与Adapter MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager(), mFragmentList); mViewPager.setAdapter(myViewPagerAdapter);
4.设置 RadioGroup
和 ViewPager
和监听,进行 RadioGroup
和 ViewPager
关联。
//RadioGroup切换监听 关联ViewPager相关页面 mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { switch (checkedId) { case R.id.rbHome: mViewPager.setCurrentItem(0); break; case R.id.rbDiscovery: mViewPager.setCurrentItem(1); break; case R.id.rbContacts: mViewPager.setCurrentItem(2); break; case R.id.rbMe: mViewPager.setCurrentItem(3); break; default: break; } } }); //ViewPager滑动监听 关联相关RadioButton mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { if (position == 0) { mRbHome.setChecked(true); } else if (position == 1) { mRbDiscovery.setChecked(true); } else if (position == 2) { mRbContacts.setChecked(true); } else if (position == 3) { mRbMe.setChecked(true); } } });
5. Activity
完整代码及效果如下。
public class MyFragmentActivity extends AppCompatActivity { private RadioGroup mRadioGroup; private RadioButton mRbHome; private RadioButton mRbDiscovery; private RadioButton mRbContacts; private RadioButton mRbMe; private ViewPager mViewPager; private MyViewPagerAdapter myViewPagerAdapter; private List<Fragment> mFragmentList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_fragment); initView(); //添加Fragment到集合中 mFragmentList = new ArrayList<>(); mFragmentList.add(FragmentHome.newInstance("首页Fragment")); mFragmentList.add(FragmentDiscovery.newInstance("发现Fragment")); mFragmentList.add(FragmentContacts.newInstance("通讯录Fragment")); mFragmentList.add(FragmentMy.newInstance("我的Fragment")); //关联ViewPager与Adapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager(), mFragmentList); mViewPager.setAdapter(myViewPagerAdapter); //默认选中首页 mViewPager.setCurrentItem(0); mRbHome.setChecked(true); //RadioGroup切换监听 关联ViewPager相关页面 mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { switch (checkedId) { case R.id.rbHome: mViewPager.setCurrentItem(0); break; case R.id.rbDiscovery: mViewPager.setCurrentItem(1); break; case R.id.rbContacts: mViewPager.setCurrentItem(2); break; case R.id.rbMe: mViewPager.setCurrentItem(3); break; default: break; } } }); //ViewPager滑动监听 关联相关RadioButton mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if (position == 0) { mRbHome.setChecked(true); } else if (position == 1) { mRbDiscovery.setChecked(true); } else if (position == 2) { mRbContacts.setChecked(true); } else if (position == 3) { mRbMe.setChecked(true); } } @Override public void onPageScrollStateChanged(int state) { } }); } /** * 动态设置四个tab的样式 */ private void initView() { mRadioGroup = findViewById(R.id.radioGroup); mRbHome = findViewById(R.id.rbHome); mRbContacts = findViewById(R.id.rbContacts); mRbDiscovery = findViewById(R.id.rbDiscovery); mRbMe = findViewById(R.id.rbMe); mViewPager = findViewById(R.id.viewPager); setStyle(R.drawable.tab_home_selector, mRbHome); setStyle(R.drawable.tab_contacts_selector, mRbContacts); setStyle(R.drawable.tab_discovery_selector, mRbDiscovery); setStyle(R.drawable.tab_me_selector, mRbMe); } /** * 动态设置每个tab的图片宽高以及文字间距 * * @param selector RadioButton的样式选择器 * @param rb RadioButton的样式选择器 */ private void setStyle(int selector, RadioButton rb) { //定义底部标签图片大小和位置 Drawable drawableHome = getResources().getDrawable(selector); //当这个图片被绘制时,给他绑定一个矩形 ltrb规定这个矩形 drawableHome.setBounds(0, 0, 80, 80); //设置图片在文字的哪个方向 rb.setCompoundDrawables(null, drawableHome, null, null); } }

结语
以上就是Fragment的一些常见使用场景,根据示例,可以变换多种使用形式,这就要求我们举一反三,根据具体业务、具体需求灵活运用。赶快在项目中练习使用吧!
如果你觉得本篇对你有所帮助,欢迎转载分享,标志出处即可,谢谢支持。