Android底部栏添加消息提醒小红点功能

2016-08-02 Jamling 更多博文 » 博客 » GitHub »

Android QuickAF

原文链接 https://jamling.github.io/2016/08/02/Android-Android-radio-badge/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


目前许多Android App都带有一个底部栏,通过底部栏可以切换Tab,比如微信,QQ都是这种风格。对于像小红点之类的醒目提醒,用得最多的就是BadgeView了,不过,如果底部栏是RadioGroup的话,那么不好意思BadgeView可不支持哦(如果使用BadgeView,RadioGroup就不能愉快地工作了)。那么如何即保留使用RadioGroup又能添加BadgeView的功能呢?请接着阅读本文。

本文阐述的是使用QuickAF的RadioBadgeView来解决RadioButton与BadgeView的冲突。在RadioButton上显示Badge消息提醒,需要将原来的RadioButton替换成RadioBadgeView。

RadioBadgeView

RadioBadgeView继承自RadioButton,可以视为RadioButton的加强版。与RadioButton不同的时,它在初始化时会生成一个BadgeView2对象(BadgeView2用于展现小红点,后文有详细介绍)。在onDraw时,调用BadegView2的draw方法将小红点画到界面上。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBadge(canvas);
    }

    private void drawBadge(Canvas canvas) {
        Point p = getBadgePosition();
        canvas.save();
        canvas.translate(p.x, p.y);
        badgeView.draw(canvas);
        canvas.restore();
    }

    /**
     * Calculate the badge view position, default is the top-right of the radio
     * button with a top drawable.
     *
     * 
     * @return the position of badge view relative to target
     */
    protected Point getBadgePosition() {
        int offw = getMeasuredWidth() >> 1;
        int offh = getMeasuredHeight() >> 1;
        // 注意这里不考虑复杂的布局
        Drawable d = getCompoundDrawables()[1]; // top drawable;
        if (d != null) {
            offw += d.getIntrinsicWidth() >> 1;
            offh -= (getCompoundDrawablePadding() + d.getIntrinsicHeight()
                    + (getPaint().descent() - getPaint().ascent())) / 2;
            offh -= badgeView.getMeasureHeight() / 2;
            if (offh < 0) {
                offh = 0;
            }
        }
        return new Point(offw, offh);
    }

从上面的代码可以看到,drawBadge用来画BadgeView, getBadgePosition()用于获取BadgeView的位置。注意了,这个位置坐标位于top drawable的右上角,如果你的页面要求小红点位于其它地方,比如右下角,那么需要参考本类的实现,重写getBadgePosition()方法。

如何控制显示的数目呢?

请看以下代码:

    public BadgeView2 getBadgeView() {
        return badgeView;
    }

    public int getBadgeCount() {
        return badgeView.getBadgeCount();
    }

    public void setBadgeCount(int count) {
        badgeView.setBadgeCount(count);
    }

    public void incrementBadgeCount(int increment) {
        int count = getBadgeCount();
        setBadgeCount(increment + count);
    }

    public void decrementBadgeCount(int decrement) {
        incrementBadgeCount(-decrement);
    }

像常用的对BadgeView数目的操作,都委托badgeView去执行了。更多的操作,需要通过getBadgeView()返回badgeView来执行。

那么可以认为RadioBadgeView相当于一个Decorator。真正的玄机还在BadgeView2类中。

BadgeView2

BadgeView2与BadgeView很像,熟悉BadgeView的同学阅读BadgeView2的代码应该很容易,由于BadgeView2的代码较长,这里只讲部分重点功能。

设置显示Style

BadgeView2提供三种显示风格

  • 显示背景,背景一般都是一个小圆点
  • 显示数字,比如QQ的消息数目,有最大值限制,可以自定义
  • 都显示(显示背景+显示数字)

代码如下:

    /**
     * Set badge view display style
     * 
     * @param badgeStyle combined value of {@link #STYLE_BACKGROUND}, {@link #STYLE_TEXT}
     * @see BadgeView2#STYLE_BOTH
     * @see BadgeView2#STYLE_BACKGROUND
     * @see BadgeView2#STYLE_TEXT
     *      
     */
    public void setBadgeStyle(int badgeStyle) {
        if (this.badgeStyle != badgeStyle) {
            this.badgeStyle = badgeStyle;
            requestLayout();
        }
    }

设置背景

提供两个API来设置背景

  • 通过指定背景半径(radius)和背景色(bgColor)来设置(背景为ShapeDrawable)
  • 通过设置背景图片(png或9.png)

详细代码如下:

    /**
     * <p>
     * Set shape drawable as background. If set
     * {@link BadgeView2#STYLE_BACKGROUND} style the background will displayed
     * as circle with assigned radius.
     * 
     * </p>
     * <p>
     * <b><em>Note </em></b>
     * If badge count less than 10 or {@link #badgeStyle} set to
     * {@link #STYLE_BACKGROUND} the background will display as circle
     * </p>
     * If radius assigned, set a default horizontal padding of radius also.
     * 
     * @param radius
     *            the radius of circle background under
     *            {@link BadgeView2#STYLE_BACKGROUND} style, px unit
     * @param bgColor
     *            background color, ARGB format
     */
    public void setBadgeBackground(int radius, int bgColor) {
        float[] radiusArray = new float[] { radius, radius, radius, radius,
                radius, radius, radius, radius };
        padding = new Rect(radius, 0, radius, 0);
        RoundRectShape roundRect = new RoundRectShape(radiusArray, null, null);
        ShapeDrawable bgDrawable = new ShapeDrawable(roundRect);
        bgDrawable.getPaint().setColor(bgColor);
        bgDrawable.setPadding(padding);
        this.badgeBackground = bgDrawable;
        requestLayout();
    }

    /**
     * Set badge background, may be a .9.png, to get well display effect, you
     * may call {@link BadgeView2#setBadgePadding(int, int, int, int)} to set
     * paddings
     * 
     * 
     * @param d
     *            background drawable
     * @see BadgeView2#setBadgePadding(int, int, int, int)
     */
    public void setBadgeBackground(Drawable d) {
        if (this.badgeBackground != d) {
            badgeBackground = d;
            requestLayout();
        }
    }    

其它的API就不详细介绍了,有兴趣可以查看QuickAF源代码。

使用方法

QuickAF的sample app中,底部的导航栏使用的就是RadioBageView,另外,在示例中还包含RadioBadgeView的三种使用方式

   int r = AppUtils.dp2px(view.getContext(), 4);
   int ts = AppUtils.sp2px(view.getContext(), 12);

   rb1 = (RadioBadgeView) findViewById(R.id.radio_badge1);
   rb2 = (RadioBadgeView) findViewById(R.id.radio_badge2);
   rb3 = (RadioBadgeView) findViewById(R.id.radio_badge3);

   rb1.getBadgeView().setBadgeBackground(ts >> 1, 0xffff0000);
   rb1.getBadgeView().setTextSize(ts);
   rb1.getBadgeView().setMax(10, null);

   rb2.getBadgeView().setBadgeBackground(
           getResources().getDrawable(android.R.drawable.btn_radio));
   rb2.getBadgeView().setTextColor(0xff00ff00);
   rb2.getBadgeView().setTextSize(ts * 3 / 2);
   rb2.getBadgeView().setBadgePadding(r, 0, r, 0);

   rb3.getBadgeView().setBadgeBackground(
           getResources().getDrawable(R.mipmap.logo));
   rb3.getBadgeView().setTextColor(0xff0000ff);
   rb3.getBadgeView().setTextSize(ts * 2 / 3);

layout中有三个RadioBadgeView,分别为rb1, rb2和rb3。

  • rb1:使用ShapeDrawable作背景,最大数目显示10
  • rb2:使用9.png作背景
  • rb3:使用png图片作背景

布局Layout如下:

<RadioGroup
    android:id="@+id/radioGroup2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:background="#ffeeeeaa"
    android:gravity="center_vertical"
    android:minHeight="55dp"
    android:orientation="horizontal" >

    <cn.ieclipse.af.view.RadioBadgeView
        android:id="@+id/radio_badge1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:button="@null"
        android:drawableTop="@android:drawable/ic_menu_camera"
        android:gravity="center"
        android:padding="8dp"
        android:text="Shape bg" />

    <cn.ieclipse.af.view.RadioBadgeView
        android:id="@+id/radio_badge2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:button="@null"
        android:drawableTop="@android:drawable/ic_menu_save"
        android:gravity="center"
        android:padding="8dp"
        android:text="9png bg" />

    <cn.ieclipse.af.view.RadioBadgeView
        android:id="@+id/radio_badge3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:button="@null"
        android:drawableTop="@android:drawable/ic_menu_manage"
        android:gravity="center"
        android:padding="8dp"
        android:text="png bg" />
</RadioGroup>

大家可以在QuickAF的sample app中修改显示风格,设置padding,增加或减少数目来查看效果。

关于

QuickAF 是一个Android平台上的app快速开发框架,欢迎读者在github star或fork。本人写作水平有限,欢迎广大读者指正,如有问题,可与我直接联系或在我的官方博客中给出评论。

参考

BadgeView: https://github.com/stefanjauker/BadgeView QuickAF: https://github.com/Jamling/QuickAF