Android 自定义消息右上角的数字提示或红点(类似微信或QQ的未读消息提示)

      支持自由定制外观、拖拽消除的MaterialDesign风格Android BadgeView,自定义消息右上角的数字提示或红点(类似微信或QQ的未读消息提示)实现消息数量大于0时显示具体消息数量,最大显示99,大于99设置99+效果

一.效果图:

 

 

 

二.添加依赖快速实现:

1.添加依赖:(也可以不添加依赖直接将自定义类写到自己的项目中)

implementation 'q.rorbin:badgeview:1.1.3'

2.代码实现:

new QBadgeView(context).bindTarget(textview).setBadgeNumber(6);

3.方法说明: 

code 说明
setBadgeNumber 设置Badge数字
setBadgeText 设置Badge文本
setBadgeTextSize 设置文本字体大小
setBadgeTextColor 设置文本颜色
setExactMode 设置是否显示精确模式数值
setBadgeGravity 设置Badge相对于TargetView的位置
setGravityOffset 设置外边距
setBadgePadding 设置内边距
setBadgeBackgroundColor 设置背景色
setBadgeBackground 设置背景图片
setShowShadow 设置是否显示阴影
setOnDragStateChangedListener 打开拖拽消除模式并设置监听
stroke 描边
hide 隐藏Badge

 

  • 请不要在xml中创建Badge
  • Badge和TargetView绑定是采用替换TargetView的Parent方式实现的,同时将Parent的Id和TargetView的Id设置成一样来保证不会在RelativeLayout中出现位置错乱问题,所以在bindTarget后再次使用findViewById(TargetViewId)得到的会是Parent而不是TargetView,此时建议使用Badge.getTargetView方法来获取TargetView

可以参考:https://github.com/qstumn/BadgeView

三.自定义BadgeView:

1.主函数代码:

import android.content.Context;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.util.DisplayMetrics;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.CompoundButton;import android.widget.EditText;import android.widget.GridView;import android.widget.ImageView;import android.widget.RadioButton;import android.widget.RelativeLayout;import android.widget.SeekBar;import android.widget.Switch;import android.widget.TextView;import com.example.m1571.myapplication.badgeview.Badge;import com.example.m1571.myapplication.badgeview.QBadgeView;import java.util.ArrayList;import java.util.List;//import q.rorbin.badgeview.QBadgeView;public class MainActivity extends AppCompatActivity {    TextView textview, tv_offsetx, tv_padding, tv_offsety, tv_numbersize, tv_dragstate;    EditText et_badgenumber, et_badgetext;    ImageView imageview, iv_badgecolor, iv_numbercolor;    Button button, btn_animation;    List radioButtons = new ArrayList<>();    CompoundButton lastRadioButton;    SeekBar seekBar_offsetx, seekBar_padding, seekBar_offsety, seekBar_numbersize;    Switch swicth_exact, swicth_draggable, swicth_shadow;    List badges;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initListener();        initBadge();        swicth_draggable.setChecked(true);    }    private void initBadge() {        badges = new ArrayList<>();        badges.add(new QBadgeView(this).bindTarget(textview).setBadgeNumber(5));        badges.add(new QBadgeView(this).bindTarget(imageview).setBadgeText("PNG").setBadgeTextColor(0x00000000)                .setBadgeGravity(Gravity.BOTTOM | Gravity.END).setBadgeBackgroundColor(0xff03a9f4)                .setBadgeBackground(getResources().getDrawable(R.drawable.shape_round_rect)));        badges.add(new QBadgeView(this).bindTarget(button).setBadgeText("新").setBadgeTextSize(13, true)                .setBadgeBackgroundColor(0xffffeb3b).setBadgeTextColor(0xff000000)                .stroke(0xff000000, 1, true));    }    private void initView() {        textview = (TextView) findViewById(R.id.textview);        tv_offsetx = (TextView) findViewById(R.id.tv_offsetx);        tv_offsety = (TextView) findViewById(R.id.tv_offsety);        tv_padding = (TextView) findViewById(R.id.tv_padding);        tv_numbersize = (TextView) findViewById(R.id.tv_numbersize);        tv_dragstate = (TextView) findViewById(R.id.tv_dragstate);        et_badgenumber = (EditText) findViewById(R.id.et_badgenumber);        et_badgetext = (EditText) findViewById(R.id.et_badgetext);        imageview = (ImageView) findViewById(R.id.imageview);        iv_badgecolor = (ImageView) findViewById(R.id.iv_badgecolor);        iv_numbercolor = (ImageView) findViewById(R.id.iv_numbercolor);        iv_numbercolor = (ImageView) findViewById(R.id.iv_numbercolor);        button = (Button) findViewById(R.id.button);        btn_animation = (Button) findViewById(R.id.btn_animation);        radioButtons.add((RadioButton) findViewById(R.id.rb_st));        radioButtons.add((RadioButton) findViewById(R.id.rb_sb));        RadioButton rb_et = (RadioButton) findViewById(R.id.rb_et);        lastRadioButton = rb_et;        radioButtons.add(rb_et);        radioButtons.add((RadioButton) findViewById(R.id.rb_eb));        radioButtons.add((RadioButton) findViewById(R.id.rb_ct));        radioButtons.add((RadioButton) findViewById(R.id.rb_ce));        radioButtons.add((RadioButton) findViewById(R.id.rb_cb));        radioButtons.add((RadioButton) findViewById(R.id.rb_cs));        radioButtons.add((RadioButton) findViewById(R.id.rb_c));        seekBar_offsetx = (SeekBar) findViewById(R.id.seekBar_offsetx);        seekBar_offsety = (SeekBar) findViewById(R.id.seekBar_offsety);        seekBar_padding = (SeekBar) findViewById(R.id.seekBar_padding);        seekBar_numbersize = (SeekBar) findViewById(R.id.seekBar_numbersize);        swicth_exact = (Switch) findViewById(R.id.swicth_exact);        swicth_draggable = (Switch) findViewById(R.id.swicth_draggable);        swicth_shadow = (Switch) findViewById(R.id.swicth_shadow);    }    private void initListener() {        CompoundButton.OnCheckedChangeListener checkedChangeListener = new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                if (!isChecked) {                    return;                }                if (lastRadioButton != null) {                    lastRadioButton.setChecked(false);                }                lastRadioButton = buttonView;                for (Badge badge : badges) {                    switch (buttonView.getId()) {                        case R.id.rb_st:                            badge.setBadgeGravity(Gravity.START | Gravity.TOP);                            break;                        case R.id.rb_sb:                            badge.setBadgeGravity(Gravity.START | Gravity.BOTTOM);                            break;                        case R.id.rb_et:                            badge.setBadgeGravity(Gravity.END | Gravity.TOP);                            break;                        case R.id.rb_eb:                            badge.setBadgeGravity(Gravity.END | Gravity.BOTTOM);                            break;                        case R.id.rb_ct:                            badge.setBadgeGravity(Gravity.CENTER | Gravity.TOP);                            break;                        case R.id.rb_ce:                            badge.setBadgeGravity(Gravity.CENTER | Gravity.END);                            break;                        case R.id.rb_cb:                            badge.setBadgeGravity(Gravity.CENTER | Gravity.BOTTOM);                            break;                        case R.id.rb_cs:                            badge.setBadgeGravity(Gravity.CENTER | Gravity.START);                            break;                        case R.id.rb_c:                            badge.setBadgeGravity(Gravity.CENTER);                            break;                    }                }            }        };        for (RadioButton rb : radioButtons) {            rb.setOnCheckedChangeListener(checkedChangeListener);        }        SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                for (Badge badge : badges) {                    if (seekBar == seekBar_offsetx || seekBar == seekBar_offsety) {                        int x = seekBar_offsetx.getProgress();                        int y = seekBar_offsety.getProgress();                        tv_offsetx.setText("GravityOffsetX : " + x);                        tv_offsety.setText("GravityOffsetY : " + y);                        badge.setGravityOffset(x, y, true);                    } else if (seekBar == seekBar_padding) {                        tv_padding.setText("BadgePadding : " + progress);                        badge.setBadgePadding(progress, true);                    } else if (seekBar == seekBar_numbersize) {                        tv_numbersize.setText("TextSize : " + progress);                        badge.setBadgeTextSize(progress, true);                    }                }            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }        };        seekBar_offsetx.setOnSeekBarChangeListener(onSeekBarChangeListener);        seekBar_offsety.setOnSeekBarChangeListener(onSeekBarChangeListener);        seekBar_padding.setOnSeekBarChangeListener(onSeekBarChangeListener);        seekBar_numbersize.setOnSeekBarChangeListener(onSeekBarChangeListener);        View.OnClickListener onClickListener = new View.OnClickListener() {            @Override            public void onClick(View v) {                if (v == iv_badgecolor) {                    selectorColor(new OnColorClickListener() {                        @Override                        public void onColorClick(int color) {                            iv_badgecolor.setBackgroundColor(color);                            for (Badge badge : badges) {                                badge.setBadgeBackgroundColor(color);                            }                        }                    });                } else if (v == iv_numbercolor) {                    selectorColor(new OnColorClickListener() {                        @Override                        public void onColorClick(int color) {                            iv_numbercolor.setBackgroundColor(color);                            for (Badge badge : badges) {                                badge.setBadgeTextColor(color);                            }                        }                    });                } else if (v == btn_animation) {                    for (Badge badge : badges) {                        badge.hide(true);                    }                }            }        };        iv_badgecolor.setOnClickListener(onClickListener);        iv_numbercolor.setOnClickListener(onClickListener);        btn_animation.setOnClickListener(onClickListener);        class MyTextWatcher implements TextWatcher {            private EditText editText;            public MyTextWatcher(EditText editText) {                this.editText = editText;            }            @Override            public void beforeTextChanged(CharSequence s, int start, int count, int after) {            }            @Override            public void onTextChanged(CharSequence s, int start, int before, int count) {                try {                    for (Badge badge : badges) {                        if (editText == et_badgenumber) {                            int num = TextUtils.isEmpty(s) ? 0 : Integer.parseInt(s.toString());                            badge.setBadgeNumber(num);                        } else if (editText == et_badgetext) {                            badge.setBadgeText(s.toString());                        }                    }                } catch (Exception e) {                    e.printStackTrace();                }            }            @Override            public void afterTextChanged(Editable s) {            }        }        et_badgenumber.addTextChangedListener(new MyTextWatcher(et_badgenumber));        et_badgetext.addTextChangedListener(new MyTextWatcher(et_badgetext));        CompoundButton.OnCheckedChangeListener onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                for (Badge badge : badges) {                    if (buttonView == swicth_exact) {                        badge.setExactMode(isChecked);                    } else if (buttonView == swicth_draggable) {                        badge.setOnDragStateChangedListener(isChecked ?                                new Badge.OnDragStateChangedListener() {                                    @Override                                    public void onDragStateChanged(int dragState, Badge badge, View targetView) {                                        switch (dragState) {                                            case STATE_START:                                                tv_dragstate.setText("STATE_START");                                                break;                                            case STATE_DRAGGING:                                                tv_dragstate.setText("STATE_DRAGGING");                                                break;                                            case STATE_DRAGGING_OUT_OF_RANGE:                                                tv_dragstate.setText("STATE_DRAGGING_OUT_OF_RANGE");                                                break;                                            case STATE_SUCCEED:                                                tv_dragstate.setText("STATE_SUCCEED");                                                break;                                            case STATE_CANCELED:                                                tv_dragstate.setText("STATE_CANCELED");                                                break;                                        }                                    }                                } : null);                    } else if (buttonView == swicth_shadow) {                        badge.setShowShadow(isChecked);                    }                }            }        };        swicth_exact.setOnCheckedChangeListener(onCheckedChangeListener);        swicth_draggable.setOnCheckedChangeListener(onCheckedChangeListener);        swicth_shadow.setOnCheckedChangeListener(onCheckedChangeListener);    }    private void selectorColor(final OnColorClickListener l) {        final AlertDialog dialog = new AlertDialog.Builder(this).create();        GridView gv = new GridView(this);        gv.setNumColumns(4);        gv.setAdapter(new BaseAdapter() {            int[] colors = new int[]{Color.TRANSPARENT, 0xffffffff, 0xff000000, 0xffe51c23, 0xffE84E40, 0xff9c27b0, 0xff673ab7,                    0xff3f51b5, 0xff5677fc, 0xff03a9f4, 0xff00bcd4, 0xff009688, 0xff259b24, 0xff8bc34a, 0xffcddc39,                    0xffffeb3b, 0xffffc107, 0xffff9800, 0xffff5722, 0xff795548};            @Override            public int getCount() {                return colors.length;            }            @Override            public Object getItem(int position) {                return colors[position];            }            @Override            public long getItemId(int position) {                return position;            }            @Override            public View getView(final int position, View convertView, ViewGroup parent) {                View v = new View(MainActivity.this);                v.setBackgroundColor(colors[position]);                v.setOnClickListener(new View.OnClickListener() {                    @Override                    public void onClick(View v) {                        l.onColorClick(colors[position]);                        dialog.dismiss();                    }                });                DisplayMetrics dm = new DisplayMetrics();                WindowManager wm = (WindowManager) MainActivity.this                        .getSystemService(Context.WINDOW_SERVICE);                wm.getDefaultDisplay().getMetrics(dm);                GridView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,                        (int) (dm.widthPixels / 5f));                v.setLayoutParams(lp);                return v;            }        });        dialog.setView(gv);        dialog.show();        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x33FFFFFF));    }    interface OnColorClickListener {        void onColorClick(int color);    }}

2.主函数布局:

<?xml version="1.0" encoding="utf-8"?>                            

3.相关属性:

dimens.xml:

        16dp    16dp

string.xml:

    My Application    TextView    Button

shape_round_rect.xml:

<?xml version="1.0" encoding="utf-8"?>        

4.实现接口Badge.java:

import android.graphics.PointF;import android.graphics.drawable.Drawable;import android.view.View;/** * 接口 */public interface Badge {    Badge setBadgeNumber(int badgeNum);    int getBadgeNumber();    Badge setBadgeText(String badgeText);    String getBadgeText();    Badge setExactMode(boolean isExact);    boolean isExactMode();    Badge setShowShadow(boolean showShadow);    boolean isShowShadow();    Badge setBadgeBackgroundColor(int color);    Badge stroke(int color, float width, boolean isDpValue);    int getBadgeBackgroundColor();    Badge setBadgeBackground(Drawable drawable);    Badge setBadgeBackground(Drawable drawable, boolean clip);    Drawable getBadgeBackground();    Badge setBadgeTextColor(int color);    int getBadgeTextColor();    Badge setBadgeTextSize(float size, boolean isSpValue);    float getBadgeTextSize(boolean isSpValue);    Badge setBadgePadding(float padding, boolean isDpValue);    float getBadgePadding(boolean isDpValue);    boolean isDraggable();    Badge setBadgeGravity(int gravity);    int getBadgeGravity();    Badge setGravityOffset(float offset, boolean isDpValue);    Badge setGravityOffset(float offsetX, float offsetY, boolean isDpValue);    float getGravityOffsetX(boolean isDpValue);    float getGravityOffsetY(boolean isDpValue);    Badge setOnDragStateChangedListener(OnDragStateChangedListener l);    PointF getDragCenter();    Badge bindTarget(View view);    View getTargetView();    void hide(boolean animate);    interface OnDragStateChangedListener {        int STATE_START = 1;        int STATE_DRAGGING = 2;        int STATE_DRAGGING_OUT_OF_RANGE = 3;        int STATE_CANCELED = 4;        int STATE_SUCCEED = 5;        void onDragStateChanged(int dragState, Badge badge, View targetView);    }}

5.自定义动画BadgeAnimator.java:

import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.ValueAnimator;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PointF;import android.util.Log;import java.lang.ref.WeakReference;import java.util.Random;import static android.content.ContentValues.TAG;/** * 自定义动画 * Animation :https://github.com/tyrantgit/ExplosionField */public class BadgeAnimator extends ValueAnimator {    private BitmapFragment[][] mFragments;    private WeakReference mWeakBadge;    public BadgeAnimator(Bitmap badgeBitmap, PointF center, QBadgeView badge) {        mWeakBadge = new WeakReference<>(badge);        setFloatValues(0f, 1f);        setDuration(500);        mFragments = getFragments(badgeBitmap, center);        addUpdateListener(new AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                QBadgeView badgeView = mWeakBadge.get();                if (badgeView == null || !badgeView.isShown()) {                    cancel();                } else {                    badgeView.invalidate();                }            }        });        addListener(new AnimatorListenerAdapter() {            @Override            public void onAnimationEnd(Animator animation) {                QBadgeView badgeView = mWeakBadge.get();                if (badgeView != null) {                    badgeView.reset();                }            }        });    }    public void draw(Canvas canvas) {        for (int i = 0; i < mFragments.length; i++) {            for (int j = 0; j < mFragments[i].length; j++) {                BitmapFragment bf = mFragments[i][j];                float value = Float.parseFloat(getAnimatedValue().toString());                bf.updata(value, canvas);            }        }    }    private BitmapFragment[][] getFragments(Bitmap badgeBitmap, PointF center) {        int width = badgeBitmap.getWidth();        int height = badgeBitmap.getHeight();        float fragmentSize = Math.min(width, height) / 6f;        float startX = center.x - badgeBitmap.getWidth() / 2f;        float startY = center.y - badgeBitmap.getHeight() / 2f;        BitmapFragment[][] fragments = new BitmapFragment[(int) (height / fragmentSize)][(int) (width / fragmentSize)];        for (int i = 0; i < fragments.length; i++) {            for (int j = 0; j < fragments[i].length; j++) {                BitmapFragment bf = new BitmapFragment();                bf.color = badgeBitmap.getPixel((int) (j * fragmentSize), (int) (i * fragmentSize));                bf.x = startX + j * fragmentSize;                bf.y = startY + i * fragmentSize;                bf.size = fragmentSize;                bf.maxSize = Math.max(width, height);                fragments[i][j] = bf;            }        }        badgeBitmap.recycle();        return fragments;    }    private class BitmapFragment {        Random random;        float x;        float y;        float size;        int color;        int maxSize;        Paint paint;        public BitmapFragment() {            paint = new Paint();            paint.setAntiAlias(true);            paint.setStyle(Paint.Style.FILL);            random = new Random();        }        public void updata(float value, Canvas canvas) {            paint.setColor(color);            x = x + 0.1f * random.nextInt(maxSize) * (random.nextFloat() - 0.5f);            y = y + 0.1f * random.nextInt(maxSize) * (random.nextFloat() - 0.5f);            canvas.drawCircle(x, y, size - value * size, paint);        }    }}

6.dp、px转换工具类DisplayUtil.java:

import android.content.Context;/** * dp px转换工具 */public class DisplayUtil {    public static int dp2px(Context context, float dp) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dp * scale + 0.5f);    }    public static int px2dp(Context context, float pxValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (pxValue / scale + 0.5f);    }}

7.数学计算工具类MathUtil.java:

import android.graphics.PointF;import java.util.List;/** * Created by chqiu on 2017/3/20. */public class MathUtil {    public static final double CIRCLE_RADIAN = 2 * Math.PI;    public static double getTanRadian(double atan, int quadrant) {        if (atan < 0) {            atan += CIRCLE_RADIAN / 4;        }        atan += CIRCLE_RADIAN / 4 * (quadrant - 1);        return atan;    }    public static double radianToAngle(double radian) {        return 360 * (radian / CIRCLE_RADIAN);    }    public static int getQuadrant(PointF p, PointF center) {        if (p.x > center.x) {            if (p.y > center.y) {                return 4;            } else if (p.y < center.y) {                return 1;            }        } else if (p.x < center.x) {            if (p.y > center.y) {                return 3;            } else if (p.y < center.y) {                return 2;            }        }        return -1;    }    public static float getPointDistance(PointF p1, PointF p2) {        return (float) Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));    }    /**     * this formula is designed by mabeijianxi     * website : http://blog.csdn.net/mabeijianxi/article/details/50560361     *     * @param circleCenter The circle center point.     * @param radius       The circle radius.     * @param slopeLine    The slope of line which cross the pMiddle.     */    public static void getInnertangentPoints(PointF circleCenter, float radius, Double slopeLine, List points) {        float radian, xOffset, yOffset;        if (slopeLine != null) {            radian = (float) Math.atan(slopeLine);            xOffset = (float) (Math.cos(radian) * radius);            yOffset = (float) (Math.sin(radian) * radius);        } else {            xOffset = radius;            yOffset = 0;        }        points.add(new PointF(circleCenter.x + xOffset, circleCenter.y + yOffset));        points.add(new PointF(circleCenter.x - xOffset, circleCenter.y - yOffset));    }}

8.自定义QQ消息类QBadgeView.java:

import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.os.Build;import android.os.Parcelable;import android.text.TextPaint;import android.text.TextUtils;import android.util.AttributeSet;import android.util.SparseArray;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.ViewParent;import android.widget.FrameLayout;import android.widget.RelativeLayout;import java.util.ArrayList;import java.util.List;/** * 自定义QQ消息数量工具类 */public class QBadgeView extends View implements Badge {    protected int mColorBackground;    protected int mColorBackgroundBorder;    protected int mColorBadgeText;    protected Drawable mDrawableBackground;    protected Bitmap mBitmapClip;    protected boolean mDrawableBackgroundClip;    protected float mBackgroundBorderWidth;    protected float mBadgeTextSize;    protected float mBadgePadding;    protected int mBadgeNumber;    protected String mBadgeText;    protected boolean mDraggable;    protected boolean mDragging;    protected boolean mExact;    protected boolean mShowShadow;    protected int mBadgeGravity;    protected float mGravityOffsetX;    protected float mGravityOffsetY;    protected float mDefalutRadius;    protected float mFinalDragDistance;    protected int mDragQuadrant;    protected boolean mDragOutOfRange;    protected RectF mBadgeTextRect;    protected RectF mBadgeBackgroundRect;    protected Path mDragPath;    protected Paint.FontMetrics mBadgeTextFontMetrics;    protected PointF mBadgeCenter;    protected PointF mDragCenter;    protected PointF mRowBadgeCenter;    protected PointF mControlPoint;    protected List mInnertangentPoints;    protected View mTargetView;    protected int mWidth;    protected int mHeight;    protected TextPaint mBadgeTextPaint;    protected Paint mBadgeBackgroundPaint;    protected Paint mBadgeBackgroundBorderPaint;    protected BadgeAnimator mAnimator;    protected OnDragStateChangedListener mDragStateChangedListener;    protected ViewGroup mActivityRoot;    public QBadgeView(Context context) {        this(context, null);    }    private QBadgeView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    private QBadgeView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        setLayerType(View.LAYER_TYPE_SOFTWARE, null);        mBadgeTextRect = new RectF();        mBadgeBackgroundRect = new RectF();        mDragPath = new Path();        mBadgeCenter = new PointF();        mDragCenter = new PointF();        mRowBadgeCenter = new PointF();        mControlPoint = new PointF();        mInnertangentPoints = new ArrayList<>();        mBadgeTextPaint = new TextPaint();        mBadgeTextPaint.setAntiAlias(true);        mBadgeTextPaint.setSubpixelText(true);        mBadgeTextPaint.setFakeBoldText(true);        mBadgeTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));        mBadgeBackgroundPaint = new Paint();        mBadgeBackgroundPaint.setAntiAlias(true);        mBadgeBackgroundPaint.setStyle(Paint.Style.FILL);        mBadgeBackgroundBorderPaint = new Paint();        mBadgeBackgroundBorderPaint.setAntiAlias(true);        mBadgeBackgroundBorderPaint.setStyle(Paint.Style.STROKE);        mColorBackground = 0xFFE84E40;        mColorBadgeText = 0xFFFFFFFF;        mBadgeTextSize = DisplayUtil.dp2px(getContext(), 11);        mBadgePadding = DisplayUtil.dp2px(getContext(), 5);        mBadgeNumber = 0;        mBadgeGravity = Gravity.END | Gravity.TOP;        mGravityOffsetX = DisplayUtil.dp2px(getContext(), 1);        mGravityOffsetY = DisplayUtil.dp2px(getContext(), 1);        mFinalDragDistance = DisplayUtil.dp2px(getContext(), 90);        mShowShadow = true;        mDrawableBackgroundClip = false;        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {            setTranslationZ(1000);        }    }    @Override    public Badge bindTarget(final View targetView) {        if (targetView == null) {            throw new IllegalStateException("targetView can not be null");        }        if (getParent() != null) {            ((ViewGroup) getParent()).removeView(this);        }        ViewParent targetParent = targetView.getParent();        if (targetParent != null && targetParent instanceof ViewGroup) {            mTargetView = targetView;            if (targetParent instanceof BadgeContainer) {                ((BadgeContainer) targetParent).addView(this);            } else {                ViewGroup targetContainer = (ViewGroup) targetParent;                int index = targetContainer.indexOfChild(targetView);                ViewGroup.LayoutParams targetParams = targetView.getLayoutParams();                targetContainer.removeView(targetView);                final BadgeContainer badgeContainer = new BadgeContainer(getContext());                if(targetContainer instanceof RelativeLayout){                    badgeContainer.setId(targetView.getId());                }                targetContainer.addView(badgeContainer, index, targetParams);                badgeContainer.addView(targetView);                badgeContainer.addView(this);            }        } else {            throw new IllegalStateException("targetView must have a parent");        }        return this;    }    @Override    public View getTargetView() {        return mTargetView;    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        if (mActivityRoot == null) findViewRoot(mTargetView);    }    private void findViewRoot(View view) {        mActivityRoot = (ViewGroup) view.getRootView();        if (mActivityRoot == null) {            findActivityRoot(view);        }    }    private void findActivityRoot(View view) {        if (view.getParent() != null && view.getParent() instanceof View) {            findActivityRoot((View) view.getParent());        } else if (view instanceof ViewGroup) {            mActivityRoot = (ViewGroup) view;        }    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getActionMasked()) {            case MotionEvent.ACTION_DOWN:            case MotionEvent.ACTION_POINTER_DOWN:                float x = event.getX();                float y = event.getY();                if (mDraggable && event.getPointerId(event.getActionIndex()) == 0                        && (x > mBadgeBackgroundRect.left && x < mBadgeBackgroundRect.right &&                        y > mBadgeBackgroundRect.top && y < mBadgeBackgroundRect.bottom)                        && mBadgeText != null) {                    initRowBadgeCenter();                    mDragging = true;                    updataListener(OnDragStateChangedListener.STATE_START);                    mDefalutRadius = DisplayUtil.dp2px(getContext(), 7);                    getParent().requestDisallowInterceptTouchEvent(true);                    screenFromWindow(true);                    mDragCenter.x = event.getRawX();                    mDragCenter.y = event.getRawY();                }                break;            case MotionEvent.ACTION_MOVE:                if (mDragging) {                    mDragCenter.x = event.getRawX();                    mDragCenter.y = event.getRawY();                    invalidate();                }                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_POINTER_UP:            case MotionEvent.ACTION_CANCEL:                if (event.getPointerId(event.getActionIndex()) == 0 && mDragging) {                    mDragging = false;                    onPointerUp();                }                break;        }        return mDragging || super.onTouchEvent(event);    }    private void onPointerUp() {        if (mDragOutOfRange) {            animateHide(mDragCenter);            updataListener(OnDragStateChangedListener.STATE_SUCCEED);        } else {            reset();            updataListener(OnDragStateChangedListener.STATE_CANCELED);        }    }    protected Bitmap createBadgeBitmap() {        Bitmap bitmap = Bitmap.createBitmap((int) mBadgeBackgroundRect.width() + DisplayUtil.dp2px(getContext(), 3),                (int) mBadgeBackgroundRect.height() + DisplayUtil.dp2px(getContext(), 3), Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(bitmap);        drawBadge(canvas, new PointF(canvas.getWidth() / 2f, canvas.getHeight() / 2f), getBadgeCircleRadius());        return bitmap;    }    protected void screenFromWindow(boolean screen) {        if (getParent() != null) {            ((ViewGroup) getParent()).removeView(this);        }        if (screen) {            mActivityRoot.addView(this, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,                    FrameLayout.LayoutParams.MATCH_PARENT));        } else {            bindTarget(mTargetView);        }    }    private void showShadowImpl(boolean showShadow) {        int x = DisplayUtil.dp2px(getContext(), 1);        int y = DisplayUtil.dp2px(getContext(), 1.5f);        switch (mDragQuadrant) {            case 1:                x = DisplayUtil.dp2px(getContext(), 1);                y = DisplayUtil.dp2px(getContext(), -1.5f);                break;            case 2:                x = DisplayUtil.dp2px(getContext(), -1);                y = DisplayUtil.dp2px(getContext(), -1.5f);                break;            case 3:                x = DisplayUtil.dp2px(getContext(), -1);                y = DisplayUtil.dp2px(getContext(), 1.5f);                break;            case 4:                x = DisplayUtil.dp2px(getContext(), 1);                y = DisplayUtil.dp2px(getContext(), 1.5f);                break;        }        mBadgeBackgroundPaint.setShadowLayer(showShadow ? DisplayUtil.dp2px(getContext(), 2f)                : 0, x, y, 0x33000000);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mWidth = w;        mHeight = h;    }    @Override    protected void onDraw(Canvas canvas) {        if (mAnimator != null && mAnimator.isRunning()) {            mAnimator.draw(canvas);            return;        }        if (mBadgeText != null) {            initPaints();            float badgeRadius = getBadgeCircleRadius();            float startCircleRadius = mDefalutRadius * (1 - MathUtil.getPointDistance                    (mRowBadgeCenter, mDragCenter) / mFinalDragDistance);            if (mDraggable && mDragging) {                mDragQuadrant = MathUtil.getQuadrant(mDragCenter, mRowBadgeCenter);                showShadowImpl(mShowShadow);                if (mDragOutOfRange = startCircleRadius < DisplayUtil.dp2px(getContext(), 1.5f)) {                    updataListener(OnDragStateChangedListener.STATE_DRAGGING_OUT_OF_RANGE);                    drawBadge(canvas, mDragCenter, badgeRadius);                } else {                    updataListener(OnDragStateChangedListener.STATE_DRAGGING);                    drawDragging(canvas, startCircleRadius, badgeRadius);                    drawBadge(canvas, mDragCenter, badgeRadius);                }            } else {                findBadgeCenter();                drawBadge(canvas, mBadgeCenter, badgeRadius);            }        }    }    private void initPaints() {        showShadowImpl(mShowShadow);        mBadgeBackgroundPaint.setColor(mColorBackground);        mBadgeBackgroundBorderPaint.setColor(mColorBackgroundBorder);        mBadgeBackgroundBorderPaint.setStrokeWidth(mBackgroundBorderWidth);        mBadgeTextPaint.setColor(mColorBadgeText);        mBadgeTextPaint.setTextAlign(Paint.Align.CENTER);    }    private void drawDragging(Canvas canvas, float startRadius, float badgeRadius) {        float dy = mDragCenter.y - mRowBadgeCenter.y;        float dx = mDragCenter.x - mRowBadgeCenter.x;        mInnertangentPoints.clear();        if (dx != 0) {            double k1 = dy / dx;            double k2 = -1 / k1;            MathUtil.getInnertangentPoints(mDragCenter, badgeRadius, k2, mInnertangentPoints);            MathUtil.getInnertangentPoints(mRowBadgeCenter, startRadius, k2, mInnertangentPoints);        } else {            MathUtil.getInnertangentPoints(mDragCenter, badgeRadius, 0d, mInnertangentPoints);            MathUtil.getInnertangentPoints(mRowBadgeCenter, startRadius, 0d, mInnertangentPoints);        }        mDragPath.reset();        mDragPath.addCircle(mRowBadgeCenter.x, mRowBadgeCenter.y, startRadius,                mDragQuadrant == 1 || mDragQuadrant == 2 ? Path.Direction.CCW : Path.Direction.CW);        mControlPoint.x = (mRowBadgeCenter.x + mDragCenter.x) / 2.0f;        mControlPoint.y = (mRowBadgeCenter.y + mDragCenter.y) / 2.0f;        mDragPath.moveTo(mInnertangentPoints.get(2).x, mInnertangentPoints.get(2).y);        mDragPath.quadTo(mControlPoint.x, mControlPoint.y, mInnertangentPoints.get(0).x, mInnertangentPoints.get(0).y);        mDragPath.lineTo(mInnertangentPoints.get(1).x, mInnertangentPoints.get(1).y);        mDragPath.quadTo(mControlPoint.x, mControlPoint.y, mInnertangentPoints.get(3).x, mInnertangentPoints.get(3).y);        mDragPath.lineTo(mInnertangentPoints.get(2).x, mInnertangentPoints.get(2).y);        mDragPath.close();        canvas.drawPath(mDragPath, mBadgeBackgroundPaint);        //draw dragging border        if (mColorBackgroundBorder != 0 && mBackgroundBorderWidth > 0) {            mDragPath.reset();            mDragPath.moveTo(mInnertangentPoints.get(2).x, mInnertangentPoints.get(2).y);            mDragPath.quadTo(mControlPoint.x, mControlPoint.y, mInnertangentPoints.get(0).x, mInnertangentPoints.get(0).y);            mDragPath.moveTo(mInnertangentPoints.get(1).x, mInnertangentPoints.get(1).y);            mDragPath.quadTo(mControlPoint.x, mControlPoint.y, mInnertangentPoints.get(3).x, mInnertangentPoints.get(3).y);            float startY;            float startX;            if (mDragQuadrant == 1 || mDragQuadrant == 2) {                startX = mInnertangentPoints.get(2).x - mRowBadgeCenter.x;                startY = mRowBadgeCenter.y - mInnertangentPoints.get(2).y;            } else {                startX = mInnertangentPoints.get(3).x - mRowBadgeCenter.x;                startY = mRowBadgeCenter.y - mInnertangentPoints.get(3).y;            }            float startAngle = 360 - (float) MathUtil.radianToAngle(MathUtil.getTanRadian(Math.atan(startY / startX),                    mDragQuadrant - 1 == 0 ? 4 : mDragQuadrant - 1));            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                mDragPath.addArc(mRowBadgeCenter.x - startRadius, mRowBadgeCenter.y - startRadius,                        mRowBadgeCenter.x + startRadius, mRowBadgeCenter.y + startRadius, startAngle,                        180);            } else {                mDragPath.addArc(new RectF(mRowBadgeCenter.x - startRadius, mRowBadgeCenter.y - startRadius,                        mRowBadgeCenter.x + startRadius, mRowBadgeCenter.y + startRadius), startAngle, 180);            }            canvas.drawPath(mDragPath, mBadgeBackgroundBorderPaint);        }    }    private void drawBadge(Canvas canvas, PointF center, float radius) {        if (center.x == -1000 && center.y == -1000) {            return;        }        if (mBadgeText.isEmpty() || mBadgeText.length() == 1) {            mBadgeBackgroundRect.left = center.x - (int) radius;            mBadgeBackgroundRect.top = center.y - (int) radius;            mBadgeBackgroundRect.right = center.x + (int) radius;            mBadgeBackgroundRect.bottom = center.y + (int) radius;            if (mDrawableBackground != null) {                drawBadgeBackground(canvas);            } else {                canvas.drawCircle(center.x, center.y, radius, mBadgeBackgroundPaint);                if (mColorBackgroundBorder != 0 && mBackgroundBorderWidth > 0) {                    canvas.drawCircle(center.x, center.y, radius, mBadgeBackgroundBorderPaint);                }            }        } else {            mBadgeBackgroundRect.left = center.x - (mBadgeTextRect.width() / 2f + mBadgePadding);            mBadgeBackgroundRect.top = center.y - (mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f);            mBadgeBackgroundRect.right = center.x + (mBadgeTextRect.width() / 2f + mBadgePadding);            mBadgeBackgroundRect.bottom = center.y + (mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f);            radius = mBadgeBackgroundRect.height() / 2f;            if (mDrawableBackground != null) {                drawBadgeBackground(canvas);            } else {                canvas.drawRoundRect(mBadgeBackgroundRect, radius, radius, mBadgeBackgroundPaint);                if (mColorBackgroundBorder != 0 && mBackgroundBorderWidth > 0) {                    canvas.drawRoundRect(mBadgeBackgroundRect, radius, radius, mBadgeBackgroundBorderPaint);                }            }        }        if (!mBadgeText.isEmpty()) {            canvas.drawText(mBadgeText, center.x,                    (mBadgeBackgroundRect.bottom + mBadgeBackgroundRect.top                            - mBadgeTextFontMetrics.bottom - mBadgeTextFontMetrics.top) / 2f,                    mBadgeTextPaint);        }    }    private void drawBadgeBackground(Canvas canvas) {        mBadgeBackgroundPaint.setShadowLayer(0, 0, 0, 0);        int left = (int) mBadgeBackgroundRect.left;        int top = (int) mBadgeBackgroundRect.top;        int right = (int) mBadgeBackgroundRect.right;        int bottom = (int) mBadgeBackgroundRect.bottom;        if (mDrawableBackgroundClip) {            right = left + mBitmapClip.getWidth();            bottom = top + mBitmapClip.getHeight();            canvas.saveLayer(left, top, right, bottom, null, Canvas.ALL_SAVE_FLAG);        }        mDrawableBackground.setBounds(left, top, right, bottom);        mDrawableBackground.draw(canvas);        if (mDrawableBackgroundClip) {            mBadgeBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));            canvas.drawBitmap(mBitmapClip, left, top, mBadgeBackgroundPaint);            canvas.restore();            mBadgeBackgroundPaint.setXfermode(null);            if (mBadgeText.isEmpty() || mBadgeText.length() == 1) {                canvas.drawCircle(mBadgeBackgroundRect.centerX(), mBadgeBackgroundRect.centerY(),                        mBadgeBackgroundRect.width() / 2f, mBadgeBackgroundBorderPaint);            } else {                canvas.drawRoundRect(mBadgeBackgroundRect,                        mBadgeBackgroundRect.height() / 2, mBadgeBackgroundRect.height() / 2,                        mBadgeBackgroundBorderPaint);            }        } else {            canvas.drawRect(mBadgeBackgroundRect, mBadgeBackgroundBorderPaint);        }    }    private void createClipLayer() {        if (mBadgeText == null) {            return;        }        if (!mDrawableBackgroundClip) {            return;        }        if (mBitmapClip != null && !mBitmapClip.isRecycled()) {            mBitmapClip.recycle();        }        float radius = getBadgeCircleRadius();        if (mBadgeText.isEmpty() || mBadgeText.length() == 1) {            mBitmapClip = Bitmap.createBitmap((int) radius * 2, (int) radius * 2,                    Bitmap.Config.ARGB_4444);            Canvas srcCanvas = new Canvas(mBitmapClip);            srcCanvas.drawCircle(srcCanvas.getWidth() / 2f, srcCanvas.getHeight() / 2f,                    srcCanvas.getWidth() / 2f, mBadgeBackgroundPaint);        } else {            mBitmapClip = Bitmap.createBitmap((int) (mBadgeTextRect.width() + mBadgePadding * 2),                    (int) (mBadgeTextRect.height() + mBadgePadding), Bitmap.Config.ARGB_4444);            Canvas srcCanvas = new Canvas(mBitmapClip);            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                srcCanvas.drawRoundRect(0, 0, srcCanvas.getWidth(), srcCanvas.getHeight(), srcCanvas.getHeight() / 2f,                        srcCanvas.getHeight() / 2f, mBadgeBackgroundPaint);            } else {                srcCanvas.drawRoundRect(new RectF(0, 0, srcCanvas.getWidth(), srcCanvas.getHeight()),                        srcCanvas.getHeight() / 2f, srcCanvas.getHeight() / 2f, mBadgeBackgroundPaint);            }        }    }    private float getBadgeCircleRadius() {        if (mBadgeText.isEmpty()) {            return mBadgePadding;        } else if (mBadgeText.length() == 1) {            return mBadgeTextRect.height() > mBadgeTextRect.width() ?                    mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f :                    mBadgeTextRect.width() / 2f + mBadgePadding * 0.5f;        } else {            return mBadgeBackgroundRect.height() / 2f;        }    }    private void findBadgeCenter() {        float rectWidth = mBadgeTextRect.height() > mBadgeTextRect.width() ?                mBadgeTextRect.height() : mBadgeTextRect.width();        switch (mBadgeGravity) {            case Gravity.START | Gravity.TOP:                mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f;                mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f;                break;            case Gravity.START | Gravity.BOTTOM:                mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f;                mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f);                break;            case Gravity.END | Gravity.TOP:                mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f);                mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f;                break;            case Gravity.END | Gravity.BOTTOM:                mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f);                mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f);                break;            case Gravity.CENTER:                mBadgeCenter.x = mWidth / 2f;                mBadgeCenter.y = mHeight / 2f;                break;            case Gravity.CENTER | Gravity.TOP:                mBadgeCenter.x = mWidth / 2f;                mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f;                break;            case Gravity.CENTER | Gravity.BOTTOM:                mBadgeCenter.x = mWidth / 2f;                mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f);                break;            case Gravity.CENTER | Gravity.START:                mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f;                mBadgeCenter.y = mHeight / 2f;                break;            case Gravity.CENTER | Gravity.END:                mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f);                mBadgeCenter.y = mHeight / 2f;                break;        }        initRowBadgeCenter();    }    private void measureText() {        mBadgeTextRect.left = 0;        mBadgeTextRect.top = 0;        if (TextUtils.isEmpty(mBadgeText)) {            mBadgeTextRect.right = 0;            mBadgeTextRect.bottom = 0;        } else {            mBadgeTextPaint.setTextSize(mBadgeTextSize);            mBadgeTextRect.right = mBadgeTextPaint.measureText(mBadgeText);            mBadgeTextFontMetrics = mBadgeTextPaint.getFontMetrics();            mBadgeTextRect.bottom = mBadgeTextFontMetrics.descent - mBadgeTextFontMetrics.ascent;        }        createClipLayer();    }    private void initRowBadgeCenter() {        int[] screenPoint = new int[2];        getLocationOnScreen(screenPoint);        mRowBadgeCenter.x = mBadgeCenter.x + screenPoint[0];        mRowBadgeCenter.y = mBadgeCenter.y + screenPoint[1];    }    protected void animateHide(PointF center) {        if (mBadgeText == null) {            return;        }        if (mAnimator == null || !mAnimator.isRunning()) {            screenFromWindow(true);            mAnimator = new BadgeAnimator(createBadgeBitmap(), center, this);            mAnimator.start();            setBadgeNumber(0);        }    }    public void reset() {        mDragCenter.x = -1000;        mDragCenter.y = -1000;        mDragQuadrant = 4;        screenFromWindow(false);        getParent().requestDisallowInterceptTouchEvent(false);        invalidate();    }    @Override    public void hide(boolean animate) {        if (animate && mActivityRoot != null) {            initRowBadgeCenter();            animateHide(mRowBadgeCenter);        } else {            setBadgeNumber(0);        }    }    /**     * @param badgeNumber equal to zero badge will be hidden, less than zero show dot     */    @Override    public Badge setBadgeNumber(int badgeNumber) {        mBadgeNumber = badgeNumber;        if (mBadgeNumber < 0) {            mBadgeText = "";        } else if (mBadgeNumber > 99) {            mBadgeText = mExact ? String.valueOf(mBadgeNumber) : "99+";        } else if (mBadgeNumber > 0 && mBadgeNumber <= 99) {            mBadgeText = String.valueOf(mBadgeNumber);        } else if (mBadgeNumber == 0) {            mBadgeText = null;        }        measureText();        invalidate();        return this;    }    @Override    public int getBadgeNumber() {        return mBadgeNumber;    }    @Override    public Badge setBadgeText(String badgeText) {        mBadgeText = badgeText;        mBadgeNumber = 1;        measureText();        invalidate();        return this;    }    @Override    public String getBadgeText() {        return mBadgeText;    }    @Override    public Badge setExactMode(boolean isExact) {        mExact = isExact;        if (mBadgeNumber > 99) {            setBadgeNumber(mBadgeNumber);        }        return this;    }    @Override    public boolean isExactMode() {        return mExact;    }    @Override    public Badge setShowShadow(boolean showShadow) {        mShowShadow = showShadow;        invalidate();        return this;    }    @Override    public boolean isShowShadow() {        return mShowShadow;    }    @Override    public Badge setBadgeBackgroundColor(int color) {        mColorBackground = color;        if (mColorBackground == Color.TRANSPARENT) {            mBadgeTextPaint.setXfermode(null);        } else {            mBadgeTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));        }        invalidate();        return this;    }    @Override    public Badge stroke(int color, float width, boolean isDpValue) {        mColorBackgroundBorder = color;        mBackgroundBorderWidth = isDpValue ? DisplayUtil.dp2px(getContext(), width) : width;        invalidate();        return this;    }    @Override    public int getBadgeBackgroundColor() {        return mColorBackground;    }    @Override    public Badge setBadgeBackground(Drawable drawable) {        return setBadgeBackground(drawable, false);    }    @Override    public Badge setBadgeBackground(Drawable drawable, boolean clip) {        mDrawableBackgroundClip = clip;        mDrawableBackground = drawable;        createClipLayer();        invalidate();        return this;    }    @Override    public Drawable getBadgeBackground() {        return mDrawableBackground;    }    @Override    public Badge setBadgeTextColor(int color) {        mColorBadgeText = color;        invalidate();        return this;    }    @Override    public int getBadgeTextColor() {        return mColorBadgeText;    }    @Override    public Badge setBadgeTextSize(float size, boolean isSpValue) {        mBadgeTextSize = isSpValue ? DisplayUtil.dp2px(getContext(), size) : size;        measureText();        invalidate();        return this;    }    @Override    public float getBadgeTextSize(boolean isSpValue) {        return isSpValue ? DisplayUtil.px2dp(getContext(), mBadgeTextSize) : mBadgeTextSize;    }    @Override    public Badge setBadgePadding(float padding, boolean isDpValue) {        mBadgePadding = isDpValue ? DisplayUtil.dp2px(getContext(), padding) : padding;        createClipLayer();        invalidate();        return this;    }    @Override    public float getBadgePadding(boolean isDpValue) {        return isDpValue ? DisplayUtil.px2dp(getContext(), mBadgePadding) : mBadgePadding;    }    @Override    public boolean isDraggable() {        return mDraggable;    }    /**     * @param gravity only support Gravity.START | Gravity.TOP , Gravity.END | Gravity.TOP ,     *                Gravity.START | Gravity.BOTTOM , Gravity.END | Gravity.BOTTOM ,     *                Gravity.CENTER , Gravity.CENTER | Gravity.TOP , Gravity.CENTER | Gravity.BOTTOM ,     *                Gravity.CENTER | Gravity.START , Gravity.CENTER | Gravity.END     */    @Override    public Badge setBadgeGravity(int gravity) {        if (gravity == (Gravity.START | Gravity.TOP) ||                gravity == (Gravity.END | Gravity.TOP) ||                gravity == (Gravity.START | Gravity.BOTTOM) ||                gravity == (Gravity.END | Gravity.BOTTOM) ||                gravity == (Gravity.CENTER) ||                gravity == (Gravity.CENTER | Gravity.TOP) ||                gravity == (Gravity.CENTER | Gravity.BOTTOM) ||                gravity == (Gravity.CENTER | Gravity.START) ||                gravity == (Gravity.CENTER | Gravity.END)) {            mBadgeGravity = gravity;            invalidate();        } else {            throw new IllegalStateException("only support Gravity.START | Gravity.TOP , Gravity.END | Gravity.TOP , " +                    "Gravity.START | Gravity.BOTTOM , Gravity.END | Gravity.BOTTOM , Gravity.CENTER" +                    " , Gravity.CENTER | Gravity.TOP , Gravity.CENTER | Gravity.BOTTOM ," +                    "Gravity.CENTER | Gravity.START , Gravity.CENTER | Gravity.END");        }        return this;    }    @Override    public int getBadgeGravity() {        return mBadgeGravity;    }    @Override    public Badge setGravityOffset(float offset, boolean isDpValue) {        return setGravityOffset(offset, offset, isDpValue);    }    @Override    public Badge setGravityOffset(float offsetX, float offsetY, boolean isDpValue) {        mGravityOffsetX = isDpValue ? DisplayUtil.dp2px(getContext(), offsetX) : offsetX;        mGravityOffsetY = isDpValue ? DisplayUtil.dp2px(getContext(), offsetY) : offsetY;        invalidate();        return this;    }    @Override    public float getGravityOffsetX(boolean isDpValue) {        return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetX) : mGravityOffsetX;    }    @Override    public float getGravityOffsetY(boolean isDpValue) {        return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetY) : mGravityOffsetY;    }    private void updataListener(int state) {        if (mDragStateChangedListener != null)            mDragStateChangedListener.onDragStateChanged(state, this, mTargetView);    }    @Override    public Badge setOnDragStateChangedListener(OnDragStateChangedListener l) {        mDraggable = l != null;        mDragStateChangedListener = l;        return this;    }    @Override    public PointF getDragCenter() {        if (mDraggable && mDragging) return mDragCenter;        return null;    }    private class BadgeContainer extends ViewGroup {        @Override        protected void dispatchRestoreInstanceState(SparseArray container) {            if(!(getParent() instanceof RelativeLayout)){                super.dispatchRestoreInstanceState(container);            }        }        public BadgeContainer(Context context) {            super(context);        }        @Override        protected void onLayout(boolean changed, int l, int t, int r, int b) {            for (int i = 0; i < getChildCount(); i++) {                View child = getChildAt(i);                child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());            }        }        @Override        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {            View targetView = null, badgeView = null;            for (int i = 0; i < getChildCount(); i++) {                View child = getChildAt(i);                if (!(child instanceof QBadgeView)) {                    targetView = child;                } else {                    badgeView = child;                }            }            if (targetView == null) {                super.onMeasure(widthMeasureSpec, heightMeasureSpec);            } else {                targetView.measure(widthMeasureSpec, heightMeasureSpec);                if (badgeView != null) {                    badgeView.measure(MeasureSpec.makeMeasureSpec(targetView.getMeasuredWidth(), MeasureSpec.EXACTLY),                            MeasureSpec.makeMeasureSpec(targetView.getMeasuredHeight(), MeasureSpec.EXACTLY));                }                setMeasuredDimension(targetView.getMeasuredWidth(), targetView.getMeasuredHeight());            }        }    }}

9.模拟数据类:

import java.util.ArrayList;import java.util.List;/** * 模拟数据类 */public class DataSupport {    private List mDatas;    public DataSupport() {        mDatas = new ArrayList<>();        mDatas.add("(`・ω・´)ฅ铲屎官快来");        mDatas.add("ฅ(´-ω-`)ฅ好酥胡");        mDatas.add("~(=^・ω・^)ฅ☆ 澡桑嚎啊小婊贝");        mDatas.add("(=ฅರ﹏ರ)ฅ我要小鱼干");        mDatas.add("(ฅ‵皿′)ฅ︵┻┻ 为什么不跟我玩儿");        mDatas.add("ヽ(ฅ≧へ≦)ฅ饭不好吃");        mDatas.add("(╬ ̄皿 ̄)ฅ拒绝洗澡");        mDatas.add("ฅ(= ̄. ̄||)ฅ愚蠢的人类");        mDatas.add("☆*:.。. ฅ(≧▽≦)ฅ .。.:*☆ ");        mDatas.add("ヾ((๑˘ㅂ˘๑)ฅ^._.^ฅ我萌吗");        mDatas.add("༻ི(ؔᵒ̶̷ᵕؔᵒ̷̶)༄୭*ˈ 浪到飞起");        mDatas.add("*:ஐ٩(๑´ᵕ`)۶ஐ:* 透心凉心飞扬");        mDatas.add("˚₊*୧⃛(๑⃙⃘⁼̴̀꒳⁼̴́๑⃙⃘)୨⃛*₊˚⋆ 精神百倍");        mDatas.add("٩(ꇐ‿ꇐ)۶世界那么大我想去看看");        mDatas.add("๛꜀꒰ ˟⌂˟꜀ ꜆꒱꜆ 起来,不愿做作业的人们");        mDatas.add("˚‧·(´ฅ・ェ・ฅ‘)‧º.喵,救命啊,人家怕水");        mDatas.add("(^,,ԾェԾ,,^)这款裙子是为本宝宝定制的吗");        mDatas.add("ლ(ಠェಠ)و美图软件用的好,老公才好找");        mDatas.add("(҂`・ェ・´) <,︻╦̵̵̿╤─ ҉ - -- 让你秀恩爱!");        mDatas.add("为你加油!!!!!!\n" +                " ☆  * .   ☆\n" +                "  . ∧_∧ ∩ * ☆\n" +                "*  ☆ ( ・∀・)/ .\n" +                " .  ⊂   ノ* ☆\n" +                "☆ * (つ ノ  .☆\n" +                "   (ノ");        mDatas.add("老\n" +                " 板\n" +                "  说\n" +                "   今\n" +                "    天\n" +                "     放\n" +                "      假\n" +                "        !\n" +                " ヽ\  //\n" +                "    ∧∧ 。\n" +                "  ゚ (゚∀゚)っ ゚\n" +                "   (っノ\n" +                "    `J");        mDatas.add("巴拉巴拉巴拉,把你变成猪!\n" +                "  ∧_∧\n" +                " (。・ω・。)つ━☆・*。\n" +                "  ⊂   ノ    ・゜+.\n" +                " しーJ   °。+ *´¨)\n" +                "            .· ´¸.·*´¨) ¸.·*¨)\n" +                "             (¸.·´ (¸.·’*");    }    public List getData() {        return mDatas;    }}

四.实现QQ或微信聊天列表的消息提醒的话可以往下看:

1.ListViewActivity.java:

import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import com.example.m1571.myapplication.badgeview.Badge;import com.example.m1571.myapplication.badgeview.QBadgeView;import java.util.List;//实现QQ微信聊天列表信息的消息数量提示public class ListViewActivity extends AppCompatActivity {    ListView listview;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_list_view);        listview = (ListView) findViewById(R.id.listview);        listview.setAdapter(new ListAdapter());    }    class ListAdapter extends BaseAdapter {        private List data;        public ListAdapter() {            data = new DataSupport().getData();        }        @Override        public int getCount() {            return data.size();        }        @Override        public Object getItem(int position) {            return data.get(position);        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            Holder holder;            if (convertView == null) {                holder = new Holder();                convertView = LayoutInflater.from(ListViewActivity.this).inflate(R.layout.item_view, parent, false);                holder.textView = (TextView) convertView.findViewById(R.id.tv_content);                holder.badge = new QBadgeView(ListViewActivity.this).bindTarget(convertView.findViewById(R.id.imageview));                holder.badge.setBadgeTextSize(12, true);                convertView.setTag(holder);            } else {                holder = (Holder) convertView.getTag();            }            holder.textView.setText(data.get(position));            holder.badge.setBadgeNumber(position);            holder.badge.setOnDragStateChangedListener(new Badge.OnDragStateChangedListener() {                @Override                public void onDragStateChanged(int dragState, Badge badge, View targetView) {                    if (dragState == STATE_SUCCEED) {                        Toast.makeText(ListViewActivity.this, String.valueOf(position), Toast.LENGTH_SHORT).show();                    }                }            });            return convertView;        }        class Holder {            TextView textView;            Badge badge;        }    }}

2.布局activity_list_view.xml:

<?xml version="1.0" encoding="utf-8"?>    

3.适配器布局item_view.xml

<?xml version="1.0" encoding="utf-8"?>                                

 

4.使用RecyclerView实现QQ或微信聊天列表消息数量显示:

RecyclerViewActivity.java:

import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import android.widget.Toast;import com.example.m1571.myapplication.badgeview.Badge;import com.example.m1571.myapplication.badgeview.QBadgeView;import java.util.List;public class RecyclerViewActivity extends AppCompatActivity {    RecyclerView recyclerView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_recycler_view);        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);        recyclerView.setLayoutManager(new LinearLayoutManager(this));        recyclerView.setAdapter(new RecyclerAdapter());    }    class RecyclerAdapter extends RecyclerView.Adapter {        private List data;        public RecyclerAdapter() {            data = new DataSupport().getData();        }        @Override        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {            return new Holder(LayoutInflater.from(RecyclerViewActivity.this).inflate(R.layout.item_view, parent, false));        }        @Override        public void onBindViewHolder(Holder holder, int position) {            holder.textView.setText(data.get(position));            holder.badge.setBadgeNumber(position);        }        @Override        public int getItemCount() {            return data.size();        }        class Holder extends RecyclerView.ViewHolder {            TextView textView;            Badge badge;            public Holder(View itemView) {                super(itemView);                textView = (TextView) itemView.findViewById(R.id.tv_content);                badge = new QBadgeView(RecyclerViewActivity.this).bindTarget(itemView.findViewById(R.id.root));                badge.setBadgeGravity(Gravity.CENTER | Gravity.END);                badge.setBadgeTextSize(14, true);                badge.setBadgePadding(6, true);                badge.setOnDragStateChangedListener(new Badge.OnDragStateChangedListener() {                    @Override                    public void onDragStateChanged(int dragState, Badge badge, View targetView) {                        if (dragState == STATE_SUCCEED) {                            Toast.makeText(RecyclerViewActivity.this, String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();                        }                    }                });            }        }    }}

 

 

 

更多相关文章

  1. Android(安卓)Handler 四个使用实例 及HandlerThread的使用
  2. WEEX-EEUI 页面的高度问题(页面高度设置为多少才对?)
  3. Android(安卓)Studio适配利器——如何设置不同机型的预览界面
  4. 如何为您的Android应用更新启动器图标
  5. Android(安卓)TextView富文本的使用
  6. 在Android中为啥建议你用Message.obtain()方法获取Message对象,而
  7. android 顶部Tab实现及原理
  8. android listview adapter中设置点击直接position被重用问题解决
  9. Android(安卓)launcher动态Icon的实现方法

随机推荐

  1. linearlayout属性
  2. jsonformatter json格式化 Android
  3. android 名称
  4. 安卓开发到底是什么
  5. android 几种发送短信的方法
  6. 温故而知新Android篇之二
  7. Android设计尺寸规范--Android Design Gu
  8. Android 限定 Activity 转向
  9. androidの布局控件居于最底部实现
  10. Android 滚动Tab