撸一个自定义底部导航View 布局+代码实现导航栏

撸一个自定义view,先上想要做成的效果图,可以看到我们默认选则的就是第二个按钮,中间图片不可替换,居中。有选中和非选中时展示不同的图片的效果。并且给外部实现点击时的回调,让外部知道当前点击的是哪个的回调。

不想了解,只想要代码请直接下拉至底部,查看完整代码。

如果想了解Java、Kotlin代码实现点击右侧链接进入 Java、Kotlin代码实现...    

首先,我们使用依赖布局文件的方式来完成,上布局代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="67dp"
    android:background="@mipmap/layout_bottom_bar_background_with_shadow">

    <RadioGroup
        android:id="@+id/radio_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginTop="7dp"
        android:layout_marginBottom="7dp"
        android:background="@color/white"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/radio_home_page_selected_one"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:button="@null"
            android:drawablePadding="4dp"
            android:gravity="center"
            android:text="kotlin1"
            android:textSize="12sp" />

        <RadioButton
            android:id="@+id/radio_home_page_selected_two"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:button="@null"
            android:drawablePadding="4dp"
            android:gravity="center"
            android:text="kotlin2"
            android:textSize="12sp" />

        <android.support.v4.widget.Space
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <RadioButton
            android:id="@+id/radio_home_page_selected_three"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:button="@null"
            android:drawablePadding="4dp"
            android:gravity="center"
            android:text="kotlin3"
            android:textSize="12sp" />

        <RadioButton
            android:id="@+id/radio_home_page_selected_four"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:button="@null"
            android:drawablePadding="4dp"
            android:gravity="center"
            android:text="kotlin4"
            android:textSize="12sp" />

    </RadioGroup>

    <ImageView
        android:id="@+id/radio_home_page_selected_center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:scaleType="fitCenter"
        android:src="@mipmap/ic_launcher" />

</RelativeLayout>

上图中的layout_bottom_bar_background_with_shadow.png是底部资源文件哦,这个需要你自己找一张透明的png图片呢,如果不想这么麻烦可以看我的 Java、Kotlin代码实现...    。可以看到我们用到的是RadioGroup嵌套RadioButton的方式,中间用一个space预留位置,再在外层使用imageView使其展示在中间,这样,我们的布局就已经完成了。先介绍一下布局内部使用的属性,一般具有基础的Android开发水平人员可以直接忽略一下内容,需则看

RadioGroup  单选按钮组
android:layout_alignParentBottom="true"  使布局置于父布局底部
android:layout_marginTop="7dp"           布局距离父布局顶部距离
android:layout_marginBottom="7dp"        布局距离父布局底部距离
android:background="@color/white"        当前布局背景颜色
android:orientation="horizontal"         当前布局的排列方式

RadioButton  单选按钮
android:button="@null"             设置默认的样式为空                    
android:gravity="center"           设置当前布局内部子控件居中
android:drawablePadding="4dp"      设置当前控件内部图片内边距为4dp
android:layout_weight="1"          设置当前控件权重为1,需配合width=0或者hight=0使用
android:text="kotlin1"             设置控件文字内容
android:textSize="12sp"            设置控件文字大小

Space        该控件在本文当做占位控件使用

ImageView 图片控件
android:layout_centerHorizontal="true"   设置控件相当于父布局水平居中
android:src="@mipmap/ic_launcher"        设置图片控件图片资源


接下来,我们新建一个类继承RelativeLayout 实现他的构造方法,我们在这选择两个

package com.goldze.mvvmhabit.app;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

/**
 * 费浩东
 * 2019/1/4
 * Created by fhd
 */
public class MainBottomBarTest extends RelativeLayout{

    public MainBottomBarTest(Context context) {
        super(context);
    }

