Android 一个可以自由定制外观、支持拖拽消除的MaterialDesign风格Android BadgeView - Go语言中文社区

Android 一个可以自由定制外观、支持拖拽消除的MaterialDesign风格Android BadgeView


   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<RadioButton> radioButtons = new ArrayList<>();
    CompoundButton lastRadioButton;
    SeekBar seekBar_offsetx, seekBar_padding, seekBar_offsety, seekBar_numbersize;
    Switch swicth_exact, swicth_draggable, swicth_shadow;

    List<Badge> 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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#555555"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="15dp"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="20dp"
            android:text="@string/textview"
            android:textColor="#FFFFFF"
            android:textSize="18sp"
            android:textStyle="bold" />

        <ImageView
            android:id="@+id/imageview"
            android:layout_width="0dp"
            android:layout_height="60dp"
            android:layout_weight="1"
            android:src="@drawable/show" />

        <Button
            android:id="@+id/button"
            android:layout_marginLeft="5dp"
            android:background="@drawable/g"
            android:layout_width="0dp"
            android:layout_height="60dp"
            android:layout_weight="1"
            android:text="@string/button" />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:layout_marginBottom="20dp"
        android:background="#FFFFFF" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp">

                <TextView
                    android:id="@+id/tv_gravity_tips"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Gravity : "
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_st"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_gravity_tips"
                    android:text="Start | Top"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_sb"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_gravity_tips"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_st"
                    android:text="Start | Bottom"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_et"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/tv_gravity_tips"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_sb"
                    android:checked="true"
                    android:text="End | Top"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_eb"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_st"
                    android:text="End | Bottom"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_ct"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_st"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_eb"
                    android:text="Center | Top"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_ce"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_st"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_ct"
                    android:text="Center | End"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_cb"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_eb"
                    android:text="Center | Bottom"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_cs"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_eb"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_cb"
                    android:text="Center | Start"
                    android:textColor="#FFFFFF" />

                <RadioButton
                    android:id="@+id/rb_c"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/rb_eb"
                    android:layout_marginLeft="5dp"
                    android:layout_toRightOf="@+id/rb_cs"
                    android:text="Center"
                    android:textColor="#FFFFFF" />
            </RelativeLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:orientation="vertical"
                    android:gravity="center_horizontal">

                    <TextView
                        android:id="@+id/tv_offsetx"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="GravityOffsetX : 1"
                        android:textColor="#FFFFFF" />

                    <SeekBar
                        android:id="@+id/seekBar_offsetx"
                        style="@style/Widget.AppCompat.SeekBar.Discrete"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="10dp"
                        android:layout_marginTop="10dp"
                        android:max="20"
                        android:progress="1" />

                </LinearLayout>

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:orientation="vertical"
                    android:gravity="center_horizontal">

                    <TextView
                        android:id="@+id/tv_offsety"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="GravityOffsetY : 1"
                        android:textColor="#FFFFFF" />
                    <SeekBar
                        android:id="@+id/seekBar_offsety"
                        style="@style/Widget.AppCompat.SeekBar.Discrete"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginBottom="10dp"
                        android:layout_marginTop="10dp"
                        android:max="20"
                        android:progress="1" />

                </LinearLayout>
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/tv_padding"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="BadgePadding : 5"
                    android:textColor="#FFFFFF" />

                <SeekBar
                    android:id="@+id/seekBar_padding"
                    style="@style/Widget.AppCompat.SeekBar.Discrete"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="10dp"
                    android:layout_marginTop="10dp"
                    android:max="20"
                    android:progress="5" />
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:layout_marginTop="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="BadgeColor : "
                    android:textColor="#FFFFFF" />

                <ImageView
                    android:id="@+id/iv_badgecolor"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_marginLeft="5dp"
                    android:background="#E84E40" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="30dp"
                    android:text="ShowShadow : "
                    android:textColor="#FFFFFF" />

                <Switch
                    android:id="@+id/swicth_shadow"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:checked="true" />

            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="TextColor : "
                    android:textColor="#FFFFFF" />

                <ImageView
                    android:id="@+id/iv_numbercolor"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_marginLeft="5dp"
                    android:background="#FFFFFF" />

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_weight="1"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/tv_numbersize"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="TextSize : 11"
                        android:textColor="#FFFFFF" />

                    <SeekBar
                        android:id="@+id/seekBar_numbersize"
                        style="@style/Widget.AppCompat.SeekBar.Discrete"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:max="30"
                        android:progress="11" />
                </LinearLayout>
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="BadgeNumber : "
                    android:textColor="#FFFFFF" />

                <EditText
                    android:id="@+id/et_badgenumber"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:inputType="numberSigned"
                    android:maxLength="7"
                    android:maxLines="1"
                    android:paddingLeft="10dp"
                    android:paddingRight="10dp"
                    android:text="5"
                    android:textColor="#FFFFFF" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="ExactMode : "
                    android:textColor="#FFFFFF" />

                <Switch
                    android:id="@+id/swicth_exact"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:checked="false" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Text : "
                    android:textColor="#FFFFFF" />

                <EditText
                    android:id="@+id/et_badgetext"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:maxLines="1"
                    android:paddingLeft="10dp"
                    android:paddingRight="10dp"
                    android:textColor="#FFFFFF" />


            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:layout_marginTop="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Draggable : "
                    android:textColor="#FFFFFF" />

                <Switch
                    android:id="@+id/swicth_draggable"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp" />

                <TextView
                    android:id="@+id/tv_dragstate"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="50dp"
                    android:text=""
                    android:textColor="#FFFFFF"
                    android:textSize="18sp" />
            </LinearLayout>

            <Button
                android:id="@+id/btn_animation"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="animation" />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

3.相关属性:

dimens.xml:

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

string.xml:

<resources>
    <string name="app_name">My Application</string>
    <string name="textview">TextView</string>
    <string name="button">Button</string>
</resources>

shape_round_rect.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ff03a9f4" />
    <corners android:radius="2dp" />
</shape>

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<QBadgeView> 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<PointF> 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<PointF> 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(),
                    
                        
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/shenggaofei/article/details/106731489
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-12-11 12:44:57
  • 阅读 ( 1073 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