android中实现底部导航栏

1、引言

底部导航栏在app应用中是十分常见了,大部分的安卓应用中也都实现了底部导航栏的功能,这里我就以我以前做的一个简单小说阅读软件为例,为大家演示一下底部导航栏的使用,需要的朋友直接复制代码过去改写就行了。

2、代码实现

2.1、主界面布局

java 复制代码
<?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"
    tools:context=".MainActivity">
    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" />
    <RadioGroup
        android:id="@+id/rg_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#f5f5f5"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="3dp">
        <!--书架-->
        <com.kotlin.custom.DrawableRadioButton
            android:id="@+id/rb_main_book"
            style="@style/bottom_tag_style"
            android:text="书架"
            app:drawableSize="30dp"
            android:clickable="true"
            app:drawableTop="@drawable/book_drawable_selector" />
        <!--书城-->
        <com.kotlin.custom.DrawableRadioButton
            android:id="@+id/rb_main_store"
            style="@style/bottom_tag_style"
            android:text="书城"
            android:clickable="true"
            app:drawableSize="30dp"
            app:drawableTop="@drawable/store_drawable_selector" />
        <!--我的-->
        <com.kotlin.custom.DrawableRadioButton
            android:id="@+id/rb_main_user"
            style="@style/bottom_tag_style"
            android:text="我的"
            android:clickable="true"
            app:drawableSize="30dp"
            app:drawableTop="@drawable/user_drawable_selector" />
    </RadioGroup>
</LinearLayout>

2.2、主界面代码

这里包含了一些进行操作实际功能的代码,大家复制时直接忽略就行。

java 复制代码
package com.kotlin.novel;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.google.gson.Gson;
import com.kotlin.base.BaseFragment;
import com.kotlin.data.UpdateVersion;
import com.kotlin.dialog.UpdateDialogActivity;
import com.kotlin.fragment.BookShelfFragment;
import com.kotlin.fragment.BookStoreFragment;
import com.kotlin.fragment.UserFragment;
import com.kotlin.utils.ServerUtils;
import com.kotlin.utils.SetUiSize;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 主界面导航切换,如果需要添加界面来这里修改
 */
public class MainActivity extends FragmentActivity {
    @Override
    protected void onResume() {//强制竖屏
        if (getRequestedOrientation()!=ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        super.onResume();
    }
    public static MainActivity activity;
    public RadioGroup rg_main;
    private List<BaseFragment> baseFragment;
    private int position;//选中的Fragment的对应的位置
    private Fragment fragment;//上次切换的Fragment
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        View decorView=getWindow().getDecorView();//获取当前界面的DecorView
        int option=View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE |//全屏隐藏状态栏
                View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;//更改文字颜色为深黑色
        decorView.setSystemUiVisibility(option);//设置系统UI元素的可见性
        getWindow().setNavigationBarColor(Color.TRANSPARENT);
        getWindow().setStatusBarColor(Color.TRANSPARENT);//将状态栏设置为透明色
        rg_main = findViewById(R.id.rg_main);
        initFragment();
        setListener();
        activity=this;
        updateVersion();
    }
    private void setListener() {
        rg_main.setOnCheckedChangeListener(new CheckedChangeListener());
        //设置默认选中书架
        rg_main.check(R.id.rb_main_book);
    }
    class CheckedChangeListener implements RadioGroup.OnCheckedChangeListener {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId){
                case R.id.rb_main_book://书架
                    position = 0;
                    break;
                case R.id.rb_main_store://书城
                    position = 1;
                    break;
                case R.id.rb_main_user://我的
                    position = 2;
                    break;
            }
            //根据位置得到对应的Fragment
            BaseFragment to = getFragment();
            //替换
            switchFrament(fragment,to);
        }
    }
    /**
     * @param from 刚显示的Fragment,马上就要被隐藏
     * @param to 马上要切换到的Fragment,马上就要被显示
     */
    private void switchFrament(Fragment from,Fragment to) {
        if(from != to){
            fragment = 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();
                }
            }
        }
    }
    /**
     * 根据位置得到对应的Fragment
     * @return
     */
    private BaseFragment getFragment() {
        BaseFragment fragment = baseFragment.get(position);
        return fragment;
    }
    private void initFragment() {
        baseFragment = new ArrayList<>();
        baseFragment.add(new BookShelfFragment());//书架Fragment
        baseFragment.add(new BookStoreFragment());//书城Fragment
        baseFragment.add(new UserFragment());//我的Fragment
    }
    public boolean exit;//标识是否可以退出
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if(event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
            if(exit) {
                BookShelfFragment.fragment.handler.sendEmptyMessage(1);//刷新书架,取消选择
                return true;//不退出
            }
        }
        return super.onKeyUp(keyCode, event);
    }
    private String result;
    /**
     * 获取更新数据
     */
    private void updateVersion(){
        new Thread(){
            @Override
            public void run() {
                URL url;
                try {
                    url=new URL(ServerUtils.updatePath);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setReadTimeout(5000);
                    connection.setConnectTimeout(5000);
                    int code = connection.getResponseCode();
                    if (code==200){
                        InputStream is = connection.getInputStream();
                        ByteArrayOutputStream bs=new ByteArrayOutputStream();
                        int len=-1;
                        byte[] buffer=new byte[1024];
                        while ((len=is.read(buffer))!=-1){
                            bs.write(buffer,0,len);
                        }
                        result = bs.toString();
                        bs.close();
                        is.close();
                        connection.disconnect();
                    }
                } catch (Exception e) {
                }
                if (result!=null&&!result.equals("")){
                    handler.sendEmptyMessage(0);
                }
            }
        }.start();
    }
    /**
     * 提示
     * @param message
     */
    private void tip(String message){
        runOnUiThread(() -> {
            //更改默认Toast显示方式,需要什么直接调用方法就行
            Toast toast=Toast.makeText(MainActivity.this,
                    message,Toast.LENGTH_SHORT);
            int size= (int) (280/ SetUiSize.displayHeightDp*SetUiSize.displayHeight);
            toast.setGravity(Gravity.TOP,
                    0,size);
            toast.show();
        });
    }
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0:
                {
                    UpdateVersion version = new Gson().fromJson(result, UpdateVersion.class);
                    if (version.getApk_version()>ServerUtils.versionCode){
                        Intent intent=new Intent(MainActivity.this, UpdateDialogActivity.class);
                        intent.putExtra("result",result);
                        startActivity(intent);
                    }
                }
                break;
            }
        }
    };
}