    public MainBottomBarTest(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

然后在构造方法中,依赖布局以及初始化控件

//第一个按钮
private RadioButton mRadioOne;
//第二个按钮
private RadioButton mRadioTwo;
//中间的按钮
private ImageView mCenter;
//第四个按钮
private RadioButton mRadioThree;
//第五个按钮
private RadioButton mRadioFour;

private void init(Context context) {
        //当前控件依赖布局
        LayoutInflater.from(context).inflate(R.layout.home_bottom_bar, this);
        //获取布局中的控件,实例化到当前对象中
        mRadioOne = findViewById(R.id.radio_home_page_selected_one);
        mRadioTwo = findViewById(R.id.radio_home_page_selected_two);
        mCenter = findViewById(R.id.radio_home_page_selected_center);
        mRadioThree = findViewById(R.id.radio_home_page_selected_three);
        mRadioFour = findViewById(R.id.radio_home_page_selected_four);
}

给每个radioButton设置图片,在init方法中调用setResourcePictures方法

    //默认的资源文件
    int[] defRes = {R.drawable.layout_bottom_bar_select,R.drawable.layout_bottom_bar_select,
            R.drawable.layout_bottom_bar_select,R.drawable.layout_bottom_bar_select};    
    //设置资源图片
    public void setResourcePictures(int[] drawableTop) {
        if (drawableTop.length != 4) {
            Log.e("setResourcePictures", "MainBottomBar中所提供的图片资源必须为4个: ");
            return;
        }
        mRadioOne.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[0]), null, null);
        mRadioTwo.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[1]), null, null);
        mRadioThree.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[2]), null, null);
        mRadioFour.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[3]), null, null);
    }

以下为Resources中的布局代码,请勿一味的复制粘贴,其中两行item内分别代表选中和非选中状态情况显示的图片

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

我们来看看现在的效果,在你的主页加入你写好的自定义View

<com.goldze.mvvmhabit.app.MainBottomBarTest
    android:layout_marginTop="80dp"
    android:id="@+id/bottomBarTest"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

可以看到我们的效果,已经大致出来了,如果不是,请检测以上的操作以及代码是否正确

从上图中效果看来,视觉效果完成的差不多了,接下来我们需要实现已经选中和非选中事件显示,以及第一次进入默认选中的按钮

选中和非选中事件我们的RadioGroup已经帮我做了,显示我们用了<selector>标签实现了。

现在我们缺少一个默认选中按钮,我们知道RadioButton的setChecked方法可以设置RadioButton的选中与否,所以使用以下代码,考虑到外部使用时有可能会遇到随时需要转换页面显示,我们设置一个公开的设置方法对外提供,方便我们自己。

为了记录我们当前的页面,定义了一个mDefaultPage变量,让我们代码内知道当前显示的页面,在init方法中调用setDefaultPage方法

    //不设置默认按钮,即为第一个按钮为默认按钮 记录当前选中按钮
    private Integer mDefaultPage = 0;

    //内部使用,默认使用的页面
    private void setDefaultPage() {
        setDefaultPage(mDefaultPage);
    }

    //设置默认打开的按钮
    public void setDefaultPage(Integer page) {
        mDefaultPage = page ;
        switch (page) {
            case 0:
                mRadioOne.setChecked(true);
                break;
            case 1:
                mRadioTwo.setChecked(true);
                break;
            case 2:
                break;
            case 3:
                mRadioThree.setChecked(true);
                break;
            case 4:
                mRadioFour.setChecked(true);
                break;
        }
    }

接下来,运行看效果,很明显我们进入时默认选中了第一个。

接下来,我们需要给外部实现点击回调,并且设置重复点击时的过滤。首先定义一个interface接口,我们给他起名为onRadioClickListener。

    //按钮点击回调
    public interface onRadioClickListener {
        void onClick(int postion);
    }

实现各个按钮的点击方法,并实现implements View.OnClickListener

    //设置点击方法
    private void initListener() {
        mRadioOne.setOnClickListener(this);
        mRadioTwo.setOnClickListener(this);
        mCenter.setOnClickListener(this);
        mRadioThree.setOnClickListener(this);
        mRadioFour.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.radio_home_page_selected_one:
                clickInterception(0);
                break;
            case R.id.radio_home_page_selected_two:
                clickInterception(1);
                break;
            case R.id.radio_home_page_selected_center:
                mRadioOne.setChecked(false);
                mRadioTwo.setChecked(false);
                mRadioThree.setChecked(false);
                mRadioFour.setChecked(false);
                clickInterception(2);
                break;
            case R.id.radio_home_page_selected_three:
                clickInterception(3);
                break;
            case R.id.radio_home_page_selected_four:
                clickInterception(4);
                break;
        }
    }

    //点击拦截,过滤重复点击
    private void clickInterception(int page) {
        if (mRepeated && page == mDefaultPage){
            return;
        }
        //记录新的页码.
        mDefaultPage = page;
        mListener.onClick(page);
    }

外部实现我们定义的onRadioClickListener即可监听到回调,至此完成了我们的半自定义view

下面附上完整的代码,如果对您有所帮助请动动手指点点赞,关注关注哦,谢谢~

package 你的App包名+项目名称+位置;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;

import com.goldze.mvvmhabit.R;
import com.jakewharton.rxbinding2.view.RxView;

