千家信息网

Android如何仿QQ微信未读消息小红点BadgeHelper

发表于:2024-12-12 作者:千家信息网编辑
千家信息网最后更新 2024年12月12日,这篇文章给大家分享的是有关Android如何仿QQ微信未读消息小红点BadgeHelper的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Android 小红点 未读消息功能
千家信息网最后更新 2024年12月12日Android如何仿QQ微信未读消息小红点BadgeHelper

这篇文章给大家分享的是有关Android如何仿QQ微信未读消息小红点BadgeHelper的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

Android 小红点 未读消息功能 BadgeHelper

因为最近的项目需求,翻遍github上的未读消息红点开源库, 发现大部分
不能适配不同情况的布局, 所以我写了一个能兼容全部的 !
网上的写法是 继承TextView然后生成一个小红点drawable,设置到背景中去, 然后把目标view外层加一层FrameLayout,然后把小红点添加进去
但这样做的问题来了, 小红点与目标View 会叠起来!, 挡住文字,!!! 看得我瞎了~~~ 而且 他们提供的setOffsetX setpadding 之类的没卵用,你如果想要偏移小红点让它不与下面的View重叠,那是不可能的
所以我的写法是 为了更好的性能,直接继承View然后画小红点背景, 然后把目标view外层加一层LinearLayout 让小红点View放目标的右边,这样就不会重叠
同时 我也支持设置 重叠模式和非重叠模式

这是效果图

由于github账号出问题了,没法上传, 所以写到博客上算了

这只是一个小工具类 供大家学习下我的思路, 就不多说了, 具体实现注释里写的很清楚,不太清楚的可以回复问我

