Android流行界面结构——Fragment通过ViewPager(带指示器)嵌套Fragment结构的创建方法详解
原创文章,转载请注明出处http://www.cnblogs.com/baipengzhan/p/6287213.html
当前Android流行界面结构的一种——Fragment通过ViewPager嵌套Fragment结构目前非常常用,在本篇文章中,
我们一步一步将其创建出来,非常详细的让大家看到这个界面是如何实现的,下面我们开始吧。
首先我们看一下最终的效果动画,以便大家有个最初的印象。
本文章专注于功能的实现,并没有着重于界面的美观,所以大家看到的效果一般,UI效果需要大家进一步修改。
主要实现思路:
底部导航栏使用RadioGroup,Activity的布局中创建了一个FrameLayout作为Fragment的容器,
点击RadioButton,动态地向FrameLayout中添加Fragment对象。每个Fragment的布局中创建
一个ViewPager,用来盛放下一级子Fragment,每个ViewPager盛放2个子Fragment对象。其实
到这里,基本结构就已经搭建完毕,顺便写上一个ViewPager指示器,让结构更完整。
此处所用的FragmentLayout + RadioGroup底部导航栏的详细讲解请参看以下文章:
http://www.cnblogs.com/baipengzhan/p/6285881.html
那下面我们就开始一步一步实现这个结构吧。
我们创建一个布局,都要从外到内比较好,先把最外层的结构搭建好,一步一步向内层添加。
首先创建出我们界面的布局,上边一个FrameLayout,中间 一条分隔线,下边一个RadioGroup
我们在一个Activity的布局中创建如下的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <!--使用FrameLayout当做盛放Fragment对象的容器-->
<FrameLayout
android:id="@+id/framelayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<!--中间为一条分割线-->
<View
android:background="@color/divider"
android:layout_width="match_parent"
android:layout_height="1dp"/> <!--最下边为RadioGroup-->
<RadioGroup
android:id="@+id/radioGroup"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"> <!--第一个RadioButton-->
<RadioButton
android:id="@+id/button_1"
android:text="button_1"
android:button="@null"
android:textColor="@color/radiobutton_color_selector"
android:background="@drawable/radiobutton_bg_selector"
android:gravity="center"
android:layout_weight="1"
android:drawableTop="@drawable/radiobutton_pic_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!--第二个RadioButton-->
<RadioButton
android:id="@+id/button_2"
android:text="button_2"
android:button="@null"
android:textColor="@color/radiobutton_color_selector"
android:background="@drawable/radiobutton_bg_selector"
android:gravity="center"
android:layout_weight="1"
android:drawableTop="@drawable/radiobutton_pic_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!--第三个RadioButton-->
<RadioButton
android:id="@+id/button_3"
android:text="button_3"
android:button="@null"
android:textColor="@color/radiobutton_color_selector"
android:background="@drawable/radiobutton_bg_selector"
android:gravity="center"
android:layout_weight="1"
android:drawableTop="@drawable/radiobutton_pic_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!--第四个RadioButton-->
<RadioButton
android:id="@+id/button_4"
android:text="button_4"
android:button="@null"
android:textColor="@color/radiobutton_color_selector"
android:background="@drawable/radiobutton_bg_selector"
android:gravity="center"
android:layout_weight="1"
android:drawableTop="@drawable/radiobutton_pic_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /> </RadioGroup> </LinearLayout>
布局中重要属性说明:
①FrameLayout的android:layout_height属性值为0,android:layout_weight属性值为1。这两个属性值配合使用的意义是:
在竖直方向上FrameLayout占满父控件的剩余空间,也就是占据LinearLayout中除去分隔线和RadioGroup的剩余空间。
关于android:layout_weight属性的详细用法请参考http://www.cnblogs.com/baipengzhan/p/6282826.html
②RadioButton的android:button属性值为@null。这个属性值的意义是,去除RadioGroup默认自带显示的小圆圈。
③RadioButton的android:gravity属性值为center。这个属性值的意义是,使RadioButton的内容(图片和文字)居中。注意,内容默认情况没有居中。
④RadioGroup的android:orientation属性值为horizontal。意为,水平布置其中的RadioButton。
⑤RadioButton的android:textColor属性值为@color/radiobutton_color_selector,是一个颜色状态选择器。颜色状态选择器就是一个定义在res/color目录
下的xml文件,color目录需要我们手动创建。颜色状态选择器的代码如下:
<!--?xml version="1.0" encoding="utf-8"?-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="#f00"></item>
<item android:color="#f00" android:state_pressed="true"></item>
<item android:color="#f00" android:state_selected="true"></item>
<!--没被选中时的颜色-->
<item android:color="#000"></item>
</selector>
关于状态选择器的更详细知识,请参考文章http://www.cnblogs.com/baipengzhan/p/6284682.html
⑥RadioButton的android:background属性值为@drawable/radiobutton_bg_selector,这一个背景状态选择器,用来改变背景颜色,代码如下:
<!--?xml version="1.0" encoding="utf-8"?-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@color/radiobutton_bg_selected"></item>
<item android:state_checked="true" android:drawable="@color/radiobutton_bg_selected"></item>
<item android:state_pressed="true" android:drawable="@color/radiobutton_bg_selected"></item>
<!--未被选中-->
<item android:drawable="@color/radiobutton_bg_normal"></item>
</selector>
这个状态选择器是放置在res/drawable目录下的一个普通状态选择器,该选择器的属性android:drawable的属性值不能直接设置颜色,
颜色要封装在values目录下的colors.xml文件中,否则出错。
⑦RadioButton的android:drawableTop属性值为@drawable/radiobutton_pic_selector,是一个普通的状态选择器,用来改变图片,代码如下:
<!--?xml version="1.0" encoding="utf-8"?-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@mipmap/ic_selected"></item>
<item android:state_checked="true" android:drawable="@mipmap/ic_selected"></item>
<item android:state_pressed="true" android:drawable="@mipmap/ic_selected"></item>
<!--未被选中-->
<item android:drawable="@mipmap/ic_launcher"></item>
</selector>
该状态选择器同样放置在res/drawable目录下,选择器的属性值android:drawable属性值变为了图片,注意代码写到此处时,系统可能不会提示,
需要手动将该属性值添加进来。
更多关于状态选择器的知识请参考文章http://www.cnblogs.com/baipengzhan/p/6284682.html
创建出FrameLayout页面盛放的Fragment
我们创建出对应于四个RadioButton的四个Fragment,每个Fragment中盛放一个ViewPager和一个指示器。下边只列出一个Fragment的写法,剩余的相似,请各位朋友自己写写哦。
下面是Fragment_1的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"> <!--MagicIndicator,强大的ViewPager指示器-->
<!--请将本控件放在ViewPager的上边,否则不显示-->
<net.lucode.hackware.magicindicator.MagicIndicator
android:id="@+id/magic_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> <!--创建ViewPager-->
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>
注意:
这里使用的Viewpager指示器为MagicIndicator,一个第三方框架,兼容性和扩展性很好,本文不详细
阐述它的使用方法了,各位朋友可以先将自己的项目添加了它的依赖,然后按照本文设置即可。这个指示
器在布局中要放在ViewPager上边,否则不显示。
接下来是本文章的重点了,是Fragment_1的逻辑处理,请看Fragment_1的实现代码:
package com.example.radiogroup_framelayout; import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import net.lucode.hackware.magicindicator.MagicIndicator;
import net.lucode.hackware.magicindicator.ViewPagerHelper;
import net.lucode.hackware.magicindicator.buildins.UIUtil;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView;
import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ClipPagerTitleView; import java.util.ArrayList;
import java.util.List; /**
* Created by chironmy on 2017/1/13.
*/ public class Fragment_1 extends Fragment { private View mView;
private ViewPager viewPager;
private Fragment_1_1 fragment_1_1;
private Fragment_1_2 fragment_1_2;
private List<Fragment> list;
private String[] titles = new String[]{"title_1_1","title_1_2"}; @Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//注意View对象的重复使用,以便节省资源
/*if(mView == null) {
mView = inflater.inflate(R.layout.fragment_2_layout,container,false);
}*/
//使用以上写法会导致空指针异常,不能使用,应该使用下面的写法 mView = inflater.inflate(R.layout.fragment_1_layout,container,false); //找到ViewPager
viewPager = (ViewPager) mView.findViewById(R.id.viewpager); //创建ViewPager盛放的Fragment对象
fragment_1_1 = new Fragment_1_1();
fragment_1_2 = new Fragment_1_2(); //创建盛放子Fragment对象的集合
//将Fragment对象加入到集合中
list = new ArrayList<>();
list.add(fragment_1_1);
list.add(fragment_1_2); //设置ViewPager
//此处作为参数传入的Fragment管理器,一定要使用getChildFragmentManager方法
//因为此处的ViewPager盛放Fragment,而ViewPager所在的父控件也在一个Fragment布局中
//形成了Fragment嵌套
//若使用常用的方法,则容易造成错误,例如内容不显示,重复加载等等。
MyViewPagerAdapter adapter = new MyViewPagerAdapter(getChildFragmentManager());
viewPager.setAdapter(adapter); //设置初始页面
viewPager.setCurrentItem(0); //设置指示器
//根据以往使用VIewPagerIndicator的经验,指示器一般写在
//ViewPager设置完指示器之后进行,否则容易出错
initIndicator(); return mView;
} //创建ViewPager适配器
public class MyViewPagerAdapter extends FragmentPagerAdapter { public MyViewPagerAdapter(FragmentManager fm) {
super(fm);
} @Override
public Fragment getItem(int position) {
return list.get(position);
} @Override
public int getCount() {
return list.size();
} @Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
} //设置ViewPager指示器,本示例中使用的是MagicIndicator
//将ViewPager指示器的设置全部放置到一个方法中,程序结构清晰
private void initIndicator() {
MagicIndicator magicIndicator = (MagicIndicator) mView.findViewById(R.id.magic_indicator);
magicIndicator.setBackgroundColor(Color.parseColor("#d43d3d"));
CommonNavigator commonNavigator = new CommonNavigator(getContext());
commonNavigator.setSkimOver(true);
int padding = UIUtil.getScreenWidth(getContext()) / 2;
commonNavigator.setRightPadding(padding);
commonNavigator.setLeftPadding(padding);
commonNavigator.setAdapter(new CommonNavigatorAdapter() { @Override
public int getCount() {
return titles == null ? 0 : titles.length;
} @Override
public IPagerTitleView getTitleView(Context context, final int index) {
ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);
clipPagerTitleView.setText(titles[index]);
clipPagerTitleView.setTextColor(Color.parseColor("#f2c4c4"));
clipPagerTitleView.setClipColor(Color.WHITE);
clipPagerTitleView.setTextSize(36);
clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewPager.setCurrentItem(index);
}
});
return clipPagerTitleView;
} @Override
public IPagerIndicator getIndicator(Context context) {
return null;
}
});
magicIndicator.setNavigator(commonNavigator);
ViewPagerHelper.bind(magicIndicator, viewPager); } }
注意:
①创建ViewPager适配器对象的时候,需要传入一个Fragment管理器,因为ViewPager中盛放的
是下一级Fragment,和ViewPager所在的父容器的Fragment形成了嵌套,此时的Fragment管理
器需要使用getChildFragmentManager方法来获得。若使用getFragmentManager方法,会导致
某一页Fragment不显示数据,报出事务正在进行等等错误和异常,这点一定要注意。
②通过布局填充成对象时,我们有时为了节省资源,常常进行非空判断,使用如下写法:
if(mView == null) {
mView = inflater.inflate(R.layout.fragment_2_layout,container,false);
}
但实际运行报出空指针错误,改成以下写法会正常运行:
mView = inflater.inflate(R.layout.fragment_1_layout,container,false);
③此处的ViewPager指示器使用的是MagicIndicator,本程序将其封装到一个方法中进行了所有操作,
大家可以参考GitHub上的源码学习,本文就不赘述了,大家使用其他指示器也可以。
ViewPager中的Fragment创建
子Fragment布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"> <!--创建TextView-->
<TextView
android:text="pager_1_1"
android:textSize="28sp"
android:textColor="#00f"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /> </LinearLayout>
子Fragment对象创建
package com.example.radiogroup_framelayout; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; /**
* Created by chironmy on 2017/1/13.
*/ public class Fragment_1_1 extends Fragment { private View mView; @Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//注意View对象的重复使用,以便节省资源
if(mView == null) {
mView = inflater.inflate(R.layout.fragment_1_1_layout,container,false);
} return mView;
} }
子Fragment只是添加了一个TextView而已,用以区别,实际应用中肯定复杂很多,一般使用比较复杂的RecyclerView。
Activity代码实现
package com.example.radiogroup_framelayout; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup; import java.util.ArrayList;
import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private RadioGroup radioGroup;
private RadioButton button_1;
private RadioButton button_2;
private RadioButton button_3;
private RadioButton button_4;
private Fragment_1 fragment_1;
private Fragment_2 fragment_2;
private Fragment_3 fragment_3;
private Fragment_4 fragment_4;
private List<Fragment> list;
private FrameLayout frameLayout; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //初始化页面
initView();
} //初始化页面
private void initView() {
frameLayout = (FrameLayout) findViewById(R.id.framelayout);
radioGroup = (RadioGroup) findViewById(R.id.radioGroup); //找到四个按钮
button_1 = (RadioButton) findViewById(R.id.button_1);
button_2 = (RadioButton) findViewById(R.id.button_2);
button_3 = (RadioButton) findViewById(R.id.button_3);
button_4 = (RadioButton) findViewById(R.id.button_4); //创建Fragment对象及集合
fragment_1 = new Fragment_1();
fragment_2 = new Fragment_2();
fragment_3 = new Fragment_3();
fragment_4 = new Fragment_4(); //将Fragment对象添加到list中
list = new ArrayList<>();
list.add(fragment_1);
list.add(fragment_2);
list.add(fragment_3);
list.add(fragment_4); //设置RadioGroup开始时设置的按钮,设置第一个按钮为默认值
radioGroup.check(R.id.button_1); //设置按钮点击监听
button_1.setOnClickListener(this);
button_2.setOnClickListener(this);
button_3.setOnClickListener(this);
button_4.setOnClickListener(this); //初始时向容器中添加第一个Fragment对象
addFragment(fragment_1);
} @Override
public void finish() {
ViewGroup viewGroup = (ViewGroup) getWindow().getDecorView();
viewGroup.removeAllViews();
super.finish();
} //点击事件处理
@Override
public void onClick(View v) {
//我们根据参数的id区别不同按钮
//不同按钮对应着不同的Fragment对象页面
switch (v.getId()) {
case R.id.button_1:
addFragment(fragment_1);
break;
case R.id.button_2:
addFragment(fragment_2);
break;
case R.id.button_3:
addFragment(fragment_3);
break;
case R.id.button_4:
addFragment(fragment_4);
break;
default:
break;
} } //向Activity中添加Fragment的方法
public void addFragment(Fragment fragment) { //获得Fragment管理器
FragmentManager fragmentManager = getSupportFragmentManager();
//使用管理器开启事务
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//使用事务替换Fragment容器中Fragment对象
fragmentTransaction.replace(R.id.framelayout,fragment);
//提交事务,否则事务不生效
fragmentTransaction.commit();
} }
可以看到Activity中的代码非诚简单,只是动态的添加Fragment对象而已。
注意:
以上代码中很多可以优化,比如xml文件中大量的属性可以提取样式,等等,这里列出只是为了方便更多水平的读者读懂,请谅解。
其实Fragment对象的容器不止FrameLayout一种,只是这种结构容器对Fragment支持比较好,更为常用,各位小伙伴可以使用其他容器尝试哦,原理是一样的。
本方式利用动态添加Fragment对象到Activity中,其实我们也可以有个设想,使用静态方法添加到容器中,各个Fragment相互重叠,只有一个显示,我还没尝试,
今后有机会了可以这样试一下,各位小伙伴可以试试哦。
好啦,FrameLayout + RadioGroup结构我们到此就讲解完成了,感谢阅读!
Android流行界面结构——Fragment通过ViewPager(带指示器)嵌套Fragment结构的创建方法详解的更多相关文章
- Android ActionBar 关于tab的应用 以及 TabListener的方法详解
actionBar的tab标签应用以及TabListener的方法详解 package com.example.actionBarTest.actionBarTab; import android.a ...
- Android编程之LayoutInflater的inflate方法详解
LayoutInflater的inflate方法,在fragment的onCreateView方法中经常用到: public View onCreateView(LayoutInflater infl ...
- Android源码下载方法详解
转自:http://www.cnblogs.com/anakin/archive/2011/12/20/2295276.html Android源码下载方法详解 相信很多下载过内核的人都对这个很熟悉 ...
- android emulator启动的两种方法详解
android emulator启动的两种方法详解 转https://blog.csdn.net/TTS_Kevin/article/details/7452237 对于android学习者,模 ...
- Android——onCreate( )方法详解(转)
android开发之onCreate( )方法详解 onCreate( )方法是android应用程序中最常见的方法之一,那么,我们在使用onCreate()方法的时候应该注意哪些问题呢? 先看看Go ...
- 《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析
第三章 Android控件架构与自定义控件详解 1.Android控件架构下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的.PhoneWin ...
- Fragment 的生命周期及使用方法详解
Fragment 的基础知识介绍 1.1 概述 1.1.1 特性 By hebang32624 Fragment 是 activity 的界面中的一部分或一种行为.可以把多个 Fragment 组合到 ...
- Android中的onWindowFocusChanged()方法详解
Android中获取手机屏幕的高度和宽度,我们知道在onCreate方法中获取到的值都是为0的,有人说可以在onClick方法中获取值,这个也是个方法 ,但在onWindowFocusChanged方 ...
- net core体系-web应用程序-4net core2.0大白话带你入门-5asp.net core环境变量详解
asp.net core环境变量详解 环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的 ...
随机推荐
- python list生成表达式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式.运用列表生成式,可以写出非常简洁的代码. >>> list(ra ...
- bzoj 4753: [Jsoi2016]最佳团体【01分数规划+二分+树上背包】
01分数规划,二分答案然后把判别式变成Σp[i]-Σs[i]*mid>=0,然后树上背包判断,设f[i][j]为在i点子树里选j个的最大收益,随便背包一下就好 最丧病的是神卡常--转移的时候要另 ...
- 洛谷P4199 万径人踪灭(manacher+FFT)
传送门 题目所求为所有的不连续回文子序列个数,可以转化为回文子序列数-回文子串数 回文子串manacher跑一跑就行了,考虑怎么求回文子序列数 我们考虑,如果$S_i$是回文子序列的对称中心,那么只要 ...
- Oracle更新数据为MD5加密数据
业务场景:在做安全等保问题,需要将原来保存的用户明文密码改成md5加密的密文密码,数据库是Oracle的 首先Oracle要管理员账号登录才可以调md5函数,具体函数是DBMS_OBFUSCATION ...
- websocket实现单聊
server# @File: ws from flask import Flask, request, render_template from geventwebsocket.handler imp ...
- [NOIP2018校模拟赛]T2矩阵分组 Matrix
题目链接: 矩阵分组 分析: 这道题求的是两部分极差当中大的那个的最小值.对于这种求最值的问题,我们很自然(其实并没有)地想到二分答案. 这个题有两个结论: (好像当时看出来了第一个?然后发现下面都不 ...
- linux系统文件目录解析
/bin 二进制可执行命令 /dev 设备文件(硬盘/光驱等) /etc 系统管理和配置文件 /etc/rc.d 启动的配置文件和脚本 /home 用户主目录,下面会有以登录用户名作为文件夹名 ...
- 什么是Servlet容器?(分析很到位)
在本文中,我写了一些关于Web服务器.Servlet容器以及它与JVM的关系的基本概念.我想表达的是,Servlet容器也仅仅不过是一个Java程序. 1. 什么是Web服务器? 想要知道什么是Ser ...
- c语言读取一个文件夹下的全部文件(jpg / png 文件)
#include <cstdio> #include <cstring> #include <unistd.h> #include<dirent.h> ...
- AJPFX总结List的三个子类的特点
ArrayList: 底层数据结构是数组,查询快,增删慢. 线程不安全,效率高. ...