zoukankan      html  css  js  c++  java
  • 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结构我们到此就讲解完成了,感谢阅读!  

      

  • 相关阅读:
    Java实例_队列Queue用法
    JAVA中不同类型的取值范围
    面试被问到的题
    关于Maven启动项目各种.xml文件和.properties文件找不到问题的解决
    jupyter使用
    java中“==”和equal区别
    redis持久化的几种方式
    事务是什么,以及事务四个特性
    Java中的String类能否被继承?为什么?以及final和static的区别
    pytest.mark.parametrize 参数化
  • 原文地址:https://www.cnblogs.com/baipengzhan/p/Android_Fragment_ViewPager_Fragment.html
Copyright © 2011-2022 走看看