Android涨姿势知识点之你没用过的BadgeDrawable

目录
  • 1.前言
  • 2.效果
  • 3.简介
  • 4.实现拆解
    • 4.1TabLayout
    • 4.2.TextView
    • 4.3.Button
    • 4.4.ImageView
    • 4.5.BottomNavigationView
  • 5.常用API整理
  • 6.源码解析
    • 6.1.BadgeDrawable.create
    • 6.2.BadgeUtils.attachBadgeDrawable
  • 7.Github
  • 8.相关文档
  • 附:Android开发版本和API等级对应关系
  • 总结

1.前言

通常情况下,我们在做小红点效果的时候,会有两种选择:

自定义BadgeView,然后设置给目标Viewxml写一个View,然后设置shape

有的同学可能会想,能实现不就行了吗,是的,代码优不优雅的不重要,代码和人只要有一个能跑就行…

不过,今天来介绍一种不同的方式来实现小红点效果,或许会让你眼前一亮~

2.效果

3.简介

  • 用途:给View添加动态显示信息(小红点提示效果)
  • app主题需使用Theme.MaterialComponents.*
  • api 要求18+ 也就Android 4.3以上(api等级对应关系见文末)

4.实现拆解

4.1TabLayout

xml:

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="#FFFAF0"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/include"
        app:tabIndicator="@drawable/shape_tab_indicator"
        app:tabIndicatorColor="@color/colorPrimary"
        app:tabIndicatorFullWidth="false"
        app:tabMaxWidth="200dp"
        app:tabMinWidth="100dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@color/colorPrimary"
        app:tabTextColor="@color/gray">

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Kotlin" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Flutter" />

    </com.google.android.material.tabs.TabLayout>

kotlin:

    private fun initTabLayout() {
        // 带数字小红点
        mBinding.tabLayout.getTabAt(0)?.let {
            it.orCreateBadge.apply {
                backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.red)
                badgeTextColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.white)
                number = 6
            }
        }

        // 不带数字小红点
        mBinding.tabLayout.getTabAt(1)?.let {
            it.orCreateBadge.apply {
                backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.red)
                badgeTextColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.white)
            }
        }
    }

4.2.TextView

xml:

    <TextView
        android:id="@+id/tv_badge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="小红点示例"
        android:textAllCaps="false"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tab_layout" />

kotlin:

    private fun initTextView() {
        // 在视图树变化
        mBinding.tvBadge.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                BadgeDrawable.create(this@BadgeDrawableActivity).apply {
                    badgeGravity = BadgeDrawable.TOP_END
                    number = 6
                    backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.colorPrimary)
                    isVisible = true
                    BadgeUtils.attachBadgeDrawable(this, mBinding.tvBadge)
                }
                mBinding.tvBadge.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }

4.3.Button

xml:

    <FrameLayout
        android:id="@+id/fl_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:padding="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_badge">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/mb_badge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button小红点示例" />

    </FrameLayout>

kotlin:

    private fun initButton() {
        mBinding.mbBadge.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            @SuppressLint("UnsafeOptInUsageError")
            override fun onGlobalLayout() {
                BadgeDrawable.create(this@BadgeDrawableActivity).apply {
                    badgeGravity = BadgeDrawable.TOP_START
                    number = 6
                    backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.red)
                    // MaterialButton本身有间距,不设置为0dp的话,可以设置badge的偏移量
                    verticalOffset = 15
                    horizontalOffset = 10
                    BadgeUtils.attachBadgeDrawable(this, mBinding.mbBadge, mBinding.flBtn)
                }
                mBinding.mbBadge.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }

关于MaterialButton的使用及解析可查看:Android MaterialButton使用详解,告别shape、selector

4.4.ImageView

xml:

    <FrameLayout
        android:id="@+id/fl_img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:padding="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/fl_btn">

        <com.google.android.material.imageview.ShapeableImageView
            android:id="@+id/siv_badge"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:contentDescription="Image小红点示例"
            android:src="@mipmap/ic_avatar" />

    </FrameLayout>

