两种常用的带有底部导航的AndroidUI实现

最近决定,从头整理Android基础知识,和常用基本架构 和框架 ,方便以后使用,更主要是知道自己到底会些什么,突破技术瓶颈!!!

这一篇先从基本上每个应用都会搭建的软件UI 总结做起,这里的软件架构是指对现在流行软件中都会搭建的UI界面底部导航,并切换不同的片段或者跳转页面,如下图:

底部均带有导航栏

主要实现一共是以下两种选择:

1、RadioButton 加帧布局并替换帧布局中的片段 实现点击按钮可切换片段的效果(这里面采用尚硅谷杨老师的封装方法)

2、结合RadioButton 加ViewPager 填充片段实现可点击并支持滑动切换片段的底部导航

一、RadioButton 加帧布局并替换帧布局中的片段

相信这种实现方式,很多人都会,这里面涉及到的主要是片段的生命周期,片段事务的使用,替换,添加,覆盖,还有就是Radiobutton的使用已经很普遍了,写在这里就算是总结,之后如果需要直接过来粘贴了。
效果图如下,很简单的效果,但是这里面加入了一下代码结构设计的思想,方便提升,还有就是防止片段重复,反复的初始化:


片段效果图

1、首先我们需要一个主Activity,然后就是四个片段

片段的生命周期这里就不做赘述了,直接来看这几个片段的创建过程,我是很喜欢这种方式,也为之后写代码提供了一种思路:

首先一个BaseFragment作为基类:这样就定义了一个规范,有时感觉定义接口并没有这样优雅。

package com.wgd.fragment;

import android.content.Context;
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 wangg on 2018/7/26.
 * * 作用:基类,公共类
 * CommonFragment,ThirdPartyFragment,CustomFragment,OtherFragment等都要继承该类
 */
public abstract class BaseFragment extends Fragment {
    //上下文
    protected Context context;

    protected String TAG="FragmentTAG";
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context=getActivity();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return initView();
    }
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initData();
    }
    protected abstract View initView();
    /**
     * 当孩子需要初始化数据,或者联网请求绑定数据,展示数据的 等等可以重写该方法
     */
    protected void initData(){}
}

然后四个片段主要是依照这个抽象类的标准去完成:
举例:

package com.wgd.fragment;

import android.graphics.Color;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;

/**
 * Created by 王国栋 on 2018/7/26.
 * qq 1350802989
 * 微信 WGDLOVELC
 * 你的煎熬,都是因为你的上进心和你的能力不成正比
 */
public class CommonFragment extends BaseFragment{
    TextView textView;
    @Override
    protected View initView() {
        Log.i(TAG, "initView: CommonFragment初始化了");
        textView=new TextView(context);
        textView.setTextSize(20);
        textView.setTextColor(Color.RED);
        textView.setGravity(Gravity.CENTER);
        return textView;
    }
    @Override
    protected void initData() {
        super.initData();
        textView.setText("common");
    }
}

以此类推创建另外三个,最后添加到主Activity中,在Activity中完成片段的添加替换和覆盖操作,什么是覆盖?就是为了防止片段反复初始化!
主Activity代码:

package com.wgd.example1;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RadioGroup;

import com.wgd.fragment.BaseFragment;
import com.wgd.fragment.CommonFragment;
import com.wgd.fragment.CustomFragment;
import com.wgd.fragment.OtherFragment;
import com.wgd.fragment.ThirdPartyFragment;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RadioGroup mRg_main;
    private List<BaseFragment> mBaseFragment;
    /**
     * 选中的Fragment的对应的位置
     */
    private int position;
    /**
     * 上次切换的Fragment
     */
    private Fragment mContent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化View
        initView();
        //初始化Fragment
        initFragment();
        //设置RadioGroup的监听
        setListener();
    }
    private void setListener() {
        mRg_main.setOnCheckedChangeListener(new MyOnCheckedChangeListener());
        //设置默认选中常用框架
        mRg_main.check(R.id.rb_common_frame);
    }

    class MyOnCheckedChangeListener implements RadioGroup.OnCheckedChangeListener {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId){
                case R.id.rb_common_frame://常用框架
                    position = 0;
                    break;
                case R.id.rb_thirdparty://第三方
                    position = 1;
                    break;
                case R.id.rb_custom://自定义
                    position = 2;
                    break;
                case R.id.rb_other://其他
                    position = 3;
                    break;
                default:
                    position = 0;
                    break;
            }
            //根据位置得到对应的Fragment
            BaseFragment to = getFragment();
            //替换
            switchFrament(mContent,to);
        }
    }
    /**
     *
     * @param from 刚显示的Fragment,马上就要被隐藏了
     * @param to 马上要切换到的Fragment,一会要显示
     */
    private void switchFrament(Fragment from,Fragment to) {
        if(from != to){
            mContent = to;
            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            //才切换
            //判断有没有被添加
            if(!to.isAdded()){
                //to没有被添加
                //from隐藏
                if(from != null){
                    ft.hide(from);
                }
                //添加to
                if(to != null){
                    ft.add(R.id.fl_content,to).commit();
                }
            }else{
                //to已经被添加
                // from隐藏
                if(from != null){
                    ft.hide(from);
                }
                //显示to
                if(to != null){
                    ft.show(to).commit();
                }
            }
        }

    }
