Android开发技巧——使用Drawable实现小红点

简介: 在产品的设计中,总难免需要我们开发去实现各种各样的小红点,小红点,小红点。 通常,我们可能会这样做: 用一个View实现小红点,放在相对布局里,设置好内边距或外边距,让它位于图片的右上角。

在产品的设计中,总难免需要我们开发去实现各种各样的小红点,小红点,小红点。
导航栏小红点
侧滑菜单项里的小红点
消息列表的小红点

通常,我们可能会这样做:
QQ截图20170105101941.jpg
用一个View实现小红点,放在相对布局里,设置好内边距或外边距,让它位于图片的右上角。
或者是给图片套一个相对布局,设置好图片的外边距,然后把表示小红点的View放在这个相对布局里面的右上角。

这个应该是最简洁直观的实现方法。然而,它也有它的局限之处。

比如在我这次的开发当中,一开始只是需要实现如下的界面:
QQ截图20170105102453.jpg
QQ截图20170105102709.jpg
为了省事,我当然是直接用AndroidStudio提供的侧滑菜单的模板了,然后再稍作改动,设置一下导航栏的按钮图标和内容布局,写一下侧滑Header的布局,再写一下侧滑菜单的menu.xml文件,就完成了。

在完成了这些,其他功能开发到一半的时候才说要在这两个界面增加小红点。然而,我们的标题栏用的是toolbar,默认对于这个导航图标的设置是只能通过toolbar.setNavigationIcon(Drawable icon)toolbar.setNavigationIcon(int resId)来设置一个图片上去的,并不能在里面添加一个小红点的View。
另外,我们的侧滑菜单,也是通过在menu资源文件夹里通过如下方式来定义的:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_wallet"
        android:icon="@drawable/icon_menu_wallet"
        android:title="@string/menu_my_wallet"/>
    <item
        android:id="@+id/nav_plate"
        android:icon="@drawable/icon_menu_plate"
        android:title="@string/menu_my_vehicle"/>
    <!--其他菜单项略-->
</menu>

它也只是指定图标和文字,并不能指定小红点。

如果说只为实现这两个小红点,就要自己去做toolbar及侧滑菜单的自定义实现,从时间成本上考虑,眼前都要过年了,肯定是难以接受的。好在发现它们两个都可以获取及设置drawable,那我们就有办法了。

思路如下,实现一个Drawable,在它里面套一层原来的Drawable,并且绘制出我们的小红点。好像很简单?support库里的TintAwareDrawable就是这么做的。
接下来思考一下我们要实现的具体功能。
首先,前面的小红点,如果你注意观察会发现,它们的位置不是都以图片的右上角为中心点的。
比如导航栏的小红点左边缘是与图标右边缘对齐的:
QQ截图20170105104247.jpg
消息中心是小红点的右边缘与图标的右边缘对齐的:
QQ截图20170105104215.jpg
另外,我们还需要一个开关,设置是否显示小红点。
最终,代码实现如下:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.view.Gravity;

public class RedPointDrawable extends Drawable {
    private Drawable mDrawable;
    private boolean mShowRedPoint;
    private Paint mPaint;
    private int mRadius;
    private int mGravity = Gravity.CENTER;

    public RedPointDrawable(Context context, Drawable origin) {
        mDrawable = origin;// 原来的drawable
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mPaint.setColor(Color.RED);
        mRadius = context.getResources().getDimensionPixelSize(R.dimen.red_point_radius_small);//小红点半径
    }

    public void setColor(int color) {
        mPaint.setColor(color);
    }

    public void setShowRedPoint(boolean showRedPoint) {
        mShowRedPoint = showRedPoint;
        invalidateSelf();
    }

    public void setRadius(int radius) {
        this.mRadius = radius;
    }

    public void setGravity(int gravity) {
        this.mGravity = gravity;
    }