2.3、自定义控件

自定义radiobutton:

java 复制代码
package com.kotlin.custom;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.RadioButton;

import com.kotlin.novel.R;
/**
 * Describe:可控制drawableTop等图片的大小
 */
@SuppressLint("AppCompatCustomView")
public class DrawableRadioButton extends RadioButton {
    private int mDrawableSize;// xml文件中设置的大小
    public DrawableRadioButton(Context context) {
        this(context, null, 0);
    }
    public DrawableRadioButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public DrawableRadioButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        Drawable drawableLeft = null, drawableTop = null, drawableRight = null, drawableBottom = null;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DrawableRadioButton);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.DrawableRadioButton_drawableSize:
                    mDrawableSize = a.getDimensionPixelSize(R.styleable.DrawableRadioButton_drawableSize, 50);
                    break;
                case R.styleable.DrawableRadioButton_drawableTop:
                    drawableTop = a.getDrawable(attr);
                    break;
                case R.styleable.DrawableRadioButton_drawableBottom:
                    drawableRight = a.getDrawable(attr);
                    break;
                case R.styleable.DrawableRadioButton_drawableRight:
                    drawableBottom = a.getDrawable(attr);
                    break;
                case R.styleable.DrawableRadioButton_drawableLeft:
                    drawableLeft = a.getDrawable(attr);
                    break;
            }
        }
        a.recycle();
        setCompoundDrawablesWithIntrinsicBounds(drawableLeft, drawableTop, drawableRight, drawableBottom);
    }
    /**
     * RadioButton上、下、左、右设置图标
     */
    public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        if (left != null) {
            left.setBounds(0, 0, mDrawableSize, mDrawableSize);
        }
        if (right != null) {
            right.setBounds(0, 0, mDrawableSize, mDrawableSize);
        }
        if (top != null) {
            top.setBounds(0, 0, mDrawableSize, mDrawableSize);
        }
        if (bottom != null) {
            bottom.setBounds(0, 0, mDrawableSize, mDrawableSize);
        }
        setCompoundDrawables(left, top, right, bottom);
    }
}

自定义圆形图标。

java 复制代码
package com.kotlin.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import com.kotlin.novel.R;