kotlin:

    private fun initImageView() {
        mBinding.sivBadge.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            @SuppressLint("UnsafeOptInUsageError")
            override fun onGlobalLayout() {
                BadgeDrawable.create(this@BadgeDrawableActivity).apply {
                    badgeGravity = BadgeDrawable.TOP_END
                    number = 99999
                    // badge最多显示字符,默认999+ 是4个字符(带'+'号)
                    maxCharacterCount = 3
                    backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.red)
                    BadgeUtils.attachBadgeDrawable(this, mBinding.sivBadge, mBinding.flImg)
                }
                mBinding.sivBadge.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }

关于ShapeableImageView的使用及解析可查看:Android ShapeableImageView使用详解,告别shape、三方库

4.5.BottomNavigationView

xml:

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/navigation_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:itemBackground="@color/colorPrimary"
        app:itemIconTint="@color/white"
        app:itemTextColor="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/navigation" />

kotlin:

    private fun initNavigationView() {
        mBinding.navigationView.getOrCreateBadge(R.id.navigation_home).apply {
            backgroundColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.red)
            badgeTextColor = ContextCompat.getColor(this@BadgeDrawableActivity, R.color.white)
            number = 9999
        }
    }

TabLayout和BottomNavigationView源码中直接提供了创建BadgeDrawable的api,未提供的使用BadgeUtils

5.常用API整理

API 描述
backgroundColor 背景色
badgeTextColor 文本颜色
alpha 透明度
number 显示的提示数字
maxCharacterCount 最多显示字符数量(99+包括‘+’号)
badgeGravity 显示位置
horizontalOffset 水平方向偏移量
verticalOffset 垂直方向偏移量
isVisible 是否显示

6.源码解析

来一段最简单的代码示例看看:

BadgeDrawable.create(this@BadgeDrawableActivity).apply {
    // ...
    BadgeUtils.attachBadgeDrawable(this, mBinding.mbBadge, mBinding.flBtn)
}

不难发现,有两个关键点:

  • BadgeDrawable.create
  • BadgeUtils.attachBadgeDrawable

下面继续跟一下,看看源码里究竟是做了什么

6.1.BadgeDrawable.create

create实际调用的是构造方法:

  private BadgeDrawable(@NonNull Context context) {
    this.contextRef = new WeakReference<>(context);
    ThemeEnforcement.checkMaterialTheme(context);
    Resources res = context.getResources();
    badgeBounds = new Rect();
    shapeDrawable = new MaterialShapeDrawable();

    badgeRadius = res.getDimensionPixelSize(R.dimen.mtrl_badge_radius);
    badgeWidePadding = res.getDimensionPixelSize(R.dimen.mtrl_badge_long_text_horizontal_padding);
    badgeWithTextRadius = res.getDimensionPixelSize(R.dimen.mtrl_badge_with_text_radius);

    textDrawableHelper = new TextDrawableHelper(/* delegate= */ this);
    textDrawableHelper.getTextPaint().setTextAlign(Paint.Align.CENTER);
    this.savedState = new SavedState(context);
    setTextAppearanceResource(R.style.TextAppearance_MaterialComponents_Badge);
  }

构造方法里有这么一行:ThemeEnforcement.checkMaterialTheme(context); 检测Material主题,如果不是会直接抛出异常

  private static void checkTheme(
      @NonNull Context context, @NonNull int[] themeAttributes, String themeName) {
    if (!isTheme(context, themeAttributes)) {
      throw new IllegalArgumentException(
          "The style on this component requires your app theme to be "
              + themeName
              + " (or a descendant).");
    }
  }

这也是上面为什么说主题要使用Theme.MaterialComponents.*

然后创建了一个文本绘制帮助类,TextDrawableHelper

比如设置文本居中:textDrawableHelper.getTextPaint().setTextAlign(Paint.Align.CENTER);

其他的就是text属性的获取和设置,跟我们平时设置一毛一样,比较好理解。

绘制文本之后怎么显示出来呢?继续跟attachBadgeDrawable

6.2.BadgeUtils.attachBadgeDrawable

    public static void attachBadgeDrawable(@NonNull BadgeDrawable badgeDrawable, @NonNull View anchor, @Nullable FrameLayout customBadgeParent) {
        setBadgeDrawableBounds(badgeDrawable, anchor, customBadgeParent);
        if (badgeDrawable.getCustomBadgeParent() != null) {
            badgeDrawable.getCustomBadgeParent().setForeground(badgeDrawable);
        } else {
            if (USE_COMPAT_PARENT) {
                throw new IllegalArgumentException("Trying to reference null customBadgeParent");
            }
            anchor.getOverlay().add(badgeDrawable);
        }
    }