    @Override
    public void draw(@NonNull Canvas canvas) 
        mDrawable.draw(canvas);//先绘制原图标
        if (mShowRedPoint) {
            // 获取原图标的右上角坐标
            int cx = getBounds().right;
            int cy = getBounds().top;
            // 计算我们的小红点的坐标
            if ((Gravity.LEFT & mGravity) == Gravity.LEFT) {
                cx -= mRadius;
            } else if ((Gravity.RIGHT & mGravity) == Gravity.RIGHT) {
                cx += mRadius;
            }
            if ((Gravity.TOP & mGravity) == Gravity.TOP) {
                cy -= mRadius;
            } else if ((Gravity.BOTTOM & mGravity) == Gravity.BOTTOM) {
                cy += mRadius;
            }
            canvas.drawCircle(cx, cy, mRadius, mPaint);//绘制小红点
        }
    }

    @Override
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
        mDrawable.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mDrawable.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return mDrawable.getOpacity();
    }

    @Override
    public int getIntrinsicHeight() {
        return mDrawable.getIntrinsicHeight();//它的高度使用原来的高度
    }

    @Override
    public int getIntrinsicWidth() {
        return mDrawable.getIntrinsicWidth();//它的宽度使用原来的宽度
    }

    @Override
    public void setBounds(@NonNull Rect bounds) {
        super.setBounds(bounds);
        mDrawable.setBounds(bounds);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        mDrawable.setBounds(left, top, right, bottom);
    }

    public static RedPointDrawable wrap(Context context, Drawable drawable) {
        // 把原来的Drawable包装为一个小红点的Drawable
        if (drawable instanceof RedPointDrawable) {
            return (RedPointDrawable) drawable;
        }
        return new RedPointDrawable(context, drawable);
    }
}

下面就可以使用它来给我们的导航栏图标设置小红点了。设置导航栏图标的代码改为如下:

        final RedPointDrawable icon = new RedPointDrawable(this, ResourcesCompat.getDrawable(getResources(), R.drawable.icon_user, null));
        icon.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
        toolbar.setNavigationIcon(icon);
        // 把drawable添加到我们的成员变量中去,以便后面直接对它进行设置
        //mRedPointView.addRedPointDrawable(redPointDrawable);

然后我们可以把这个icon给保存到成员变量里,通过调用这个drawable的setShowRedPoint(boolean)就可以设置显示及隐藏了。
然后我们还要获取侧滑菜单消息中心的drawable,给它也设置一下:

    private void initForMessageCenterIcon(NavigationView navigationView) {
        Menu menu = navigationView.getMenu();
        int size = menu.size();
        for (int i = 0; i < size; i++) {
            MenuItem item = menu.getItem(i);
            if (item.getItemId() == R.id.nav_message) {
                RedPointDrawable redPointDrawable = RedPointDrawable.wrap(this, item.getIcon());
                redPointDrawable.setGravity(Gravity.LEFT);
                item.setIcon(redPointDrawable);
                // 把drawable添加到我们的成员变量中去,以便后面直接对它进行设置
                //mRedPointView.addRedPointDrawable(redPointDrawable);
            }
        }
    }
目录
相关文章
|
1月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
236 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
203 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
516 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
开发工具 Android开发
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
481 11
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
|
1月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
120 0
|
2月前
|
XML 编解码 Android开发
非常经典的Android开发问题-mipmap图标目录和drawable图标目录的区别和适用场景实战举例-优雅草卓伊凡
非常经典的Android开发问题-mipmap图标目录和drawable图标目录的区别和适用场景实战举例-优雅草卓伊凡
189 0
非常经典的Android开发问题-mipmap图标目录和drawable图标目录的区别和适用场景实战举例-优雅草卓伊凡
|
2月前
|
Java 开发工具 Maven
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
196 6
|
4月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
354 11
|
4月前
|
移动开发 Java 编译器
Kotlin与Jetpack Compose:Android开发生态的演进与架构思考
本文从资深Android工程师视角深入分析Kotlin与Jetpack Compose在Android系统中的技术定位。Kotlin通过空安全、协程等特性解决了Java在移动开发中的痛点,成为Android官方首选语言。Jetpack Compose则引入声明式UI范式,通过重组机制实现高效UI更新。两者结合不仅提升开发效率,更为跨平台战略和现代架构模式提供技术基础,代表了Android开发生态的根本性演进。
179 0
|
8月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
1990 77