//    private void switchFrament(BaseFragment fragment) {
//        //1.得到FragmentManger
//        FragmentManager fm = getSupportFragmentManager();
//        //2.开启事务
//        FragmentTransaction transaction = fm.beginTransaction();
//        //3.替换
//        transaction.replace(R.id.fl_content, fragment);
//        //4.提交事务
//        transaction.commit();
//    }

    /**
     * 根据位置得到对应的Fragment
     * @return
     */
    private BaseFragment getFragment() {
        BaseFragment fragment = mBaseFragment.get(position);
        return fragment;
    }

    private void initFragment() {
        mBaseFragment = new ArrayList<>();
        mBaseFragment.add(new CommonFragment());//常用框架Fragment
        mBaseFragment.add(new ThirdPartyFragment());//第三方Fragment
        mBaseFragment.add(new CustomFragment());//自定义控件Fragment
        mBaseFragment.add(new OtherFragment());//其他Fragment
    }

    private void initView() {
        setContentView(R.layout.activity_main);
        mRg_main = (RadioGroup) findViewById(R.id.rg_main);
    }
}

activty_main.xml文件

<?xml version="1.0" encoding="utf-8"?>
<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.wgd.example1.MainActivity">
    <!--标题栏-->
    <include layout="@layout/layout_title" />
    <!--FrameLayout-->
    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <!--底部的RadioGroup-->
    <RadioGroup
        android:id="@+id/rg_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#11000000"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="5dp">


        <RadioButton
            android:id="@+id/rb_common_frame"
            style="@style/bottom_tag_style"
            android:drawableTop="@drawable/rb_common_frame_drawable_selector"
            android:text="常用框架"
            />

        <RadioButton
            android:id="@+id/rb_thirdparty"
            style="@style/bottom_tag_style"
            android:drawableTop="@drawable/rb_thirdparty_drawable_selector"
            android:text="第三方"

            />
        <RadioButton
            android:id="@+id/rb_custom"
            style="@style/bottom_tag_style"
            android:drawableTop="@drawable/rb_custom_drawable_selector"
            android:text="自定义控件" />
        <RadioButton
            android:id="@+id/rb_other"
            style="@style/bottom_tag_style"
            android:drawableTop="@drawable/rb_other_drawable_selector"
            android:text="其他" />

    </RadioGroup>
</LinearLayout>

bottom_tag_style文件

   <style name="bottom_tag_style" >
        <!-- Customize your theme here. -->
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:button">@android:color/transparent</item>
        <item name="android:drawablePadding">3dp</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">@drawable/bottom_textcolor_drawable_selector</item>
        <item name="android:textSize">10sp</item>
    </style>

bottom_textcolor_drawable_selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="false" android:color="#363636"/>
    <item android:state_checked="true" android:color="#3097FD"/>
</selector>

rb_common_frame_drawable_selector文件,另外三个片段一样

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:drawable="@drawable/ic_tab_video"/>
<item android:state_checked="true" android:drawable="@drawable/ic_tab_video_press"/>
</selector>

这个例子RadioButton的字体的颜色是通过Selector变化的,也可以像下面一样,第二种方式实现。

二、RadioButton 加ViewPager 填充片段实现(可点击可滑动)

1、首先同样需要一个主Activity,四个片段(一般应用底部都会分为四个部分,可根据实际需求添加)

底部导航栏的图片,每个主题对应深色和浅色两张图片,分别用来给RedioButton填充背景,作为待选择和被选择的效果。

主Activity布局文件

<?xml version="1.0" encoding="utf-8"?>
<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=".activity.MainActivity">
    <!-- 这里可根据需求添加顶部title布局这个例子就省略了 -->

    <android.support.v4.view.ViewPager
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/viewpager"
        android:background="@color/background"
        >
    </android.support.v4.view.ViewPager>

    <RadioGroup
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_weight="0"
        android:orientation="horizontal"
        android:gravity="center"
        android:background="#E3E2E1"
        android:id="@+id/radiogroup"
        >
        <RadioButton
            style="@style/MyRadioButtonStyle"
            android:id="@+id/r1"
            android:drawableTop="@drawable/radiobtn_selector1"
            android:text="@string/workbank"
            android:checked="true"
            android:textColor="@color/blue"
            />
        <RadioButton
            style="@style/MyRadioButtonStyle"
            android:id="@+id/r2"
            android:drawableTop="@drawable/radiobtn_selector2"
            android:text="@string/jobfair"
            android:textColor="@color/gray"
            />
        <RadioButton
            style="@style/MyRadioButtonStyle"
            android:id="@+id/r3"
            android:drawableTop="@drawable/radiobtn_selector3"
            android:text="@string/news"
            android:textColor="@color/gray"
            />
        <RadioButton
            style="@style/MyRadioButtonStyle"
            android:id="@+id/r4"
            android:drawableTop="@drawable/radiobtn_selector4"
            android:text="@string/mine"
            android:textColor="@color/gray"
            />
    </RadioGroup>
</LinearLayout>

style如下:

 <style name="MyRadioButtonStyle">
        <item name="android:button">@null</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:gravity">center</item>
    </style>

Selecter文件如下(需要几个导航栏就创建几个)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:drawable="@drawable/nav1_01"></item>
    <item android:state_checked="false" android:drawable="@drawable/nav1_02"></item>
</selector>

RedioButton 和 Selecter的用法应该不用多说了,主要是再Style样式里面的配置。
RedioButton 图片下发的字体颜色还可以用这种方式实现,就是先再在xml中设置一个默认颜色,然后在Activty中根据选中状态,修改字体颜色。
Selecter 给选中和未被选中时两个不同颜色的图片即可,注意是state_checked

主Activity中的代码

/**
 * author wanggd
 *
 * 主入口Activity
 */
public class MainActivity extends FragmentActivity
        implements RadioGroup.OnCheckedChangeListener,ViewPager.OnPageChangeListener{
    private RadioButton r1, r2, r3, r4;
    private RadioGroup radioGroup;
    private ViewPager pager;
    private List<Fragment> fraglist;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    private void initView() {
        fraglist = new ArrayList<>();
        fraglist.add(new WorkBankFragment());
        fraglist.add(new JobFairFragment());
        fraglist.add(new NewsFragment());
        fraglist.add(new MineFragment());
        r1 = (RadioButton) findViewById(R.id.r1);
        r2 = (RadioButton) findViewById(R.id.r2);
        r3 = (RadioButton) findViewById(R.id.r3);
        r4 = (RadioButton) findViewById(R.id.r4);
        radioGroup = (RadioGroup) findViewById(R.id.radiogroup);
        radioGroup.setOnCheckedChangeListener(this);
        pager = (ViewPager) findViewById(R.id.viewpager);
        pager.setPageTransformer(true, new DepthPageTransformer());
        pager.addOnPageChangeListener(this);
        pager.setAdapter(new MyAdapter(getSupportFragmentManager()));
    }
    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        if (checkedId == R.id.r1) {
            checkLogin();
            r1.setTextColor(ContextCompat.getColor(this, R.color.blue));
            r2.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r3.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r4.setTextColor(ContextCompat.getColor(this, R.color.gray));
            pager.setCurrentItem(0);
        } else if (checkedId == R.id.r2) {
            checkLogin();
            r2.setTextColor(ContextCompat.getColor(this, R.color.blue));
            r1.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r3.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r4.setTextColor(ContextCompat.getColor(this, R.color.gray));
            pager.setCurrentItem(1);
        } else if (checkedId == R.id.r3) {
            checkLogin();
            r3.setTextColor(ContextCompat.getColor(this, R.color.blue));
            r1.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r2.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r4.setTextColor(ContextCompat.getColor(this, R.color.gray));
            pager.setCurrentItem(2);
        } else if (checkedId == R.id.r4) {
            checkLogin();
            r4.setTextColor(ContextCompat.getColor(this, R.color.blue));
            r1.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r3.setTextColor(ContextCompat.getColor(this, R.color.gray));
            r2.setTextColor(ContextCompat.getColor(this, R.color.gray));
            pager.setCurrentItem(3);
        }
    }
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }
    @Override
    public void onPageSelected(int position) {
        switch (position) {
            case 0:
                r1.setChecked(true);
                break;
            case 1:
                r2.setChecked(true);
                break;
            case 2:
                r3.setChecked(true);
                break;
            case 3:
                r4.setChecked(true);
                break;
            default:
                break;
        }
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
    private class MyAdapter extends FragmentStatePagerAdapter {

        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return fraglist.get(position);
        }

        @Override
        public int getCount() {
            return fraglist.size();
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            //这个地方注释掉可以防止ViewPager加载卡顿的问题
            //super.destroyItem(container, position, object);
        }
    }
}

Fragment 的实现 可以结合第一种方式实现。或者如果java基础薄弱根据实际需要直接创建不同的Fragment,然后每个Fragment一个布局文件也可以,只是会显得杂乱而已
到此,就可以实现结合viewpager的滑动导航,其实也可以结合上面的例子,将代码精简很多,这是很久之前写的代码了,作为例子放到这。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 内容 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayo...
    小狼W阅读 1,613评论 0 10
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,902评论 25 707
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,740评论 22 665
  • 今夜,一定有什么是我错过的 比如,窗外摇曳的树影 又比如,窗台吹乱的落花 那我未错过的呢 几页书之外 必定是一扇打...
    东方云鹤阅读 189评论 0 0
  • 姓名:陈权 公司:青柠养车 【知~学习】 《财富自由》音频打卡第2天 《轻课口语》打卡第418天 【行~实践】 一...
    水青柠阅读 132评论 0 0