在产品的设计中,总难免需要我们开发去实现各种各样的小红点,小红点,小红点。





通常,我们可能会这样做:



用一个View实现小红点,放在相对布局里,设置好内边距或外边距,让它位于图片的右上角。

或者是给图片套一个相对布局,设置好图片的外边距,然后把表示小红点的View放在这个相对布局里面的右上角。

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

比如在我这次的开发当中,一开始只是需要实现如下的界面:





为了省事,我当然是直接用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就是这么做的。

接下来思考一下我们要实现的具体功能。

首先,前面的小红点,如果你注意观察会发现,它们的位置不是都以图片的右上角为中心点的。

比如导航栏的小红点左边缘是与图标右边缘对齐的:



消息中心是小红点的右边缘与图标的右边缘对齐的:



另外,我们还需要一个开关,设置是否显示小红点。

最终,代码实现如下:

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);
            }
        }
    }

Android开发技巧——使用Drawable实现小红点的更多相关文章

  1. Android开发技巧——大图裁剪

    本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...

  2. Android开发技巧——使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...

  3. Android开发技巧——实现可复用的ActionSheet菜单

    在上一篇<Android开发技巧--使用Dialog实现仿QQ的ActionSheet菜单>中,讲了这种菜单的实现过程,接下来将把它改成一个可复用的控件库. 本文原创,转载请注明出处: h ...

  4. Android开发技巧——自定义控件之增加状态

    Android开发技巧--自定义控件之增加状态 题外话 这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天.不想把这篇拖到下个月,所以还是先硬着头皮写了. ...

  5. Android开发技巧——自定义控件之自定义属性

    Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...

  6. 50个android开发技巧

    50个android开发技巧 http://blog.csdn.net/column/details/androidhacks.html

  7. Android开发技巧——高亮的用户操作指南

    Android开发技巧--高亮的用户操作指南 2015-12-15补记: 发现使用PopupWindow进行遮罩层的显示,在华为P7上会有问题.具体表现为:画出来的高亮部分会偏下.原因为:通过view ...

  8. Android开发技巧——自定义控件之使用style

    Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...

  9. Android开发技巧——自定义控件之组合控件

    Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...

随机推荐

  1. POJ-3268 Silver Cow Party---正向+反向Dijkstra

    题目链接: https://vjudge.net/problem/POJ-3268 题目大意: 有编号为1-N的牛,它们之间存在一些单向的路径.给定一头牛的编号X,其他牛要去拜访它并且拜访完之后要返回 ...

  2. join()的用法

    Python中有join()和os.path.join()两个函数,具体作用如下:    join():    连接字符串数组.将字符串.元组.列表中的元素以指定的字符(分隔符)连接生成一个新的字符串 ...

  3. Linux服务器SSH无法通过DSA证书登录的解决方法

    从openssh7.0开始,ssh-dss密钥被默认禁用. 修改服务器端的openssh设置重新开启 # vim /etc/sshd/sshd_config添加以下选项PubkeyAcceptedKe ...

  4. SQL发音考(搜寻SQL-86标准)

    据我观察,中国的开发者创造了一种独特的SQL发音:/'sɜːkl/,既好听,又好读,挺好的.但是今年我开始做数据库相关的工作,作为一个专业人士,决定对SQL发音进行一些考证. 直接说结论吧,很多人沿用 ...

  5. [LeetCode] Convert BST to Greater Tree 将二叉搜索树BST转为较大树

    Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...

  6. 在iview的Table中添加Select(render)

    首先对Render进行分析,在iview官方的文档中,找到了table插入Button的例子: { title: 'Action', key: 'action', width: 150, align: ...

  7. STL deque

      STL之deque容器详解 Deque 容器 deque容器是C++标准模版库(STL,Standard Template Library)中的部分内容.deque容器类与vector类似,支持随 ...

  8. [COGS 2258][HZOI 2015]复仇的序幕曲

    Description 你还梦不梦痛不痛,回忆这么重你怎么背得动 ----序言 当年的战火硝烟已经渐渐远去,可仇恨却在阿凯蒂王子的心中越来越深 他的叔父三年前谋权篡位,逼宫杀死了他的父王,用铁血手腕平 ...

  9. ●SCOI2018 AFO

    Day2 T3代码: #include<cstdio> #include<cstring> #include<iostream> using namespace s ...

  10. 【luogu3174】【HAOI2009】毛毛虫

    Description 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大. Input 在文本文件 worm.in 中第一行两个整数 N , M ,分 ...