public class CircleImageView extends androidx.appcompat.widget.AppCompatImageView {
    private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLORDRAWABLE_DIMENSION = 1;
    private static final int DEFAULT_BORDER_WIDTH = 0;
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
    private final RectF mDrawableRect = new RectF();
    private final RectF mBorderRect = new RectF();
    private final Matrix mShaderMatrix = new Matrix();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mBorderPaint = new Paint();
    private int mBorderColor = DEFAULT_BORDER_COLOR;
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    private Bitmap mBitmap;
    private BitmapShader mBitmapShader;
    private int mBitmapWidth;
    private int mBitmapHeight;
    private float mDrawableRadius;
    private float mBorderRadius;
    private boolean mReady;
    private boolean mSetupPending;
    public CircleImageView(Context context) {
        super(context);
    }
    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        super.setScaleType(SCALE_TYPE);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.CircleImageView, defStyle, 0);
        mBorderWidth = a.getDimensionPixelSize(
                R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
        mBorderColor = a.getColor(R.styleable.CircleImageView_border_color,
                DEFAULT_BORDER_COLOR);
        a.recycle();
        mReady = true;
        if (mSetupPending) {
            setup();
            mSetupPending = false;
        }
    }
    @Override
    public ScaleType getScaleType() {
        return SCALE_TYPE;
    }
    @Override
    public void setScaleType(ScaleType scaleType) {
        if (scaleType != SCALE_TYPE) {
            throw new IllegalArgumentException(String.format(
                    "ScaleType %s not supported.", scaleType));
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        if (getDrawable() == null) {
            return;
        }
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius,
                mBitmapPaint);
        if (mBorderWidth != 0) {
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius,
                    mBorderPaint);
        }
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        setup();
    }
    public int getBorderColor() {
        return mBorderColor;
    }
    public void setBorderColor(int borderColor) {
        if (borderColor == mBorderColor) {
            return;
        }
        mBorderColor = borderColor;
        mBorderPaint.setColor(mBorderColor);
        invalidate();
    }
    public int getBorderWidth() {
        return mBorderWidth;
    }
    public void setBorderWidth(int borderWidth) {
        if (borderWidth == mBorderWidth) {
            return;
        }
        mBorderWidth = borderWidth;
        setup();
    }
    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        mBitmap = bm;
        setup();
    }
    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        mBitmap = getBitmapFromDrawable(drawable);
        setup();
    }
    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        mBitmap = getBitmapFromDrawable(getDrawable());
        setup();
    }
    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }
        try {
            Bitmap bitmap;
            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION,
                        COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight(), BITMAP_CONFIG);
            }
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (OutOfMemoryError e) {
            return null;
        }
    }
    private void setup() {
        if (!mReady) {
            mSetupPending = true;
            return;
        }
        if (mBitmap == null) {
            return;
        }
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBitmapHeight = mBitmap.getHeight();
        mBitmapWidth = mBitmap.getWidth();
        mBorderRect.set(0, 0, getWidth(), getHeight());
        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2,
                (mBorderRect.width() - mBorderWidth) / 2);
        mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width()
                - mBorderWidth, mBorderRect.height() - mBorderWidth);
        mDrawableRadius = Math.min(mDrawableRect.height() / 2,
                mDrawableRect.width() / 2);
        updateShaderMatrix();
        invalidate();
    }
    private void updateShaderMatrix() {
        float scale;
        float dx = 0;
        float dy = 0;
        mShaderMatrix.set(null);
        if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width()
                * mBitmapHeight) {
            scale = mDrawableRect.height() / (float) mBitmapHeight;
            dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
        } else {
            scale = mDrawableRect.width() / (float) mBitmapWidth;
            dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
        }
        mShaderMatrix.setScale(scale, scale);
        mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth,
                (int) (dy + 0.5f) + mBorderWidth);
        mBitmapShader.setLocalMatrix(mShaderMatrix);
    }
}

自定义控件的attrs文件。

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--自定义RadioButton,增加了控制图片的功能,不要在themes文件中也加上,否则会报错-->
    <declare-styleable name="DrawableRadioButton">
        <attr name="drawableSize" format="dimension"/>
        <attr name="drawableTop" format="reference"/>
        <attr name="drawableLeft" format="reference"/>
        <attr name="drawableRight" format="reference"/>
        <attr name="drawableBottom" format="reference"/>
    </declare-styleable>
    <declare-styleable name="CircleImageView">
        <attr name="border_width" format="dimension"/>
        <attr name="border_color" format="color" />
    </declare-styleable>
</resources>

3、效果

3.1、书架页面

3.2、书城页面

3.3、我的页面

相关推荐
千里马学框架13 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台13 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone13 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc14 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo16 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun00770016 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼17 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone18 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen18 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone18 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui