手把手教您撸一个底部中心凸起的可以添加通知条数的底栏(安卓APP底栏点击第一次选中当前Fragment第二次点击更新当前页面(实现))

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010724819/article/details/78094106

首先我们来看下实现之后的效果动图:



效果还可以吧?哈哈!

下面我们就来一步步做这个效果,这里面的坑还是比较多的,可能文章中大家体验不到,但是在做的时候确实会有很多问题,我这里会在文章中给大家写出来的!

首先,我们来写布局文件,有同学可能会问了,这个功能页面不是可以用RadioButton+RadioGroup的形式做出来吗,那是之前一贯的做法,这种做法确实行之有效,它的最大优势在于屏蔽了大家在底栏单选时候的逻辑,直接设置RadioButton的id相同就能实现单选效果(看不懂这段话的同学们面壁思过去!),我们这里的思路是,将困难分解,并且逐个击破,底下的每个item都是一个小的布局:


学习平台这里,咱们作为例子,布局如下:

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="学习平台"
                    android:textColor="@color/main_bottom_tv_color"/>

                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="5dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/study_selector"/>
            </FrameLayout>
看不懂的同学自己打自己一下!嗯!

其他四个模块大家请自行脑补一下!除了id和selector不同,其他都是保持一致的!

咱们继续,我们这里把高度写死 

 android:layout_height="65dp"

大家不要吐槽为啥会写死了,因为基本上都是写死的,这种布局始终会在当前页面的最下面,图片和文字大小也一般会固定,所以常规来说都是这样。

下面本文第一个重点来了,中间这种突出的底栏怎么做?


其实大家觉得难正常,因为没有找到思路呗,其实只需要在当前底栏布局设置一个参数即可:

android:clipChildren="false"

下面贴出来视频选项的完整代码:

   <FrameLayout
                android:id="@+id/ll_first"
                android:layout_width="0dp"
                android:layout_height="90dp"
                android:layout_weight="1"
                android:background="@android:color/transparent"
                android:clipChildren="false">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="视频"
                    android:textColor="@color/main_bottom_tv_color"/>

                <ImageView
                    android:id="@+id/iv_circle_video"
                    android:layout_width="match_parent"
                    android:layout_height="70dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/video_selector"/>
            </FrameLayout>

接下来贴出来完整的布局代码:

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <data></data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/main_fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="65dp"/>

        <LinearLayout
            android:id="@+id/main_bottome_switcher_container"
            android:layout_width="match_parent"
            android:layout_height="65dp"
            android:layout_alignParentBottom="true"
            android:background="@color/colorTitleBlack"
            android:clipChildren="false"
            android:gravity="bottom"
            android:orientation="horizontal">

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack">

                <TextView
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_gravity="right"
                    android:background="@drawable/bg_red_point_style"
                    android:gravity="center"
                    android:text="99+"
                    android:textColor="@color/colorTitleWhite"
                    android:textSize="10sp"
                    android:visibility="gone"/>

                <ImageView
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="5dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/store_selector"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="商城"
                    android:textColor="@color/main_bottom_tv_color"/>

            </FrameLayout>

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="学习平台"
                    android:textColor="@color/main_bottom_tv_color"/>

                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="5dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/study_selector"/>
            </FrameLayout>

            <FrameLayout
                android:id="@+id/ll_first"
                android:layout_width="0dp"
                android:layout_height="90dp"
                android:layout_weight="1"
                android:background="@android:color/transparent"
                android:clipChildren="false">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="视频"
                    android:textColor="@color/main_bottom_tv_color"/>

                <ImageView
                    android:id="@+id/iv_circle_video"
                    android:layout_width="match_parent"
                    android:layout_height="70dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/video_selector"/>
            </FrameLayout>

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack"
                android:visibility="gone">

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/live_selector"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="直播"
                    android:textColor="@color/main_bottom_tv_color"/>
            </FrameLayout>

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack">

                <ImageView
                    android:layout_width="35dp"
                    android:layout_height="35dp"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="5dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/robot_selector"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="语音"
                    android:textColor="@color/main_bottom_tv_color"/>
            </FrameLayout>

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="@color/colorTitleBlack">

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="32dp"
                    android:layout_marginTop="5dp"
                    android:background="@android:color/transparent"
                    android:src="@drawable/person_selector"/>

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="20dp"
                    android:layout_gravity="bottom"
                    android:gravity="center"
                    android:text="个人中心"
                    android:textColor="@color/main_bottom_tv_color"/>
            </FrameLayout>
        </LinearLayout>

    </RelativeLayout>

</layout>

就这样基本的就能跑起来了。