#核心类:
##BadgeHelper .java

 package com.truescend.gofit.views;import android.content.Context;import android.content.res.Resources;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.RectF;import android.support.annotation.IntDef;import android.support.design.widget.TabLayout;import android.util.Log;import android.util.TypedValue;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.FrameLayout;import android.widget.LinearLayout;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.reflect.Field;/** * 作者:东芝(2018/8/23). * 支持 重叠目标模式 和 放目标右上角但不重叠的模式 两种模式 通过setOverlap 设置,  默认为false=不重叠 */public class BadgeHelper extends View {    private static final String TAG = "BadgeHelper";    private float density;    private Paint mTextPaint;    private Paint mBackgroundPaint;    private String text = "0";    private int number;    @Type    private int type = Type.TYPE_POINT;    private boolean isOverlap;    private final RectF rect = new RectF();    private int badgeColor = 0xFFD3321B; //默认的小红点颜色    private int textColor = 0xFFFFFFff;    private float textSize;    private int w;    private int h;    private boolean isSetup;    private boolean mIgnoreTargetPadding;    private boolean isCenterVertical;    private int leftMargin;    private int topMargin;    private int rightMargin;    private int bottomMargin;    @IntDef({Type.TYPE_POINT, Type.TYPE_TEXT})    @Retention(RetentionPolicy.SOURCE)    public @interface Type {        int TYPE_POINT = 0;        int TYPE_TEXT = 1;    }    public BadgeHelper(Context context) {        super(context);    }    private void init(@Type int type, boolean isOverlap) {        this.type = type;        this.isOverlap = isOverlap;        density = getResources().getDisplayMetrics().density;        switch (type) {            case Type.TYPE_POINT:                mBackgroundPaint = new Paint();                mBackgroundPaint.setStyle(Paint.Style.FILL);                mBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);                mBackgroundPaint.setColor(badgeColor);                //计算小红点无文本情况下的小红点大小,  按屏幕像素计算, 如果你有你自己认为更好的算法, 改这里即可                w = h = Math.round(density * 7f);                break;            case Type.TYPE_TEXT:                mBackgroundPaint = new Paint();                mBackgroundPaint.setStyle(Paint.Style.FILL);                mBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);                mBackgroundPaint.setColor(badgeColor);                mTextPaint = new Paint();                mTextPaint.setStyle(Paint.Style.FILL);                mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);                mTextPaint.setColor(textColor);//文本颜色                if (textSize == 0) {                    mTextPaint.setTextSize(density * 10);//文本大小按屏幕像素 计算, 没写死是为了适配各种屏幕,  但如果你有你认为更合理的计算方式 你可以改这里                } else {                    mTextPaint.setTextSize(textSize);//使用自定义大小                }                //计算小红点有文本情况下的小红点大小,  按文本宽高计算, 如果你有你自己认为更好的算法, 改这里即可                float textWidth = getTextWidth("99", mTextPaint);                w = h = Math.round(textWidth * 1.4f);//让背景比文本大一点                break;        }    }    /**     * 设置Margin 可用于做偏移     * @param left     * @param top     * @param right     * @param bottom     * @return     */    public BadgeHelper setBadgeMargins(int left, int top, int right, int bottom) {        leftMargin = left;        topMargin = top;        rightMargin = right;        bottomMargin = bottom;        return this;    }    /**     * 设置Gravity居中     * @return     */    public BadgeHelper setBadgeCenterVertical(  ) {        isCenterVertical = true;        return this;    }    /**     * 设置小红点类型     *     * @param type     * @return     */    public BadgeHelper setBadgeType(@Type int type) {        this.type = type;        return this;    }    /**     * 设置小红点大小, 默认自动适配     *     * @param textSize     * @return     */    public BadgeHelper setBadgeTextSize(int textSize) {        Context c = getContext();        Resources r;        if (c == null) {            r = Resources.getSystem();        } else {            r = c.getResources();        }        this.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize, r.getDisplayMetrics());        return this;    }    /**     * 设置小红点文字颜色, 默认白     *     * @param textColor     * @return     */    public BadgeHelper setBadgeTextColor(int textColor) {        this.textColor = textColor;        return this;    }    /**     * 设置重叠模式, 默认是false(不重叠)     *     * @param isOverlap 是否把小红点重叠到目标View之上     * @return     */    public BadgeHelper setBadgeOverlap(boolean isOverlap) {        return setBadgeOverlap(isOverlap, false);    }    /**     * 设置重叠模式, 默认是false(不重叠)     *     * @param isOverlap             是否把小红点重叠到目标View之上     * @param isIgnoreTargetPadding 是否忽略目标View的padding     * @return     */    public BadgeHelper setBadgeOverlap(boolean isOverlap, boolean isIgnoreTargetPadding) {        this.isOverlap = isOverlap;        this.mIgnoreTargetPadding = isIgnoreTargetPadding;        if (!isOverlap && isIgnoreTargetPadding) {            Log.w(TAG, "警告:只有重叠模式isOverlap=true 设置mIgnoreTargetPadding才有意义");        }        return this;    }    /**     * 设置小红点颜色     *     * @param mBadgeColor     * @return     */    public BadgeHelper setBadgeColor(int mBadgeColor) {        this.badgeColor = mBadgeColor;        return this;    }    /**     * 设置小红点大小     *     * @param w     * @param h     * @return     */    public BadgeHelper setBadgeSize(int w, int h) {        this.w = w;        this.h = h;        return this;    }    /**     * 是否显示     * @param enable     */    public void setBadgeEnable(boolean enable) {       setVisibility(enable?VISIBLE:INVISIBLE);    }    /**     * 设置小红点的文字     *     * @param number     */    public void setBadgeNumber(int number) {        this.number = number;        this.text = String.valueOf(number);        if (isSetup) {            if(number==0){                setVisibility(INVISIBLE);            }else{                setVisibility(VISIBLE);            }            invalidate();        }    }    public void bindToTargetView(TabLayout target, int tabIndex) {        TabLayout.Tab tab = target.getTabAt(tabIndex);        View targetView = null;        View tabView = null;        try {            Field viewField = TabLayout.Tab.class.getDeclaredField("mView");            viewField.setAccessible(true);            targetView = tabView = (View) viewField.get(tab);        } catch (Exception e) {            e.printStackTrace();        }        try {            if (tabView != null) {                Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");//"mIconView"                mTextViewField.setAccessible(true);                targetView = (View) mTextViewField.get(tabView);            }        } catch (Exception e) {            e.printStackTrace();        }        if (targetView != null) {            bindToTargetView(targetView);        }    }    /**     * 绑定小红点到目标View的右上角     *     * @param target     */    public void bindToTargetView(View target) {        init(type, isOverlap);        if (getParent() != null) {            ((ViewGroup) getParent()).removeView(this);        }        if (target == null) {            return;        }        if (target.getParent() instanceof ViewGroup) {            ViewGroup parent = (ViewGroup) target.getParent();            int groupIndex = parent.indexOfChild(target);            parent.removeView(target);            if (isOverlap) {//[小红点与目标View重叠]模式                FrameLayout badgeContainer = new FrameLayout(getContext());                ViewGroup.LayoutParams targetLayoutParams = target.getLayoutParams();                badgeContainer.setLayoutParams(targetLayoutParams);                target.setLayoutParams(new ViewGroup.LayoutParams(                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));                parent.addView(badgeContainer, groupIndex, targetLayoutParams);                badgeContainer.addView(target);                badgeContainer.addView(this);                FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();                if(isCenterVertical) {                    layoutParams.gravity =  Gravity.CENTER_VERTICAL ;                }else{                    layoutParams.gravity = Gravity.END | Gravity.TOP;                }                if (mIgnoreTargetPadding) {                    layoutParams.rightMargin = target.getPaddingRight() - w;                    layoutParams.topMargin = target.getPaddingTop() - h / 2;                }                setLayoutParams(layoutParams);            } else {//[小红点放右侧]模式                LinearLayout badgeContainer = new LinearLayout(getContext());                badgeContainer.setOrientation(LinearLayout.HORIZONTAL);                ViewGroup.LayoutParams targetLayoutParams = target.getLayoutParams();                badgeContainer.setLayoutParams(targetLayoutParams);                target.setLayoutParams(new ViewGroup.LayoutParams(                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));                parent.addView(badgeContainer, groupIndex, targetLayoutParams);                badgeContainer.addView(target);                badgeContainer.addView(this);                if(isCenterVertical) {                    badgeContainer.setGravity(Gravity.CENTER_VERTICAL);                }            }            boolean hasSetMargin = leftMargin>0||topMargin>0||rightMargin>0||bottomMargin>0;            if (hasSetMargin&&getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {                ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) getLayoutParams();                p.setMargins(leftMargin, topMargin, rightMargin, bottomMargin);                setLayoutParams(p);            }            isSetup = true;        } else if (target.getParent() == null) {            throw new IllegalStateException("目标View不能没有父布局!");        }        if(number==0){            setVisibility(INVISIBLE);        }else{            setVisibility(VISIBLE);        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        if (w > 0 && h > 0) {            setMeasuredDimension(w, h);        } else {            throw new IllegalStateException("如果你自定义了小红点的宽高,就不能设置其宽高小于0 ,否则请不要设置!");        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //这里不用解释了 很简单 就是画一个圆形和文字        rect.left = 0;        rect.top = 0;        rect.right = getWidth();        rect.bottom = getHeight();        canvas.drawRoundRect(rect, getWidth() / 2, getWidth() / 2, mBackgroundPaint);        if (type == Type.TYPE_TEXT) {            float textWidth = getTextWidth(text, mTextPaint);            float textHeight = getTextHeight(text, mTextPaint);            canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mTextPaint);        }    }    private float getTextWidth(String text, Paint p) {        return p.measureText(text, 0, text.length());    }    private float getTextHeight(String text, Paint p) {        Rect rect = new Rect();        p.getTextBounds(text, 0, text.length(), rect);        return rect.height();    }}

#使用示例:

public class Main2Activity extends AppCompatActivity {    private TextView mVTypeA;    private TextView mVTypeB;    private TextView mVTypeC;    private TextView mVTypeD;    private ImageView mVTypeE;    private TabLayout mTabLayout;    private void initView() {        mVTypeA = (TextView) findViewById(R.id.vTypeA);        mVTypeB = (TextView) findViewById(R.id.vTypeB);        mVTypeC = (TextView) findViewById(R.id.vTypeC);        mVTypeD = (TextView) findViewById(R.id.vTypeD);        mVTypeE = (ImageView) findViewById(R.id.vTypeE);        mTabLayout = (TabLayout) findViewById(R.id.tabLayout);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        initView();        //情况A(有margin时)        new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_POINT)                .setBadgeOverlap(false)                .bindToTargetView(mVTypeA);        //情况B(有padding时)        new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_POINT)                .setBadgeOverlap(true, true)//重叠模式+忽略目标ViewPadding 效果, 什么意思? 你可以把第二个参数写成false看看会发生什么                .bindToTargetView(mVTypeB);        //情况C(默认情况)        BadgeHelper badgeHelperC = new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_TEXT)                .setBadgeOverlap(false);        badgeHelperC.bindToTargetView(mVTypeC);        badgeHelperC.setBadgeNumber(6);//数字模式        //情况D(有权重时)        new BadgeHelper(this)                .setBadgeColor(0xff0f00ff)//顺便演示下 小红点颜色的设置                .setBadgeType(BadgeHelper.Type.TYPE_POINT)                .setBadgeOverlap(false)                .bindToTargetView(mVTypeD);        //情况E(小红点与图片重叠)+ 数字模式        BadgeHelper badgeHelperE = new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_TEXT)                .setBadgeOverlap(true, true);//注意 isIgnoreTargetPadding=true时 和 setBadgeMargins 不能同时使用        //.setBadgeSize(100,100)//设置小红点的大小, 如果未设置则使用默认宽高, 一般默认就好        //.setBadgeTextSize(15) //设置文本大小,  不设置 则使用默认, 一般默认就好        badgeHelperE.bindToTargetView(mVTypeE);        //任意地方可以刷新数字        badgeHelperE.setBadgeNumber(58);//数字        //情况F(TabLayout兼容)        BadgeHelper badgeHelperF = new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_TEXT)                .setBadgeCenterVertical()//红点居中                .setBadgeMargins(10,0,0,0)//偏移一下                .setBadgeOverlap(false);        badgeHelperF.bindToTargetView(mTabLayout, 0);        badgeHelperF.setBadgeNumber(5);        new BadgeHelper(this)                .setBadgeType(BadgeHelper.Type.TYPE_POINT)                .setBadgeCenterVertical()//红点居中                .setBadgeMargins(10,0,0,0)//偏移一下                .setBadgeOverlap(false)                .bindToTargetView(mTabLayout,1);    }}

#布局:

                                                                        

感谢各位的阅读!关于"Android如何仿QQ微信未读消息小红点BadgeHelper"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0