import java.util.concurrent.TimeUnit;

import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

import static me.goldze.mvvmhabit.binding.viewadapter.view.ViewAdapter.CLICK_INTERVAL;

/**
 * 费浩东
 * 2019/1/4
 * Created by fhd
 */
public class MainBottomBar extends RelativeLayout implements View.OnClickListener {

    //默认的资源文件
    int[] defRes = {R.drawable.layout_bottom_bar_select,R.drawable.layout_bottom_bar_select,
            R.drawable.layout_bottom_bar_select,R.drawable.layout_bottom_bar_select};

    //第一个按钮
    private RadioButton mRadioOne;
    //第二个按钮
    private RadioButton mRadioTwo;
    //中间的按钮
    private ImageView mCenter;
    //第四个按钮
    private RadioButton mRadioThree;
    //第五个按钮
    private RadioButton mRadioFour;

    //不设置默认按钮,即为第一个按钮为默认按钮 记录当前选中按钮
    private Integer mDefaultPage = 0;

    //避免重复点击 默认打开
    private Boolean mRepeated = true ;

    //点击回调
    private onRadioClickListener mListener;

    public MainBottomBar(Context context) {
        super(context);
    }

    public MainBottomBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MainBottomBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init(Context context) {
        LayoutInflater.from(context).inflate(R.layout.home_bottom_bar, this);

        mRadioOne = findViewById(R.id.radio_home_page_selected_one);
        mRadioTwo = findViewById(R.id.radio_home_page_selected_two);
        mCenter = findViewById(R.id.radio_home_page_selected_center);
        mRadioThree = findViewById(R.id.radio_home_page_selected_three);
        mRadioFour = findViewById(R.id.radio_home_page_selected_four);

        //设置点击方法
        initListener();

        //设置资源图片
        setResourcePictures(defRes);

        //默认页面
        setDefaultPage();
    }

    private void initListener() {
        mRadioOne.setOnClickListener(this);
        mRadioTwo.setOnClickListener(this);
        mCenter.setOnClickListener(this);
        mRadioThree.setOnClickListener(this);
        mRadioFour.setOnClickListener(this);
    }

    //设置点击回调
    public void setOnRadioClickListener(onRadioClickListener onRadioClickListener) {
        mListener = onRadioClickListener;
    }

    //设置资源图片
    public void setResourcePictures(int[] drawableTop) {
        if (drawableTop.length != 4) {
            Log.e("setResourcePictures", "MainBottomBar中所提供的图片资源必须为4个: ");
            return;
        }
        mRadioOne.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[0]), null, null);
        mRadioTwo.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[1]), null, null);
        mRadioThree.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[2]), null, null);
        mRadioFour.setCompoundDrawablesRelativeWithIntrinsicBounds(null, this.getResources().getDrawable(drawableTop[3]), null, null);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.radio_home_page_selected_one:
                clickInterception(0);
                break;
            case R.id.radio_home_page_selected_two:
                clickInterception(1);
                break;
            case R.id.radio_home_page_selected_center:
                mRadioOne.setChecked(false);
                mRadioTwo.setChecked(false);
                mRadioThree.setChecked(false);
                mRadioFour.setChecked(false);
                clickInterception(2);
                break;
            case R.id.radio_home_page_selected_three:
                clickInterception(3);
                break;
            case R.id.radio_home_page_selected_four:
                clickInterception(4);
                break;
        }
    }

    //点击拦截,过滤重复点击
    private void clickInterception(int page) {
        if (mRepeated && page == mDefaultPage){
            return;
        }
        //记录新的页码.
        mDefaultPage = page;
        mListener.onClick(page);
    }

    //按钮点击回调
    public interface onRadioClickListener {
        void onClick(int postion);
    }

    //内部使用,默认使用的页面
    private void setDefaultPage() {
        setDefaultPage(mDefaultPage);
    }

    //设置是否过滤重复点击
    public void setRepeated(Boolean repeated) {
        this.mRepeated = repeated;
    }

    //设置默认打开的按钮
    public void setDefaultPage(Integer page) {
        mDefaultPage = page ;
        switch (page) {
            case 0:
                mRadioOne.setChecked(true);
                break;
            case 1:
                mRadioTwo.setChecked(true);
                break;
            case 2:
                break;
            case 3:
                mRadioThree.setChecked(true);
                break;
            case 4:
                mRadioFour.setChecked(true);
                break;
        }
    }

}

猜你喜欢

转载自blog.csdn.net/LikeBoke/article/details/86080097