这里先是判断badgeDrawable.getCustomBadgeParent() != null,这个parent view的类型就是FrameLayout,不为空的情况下,层级前置。

为空的情况下先是判断了if (USE_COMPAT_PARENT),这里其实是对api level的判断

    static {
        USE_COMPAT_PARENT = VERSION.SDK_INT < 18;
    }

核心代码:

anchor.getOverlay().add(badgeDrawable);

如果有同学做过类似全局添加View的需求,这行代码就看着比较熟悉了。

ViewOverlay,视图叠加,也可以理解为浮层,在不影响子view的情况下,可以添加、删除View,这个api就是android 4.3加的,这也是为什么前面说api 要求18+。

ok,至此关于BadgeDrawable的使用和源码解析就介绍完了。

7.Github

https://github.com/yechaoa/MaterialDesign

8.相关文档

附:Android开发版本和API等级对应关系

Platform Version API Level VERSION_CODE
13.0(beta)    
12.0 32 S_V2
12.0 31 S
11.0 30 R
10.0 29 Q
9.0 28 P
8.1 27 O_MR1
8.0 26 O
7.1 25 N_MR1
7.0 24 N
6.0 23 M
5.1 22 LOLLIPOP_MR1
5.0 21 LOLLIPOP
4.4w 20 KITKAT_WATCH
4.4 19 KITKAT
4.3 18 JELLY_BEAN_MR2
4.2 17 JELLY_BEAN_MR1
4.1 16 JELLY_BEAN
4.0.3 15 ICE_CREAM_SANDWICH_MR1
4.0 14 ICE_CREAM_SANDWICH
3.2 13 HONEYCOMB_MR2
3.1 12 HONEYCOMB_MR1
3.0 11 HONEYCOMB
2.3.3-2.3.4 10 GINGERBREAD_MR1
2.3.0-2.3.2 9 GINGERBREAD
2.2 8 FROYO
2.1 7 ECLAIR_MR1
2.0.1 6 ECLAIR_0_1
2.0 5 ECLAIR
1.6 4 DONUT
1.5 3 CUPCAKE
1.1 2 BASE_1_1
1.0 1 BASE

总结