这里的布局有个坑啊,就是当前页面如果用相对布局,把底栏放在最下面,然后上面的布局(一般是Fragment)切换,直接写成above会形成一个覆盖,底栏中间的视频模块会被覆盖,这里因为底栏写死了高度,我就直接marginBottom了,很好的解决了这个问题!


但是大家会发现这样做的话是没有点击逻辑的,是的,因为我们这边是使用普通布局来做的,这样有个好处就是灵活性比较强,但是有个缺点就是需要去自己处理点击的逻辑。

下面来说说解决思路:

每次点击我们都可以得到相应的事件,如果单个控件去添加监听,非常的麻烦,我们这里通过一个巧妙的办法来注册监听事件:

我们通过获取底部的最外层布局,然后遍历到每个子布局,接着添加点击事件就可以了:

    //初始化布局
    private void initViews() {

        int count = binding.mainBottomeSwitcherContainer.getChildCount();
        for (int i = 0; i < count; i++) {
            FrameLayout childAt = (FrameLayout) binding.mainBottomeSwitcherContainer.getChildAt(i);
            childAt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int index = binding.mainBottomeSwitcherContainer.indexOfChild(view);
                   
                }


            });
        }

//        binding.civVideo.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View view) {
//                startFragmentAdd(2);
//                changeUI(2);
//            }
//        });
    }

然后呢?我们来处理下底栏选中和为被选中的逻辑:

   //通过点击的时候来改变底部导航栏的UI
    private void changeUI(int index) {
//        ToastUtil.showText(MainActivity.this, index + "");
//        if (index == 2) {
//            binding.civVideo.setImageResource(R.mipmap.video_on);
//        } else {
//            binding.civVideo.setImageResource(R.mipmap.video_off);
//        }

        if (beforePosition != -1) {
            if (beforePosition == index) {
                //这里大家可以加上底栏二次点击你想要实现相应的操作
            }
        }
        int childCount = binding.mainBottomeSwitcherContainer.getChildCount();
        for (int i = 0; i < childCount; i++) {
            if (i == index) {
                setEnable(binding.mainBottomeSwitcherContainer.getChildAt(i), false);
            } else {
                setEnable(binding.mainBottomeSwitcherContainer.getChildAt(i), true);
            }
        }
        beforePosition = index;
    }


    /**
     * 将每个Item中的所用控件状态一同改变
     * 由于我们处理一个通用的代码,那么Item可能会有很多层,所以我们需要使用递归
     *
     * @param item
     * @param b
     */
    private void setEnable(View item, boolean b) {
        if (!(item instanceof FrameLayout)) {
            item.setEnabled(b);
        } else {
            item.setEnabled(true);
        }
        if (item instanceof ViewGroup) {
            int childCount = ((ViewGroup) item).getChildCount();
            for (int i = 0; i < childCount; i++) {
                setEnable(((ViewGroup) item).getChildAt(i), b);
            }
        }
    }

我们是通过设置Enable以及Disable来设置额,非常方便。
 if (!(item instanceof FrameLayout)) {
            item.setEnabled(b);
        } else {
            item.setEnabled(true);
        }
setEnable方法里面的这句代码看似简单,实则暗藏玄机,我们这样做的目的是不让底栏的单个布局事件被设置成Disable,防止不能响应第二次点击的事件。这样做的好处就是在未来我们看可以利用这个来实现类似于京东点击当前页面的底栏,更新当前页面的效果。

好的,基本上主要的代码逻辑已经说完,下面我们来聊一聊底栏上的数字红点。

其实,我第一次碰到这个红点的时候也是没啥思路,但时候来发现TextView可以设置背景色,后来突发奇想,能不能设置红色圆形的shape,然后直接设置图片上的数字就可以实现这种效果?事实证明这种想法是可行的,而且非常容易实现。

首先看下布局:

    <TextView
                    style="@style/myRedPointTextStyle"
                    android:text="99+"
                    android:visibility="visible"/>
抽取了Style,方便统一管理:
    <style name="myRedPointTextStyle">
        <item name="android:layout_width">20dp</item>
        <item name="android:layout_height">20dp</item>
        <item name="android:layout_gravity">right</item>
        <item name="android:background">@drawable/bg_red_point_style</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">@color/colorTitleWhite</item>
        <item name="android:textSize">10sp</item>
    </style>
这样就实现啦,哈哈,是不是非常简单呢?


关于这里的消息更新,我提一句,直接用EventBus即可,在主页面BaseActivity注册,然后Subscribe一下,相应一下之后更新数字就行!



代码是最好的老师,下面放出代码:


MyBottomListShowTest,点击打开链接


加油!


永远不是别人说你什么样,你就是什么样,而是你想成为什么样,才会成为什么样!


猜你喜欢

转载自blog.csdn.net/u010724819/article/details/78094106