到此这篇关于Android涨姿势知识点之BadgeDrawable的文章就介绍到这了,更多相关Android BadgeDrawable详解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android涨姿势知识点之你没用过的BadgeDrawable

    目录 1.前言 2.效果 3.简介 4.实现拆解 4.1TabLayout 4.2.TextView 4.3.Button 4.4.ImageView 4.5.BottomNavigationView 5.常用API整理 6.源码解析 6.1.BadgeDrawable.create 6.2.BadgeUtils.attachBadgeDrawable 7.Github 8.相关文档 附:Android开发版本和API等级对应关系 总结 1.前言 通常情况下,我们在做小红点效果的时候,会有两种选

  • android自动化测试知识点总结

    本次教程将教大家如何用monkeyrunner进行android的自动化测试,包括环境的搭建.monkeyrunner和uiautomatorviewer工具的使用. 打开eclipse新建一个avd(android模拟器),然后运行这个模拟器.打开eclipse新建一个avd(android模拟器),然后运行这个模拟器. 进入sdk下面的tools目录下运行monkeyrunner. 现在可以执行一些自动化测试的命令啦,这里以点击界面的命令做讲解. 首先是引入monkeyrunner相关的包.

  • android初学者必须掌握的Activity状态的四大知识点(必读)

    这几天一直都在捣鼓android的知识点,兴趣班的老师,讲课太过深奥,天(想到什么就见什么,后后面完全不想听),最后自己找资料总结了在Android学习中很重要的一个组件Activity,那就开始吧! 第一:掌握Activity的四种状态及什么时候触发 首先我们要知道什么是Activity,简单来说Activity其实就是一个屏幕的显示页面.(简单的阐述) 我们知道Activity是由Activity栈进管理,当来到一个新的Activity后,此Activity将被加入到Activity栈顶,之

  • 快速掌握Android屏幕的知识点

    一.首先来介绍下关于PX.PT.PPI.DPI.DP的知识 术语 说明 PX (pixel),像素,屏幕上显示数据的最基本的点 PT (point), 点1pt=1/72英寸 PPI (pixel per inch),每英寸像素数 DPI (dot per inch),每英寸点数 DP 即dip(Density-independent pixel), 设备独立像素1dp=160dpi时1px长度 其中px, pt, dp为长度单位,ppi和dpi为密度单位 密度 ldpi  mdpi hdpi

  • Android+Html5混合开发仿微信朋友圈

    开发之前 大约从去年开始吧, 也可能是前年 Html5好像火得不得了, 不得了...总能从网上听说到 XXX混合开发, 为了紧跟潮流(虽然有点儿晚了), 咱们也看看Android+Html5混合开发是怎样的! 今天带来的案例是微信的朋友圈, 因为我觉得是微信把H5给"捧红了". 不过丑话说在前头, 咱们的仿朋友圈可是"低仿", 只是把混合开发的大致流程说说, 界面可能不堪入目...见谅.. 开发环境 Android Studio 2.2.2 JDK1.7 API 2

  • AndroidStudio项目制作倒计时模块的方法

    前言 大家好,我是 Vic,今天给大家带来AndroidStudio项目制作倒计时模块的概述,希望你们喜欢 项目难度 AndroidStudio项目制作倒计时模块的难度,不是很大,就是主要用了Timer和TimerTask这两个,接着就是现实界面的一些基础效果. 设计界面 做个倒计时的界面就比较好想了,就如下界面控件 填写倒计时时间 获取倒计时时间 显示倒计时 开始计时 停止计时 就在自动创建的activity_main.xml中写入代码: <?xml version="1.0"

  • Android手机注册登录时获取验证码之后倒计时功能(知识点总结)

    app注册界面经常会遇到一个场景:手机注册,点击获取验证码,验证码发送成功之后,开始倒计时 具体代码如下所示: private TimerTask timerTask; private Timer timer; private int time = 5000;//五秒 private int timess; /** * 开始倒计时 */ private void startTimer() { timess = time/1000; tvTime.setText(timess+"S");

  • Android APP瘦身(清除工程中没用到的资源)详解

    清除Android工程中没用到的资源 项目需求一改再改,UI一调再调,结果就是项目中一堆已经用不到但却没有清理的垃圾资源,不说工程大小问题,对新进入项目的人或看其他模块的代码的人来说,这些没清理的资源可能也可能会带来困扰,所以最好还是清理掉这些垃圾,对于一个稍微大一点的工程来说,手工清理明显是不现实的,这就需要一个方法做这些事情. 清理资源文件 要清理没用的资源,首要的工作当然是找到他们,我们知道Anroid SDK中有一个工具叫lint,可以帮助我们查看工程中存在的问题,其中有一项功能就是查找

  • Android 线程优化知识点学习

    目录 前言 一.线程调度原理解析 线程调度的原理 线程调度模型 Android 的线程调度 线程调度小结 二.Android 异步方式汇总 Thread HandlerThread IntentService AsyncTask 线程池 RxJava 三.Android线程优化实战 线程使用准则 线程池优化实战 四.定位线程创建者 如何确定线程创建者 Epic实战 五.优雅实现线程收敛 线程收敛常规方案 基础库如何使用线程 基础库优雅使用线程 前言 在实际项目开发中会频繁的用到线程,线程使用起来

  • Android 内存优化知识点梳理总结

    目录 RAM 和 ROM 常见内存问题 内存溢出 内存泄漏 常见内存泄漏场景 静态变量或单例持有对象 非静态内部类的实例生命周期比外部类更长导致的内存泄漏 Handler 导致的内存泄漏 postDelayed 导致的内存泄漏 View 的生命周期大于 Activity 时导致的内存泄漏 集合中的对象未释放导致内存泄漏 WebView 导致的内存泄漏 内存抖动 解决方案 其他优化点 App 内存过低时主动清理 前言: Android 操作系统给每个进程都会分配指定额度的内存空间,App 使用内存

随机推荐