本文主要是介绍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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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);}}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic 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() {@Overridepublic void onClick(View v) {if (v == iv_badgecolor) {selectorColor(new OnColorClickListener() {@Overridepublic void onColorClick(int color) {iv_badgecolor.setBackgroundColor(color);for (Badge badge : badges) {badge.setBadgeBackgroundColor(color);}}});} else if (v == iv_numbercolor) {selectorColor(new OnColorClickListener() {@Overridepublic 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;}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic 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();}}@Overridepublic void afterTextChanged(Editable s) {}}et_badgenumber.addTextChangedListener(new MyTextWatcher(et_badgenumber));et_badgetext.addTextChangedListener(new MyTextWatcher(et_badgetext));CompoundButton.OnCheckedChangeListener onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {@Overridepublic 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() {@Overridepublic 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};@Overridepublic int getCount() {return colors.length;}@Overridepublic Object getItem(int position) {return colors[position];}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {View v = new View(MainActivity.this);v.setBackgroundColor(colors[position]);v.setOnClickListener(new View.OnClickListener() {@Overridepublic 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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="15dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid: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" /><ImageViewandroid:id="@+id/imageview"android:layout_width="0dp"android:layout_height="60dp"android:layout_weight="1"android:src="@drawable/show" /><Buttonandroid: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><Viewandroid:layout_width="match_parent"android:layout_height="1px"android:layout_marginBottom="20dp"android:background="#FFFFFF" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"><TextViewandroid:id="@+id/tv_gravity_tips"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Gravity : "android:textColor="#FFFFFF" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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" /><RadioButtonandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:orientation="vertical"android:gravity="center_horizontal"><TextViewandroid:id="@+id/tv_offsetx"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="GravityOffsetX : 1"android:textColor="#FFFFFF" /><SeekBarandroid: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><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:orientation="vertical"android:gravity="center_horizontal"><TextViewandroid:id="@+id/tv_offsety"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="GravityOffsetY : 1"android:textColor="#FFFFFF" /><SeekBarandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_padding"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="BadgePadding : 5"android:textColor="#FFFFFF" /><SeekBarandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginTop="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="BadgeColor : "android:textColor="#FFFFFF" /><ImageViewandroid:id="@+id/iv_badgecolor"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="5dp"android:background="#E84E40" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:text="ShowShadow : "android:textColor="#FFFFFF" /><Switchandroid:id="@+id/swicth_shadow"android:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="true" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextColor : "android:textColor="#FFFFFF" /><ImageViewandroid:id="@+id/iv_numbercolor"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="5dp"android:background="#FFFFFF" /><LinearLayoutandroid:layout_width="0dp"android:layout_weight="1"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/tv_numbersize"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextSize : 11"android:textColor="#FFFFFF" /><SeekBarandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="BadgeNumber : "android:textColor="#FFFFFF" /><EditTextandroid: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" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="ExactMode : "android:textColor="#FFFFFF" /><Switchandroid:id="@+id/swicth_exact"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:checked="false" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Text : "android:textColor="#FFFFFF" /><EditTextandroid: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><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginTop="10dp"android:gravity="center_vertical"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Draggable : "android:textColor="#FFFFFF" /><Switchandroid:id="@+id/swicth_draggable"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp" /><TextViewandroid: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><Buttonandroid: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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {QBadgeView badgeView = mWeakBadge.get();if (badgeView == null || !badgeView.isShown()) {cancel();} else {badgeView.invalidate();}}});addListener(new AnimatorListenerAdapter() {@Overridepublic 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);}}@Overridepublic 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;}@Overridepublic View getTargetView() {return mTargetView;}@Overrideprotected 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;}}@Overridepublic 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);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;}@Overrideprotected 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 borderif (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();}@Overridepublic 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*/@Overridepublic 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;}@Overridepublic int getBadgeNumber() {return mBadgeNumber;}@Overridepublic Badge setBadgeText(String badgeText) {mBadgeText = badgeText;mBadgeNumber = 1;measureText();invalidate();return this;}@Overridepublic String getBadgeText() {return mBadgeText;}@Overridepublic Badge setExactMode(boolean isExact) {mExact = isExact;if (mBadgeNumber > 99) {setBadgeNumber(mBadgeNumber);}return this;}@Overridepublic boolean isExactMode() {return mExact;}@Overridepublic Badge setShowShadow(boolean showShadow) {mShowShadow = showShadow;invalidate();return this;}@Overridepublic boolean isShowShadow() {return mShowShadow;}@Overridepublic 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;}@Overridepublic Badge stroke(int color, float width, boolean isDpValue) {mColorBackgroundBorder = color;mBackgroundBorderWidth = isDpValue ? DisplayUtil.dp2px(getContext(), width) : width;invalidate();return this;}@Overridepublic int getBadgeBackgroundColor() {return mColorBackground;}@Overridepublic Badge setBadgeBackground(Drawable drawable) {return setBadgeBackground(drawable, false);}@Overridepublic Badge setBadgeBackground(Drawable drawable, boolean clip) {mDrawableBackgroundClip = clip;mDrawableBackground = drawable;createClipLayer();invalidate();return this;}@Overridepublic Drawable getBadgeBackground() {return mDrawableBackground;}@Overridepublic Badge setBadgeTextColor(int color) {mColorBadgeText = color;invalidate();return this;}@Overridepublic int getBadgeTextColor() {return mColorBadgeText;}@Overridepublic Badge setBadgeTextSize(float size, boolean isSpValue) {mBadgeTextSize = isSpValue ? DisplayUtil.dp2px(getContext(), size) : size;measureText();invalidate();return this;}@Overridepublic float getBadgeTextSize(boolean isSpValue) {return isSpValue ? DisplayUtil.px2dp(getContext(), mBadgeTextSize) : mBadgeTextSize;}@Overridepublic Badge setBadgePadding(float padding, boolean isDpValue) {mBadgePadding = isDpValue ? DisplayUtil.dp2px(getContext(), padding) : padding;createClipLayer();invalidate();return this;}@Overridepublic float getBadgePadding(boolean isDpValue) {return isDpValue ? DisplayUtil.px2dp(getContext(), mBadgePadding) : mBadgePadding;}@Overridepublic 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*/@Overridepublic 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;}@Overridepublic int getBadgeGravity() {return mBadgeGravity;}@Overridepublic Badge setGravityOffset(float offset, boolean isDpValue) {return setGravityOffset(offset, offset, isDpValue);}@Overridepublic 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;}@Overridepublic float getGravityOffsetX(boolean isDpValue) {return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetX) : mGravityOffsetX;}@Overridepublic float getGravityOffsetY(boolean isDpValue) {return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetY) : mGravityOffsetY;}private void updataListener(int state) {if (mDragStateChangedListener != null)mDragStateChangedListener.onDragStateChanged(state, this, mTargetView);}@Overridepublic Badge setOnDragStateChangedListener(OnDragStateChangedListener l) {mDraggable = l != null;mDragStateChangedListener = l;return this;}@Overridepublic PointF getDragCenter() {if (mDraggable && mDragging) return mDragCenter;return null;}private class BadgeContainer extends ViewGroup {@Overrideprotected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {if(!(getParent() instanceof RelativeLayout)){super.dispatchRestoreInstanceState(container);}}public BadgeContainer(Context context) {super(context);}@Overrideprotected 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());}}@Overrideprotected 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<String> 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<String> 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;@Overrideprotected 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<String> data;public ListAdapter() {data = new DataSupport().getData();}@Overridepublic int getCount() {return data.size();}@Overridepublic Object getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic 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() {@Overridepublic 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"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_list_view"android:layout_width="match_parent"android:layout_height="match_parent"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=".ListViewActivity"><ListViewandroid:id="@+id/listview"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</RelativeLayout>
3.适配器布局item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"><RelativeLayoutandroid:id="@+id/root"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"><ImageViewandroid:id="@+id/imageview"android:layout_width="60dp"android:layout_height="60dp"android:layout_marginRight="10dp"android:src="@mipmap/ic_launcher" /><TextViewandroid:id="@+id/tv_time"android:layout_width="wrap_content"android:layout_height="30dp"android:layout_toRightOf="@+id/imageview"android:textSize="13sp"android:gravity="center_vertical"android:text="2016年8月8日 10:30:02"/><TextViewandroid:id="@+id/tv_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@+id/imageview"android:layout_below="@+id/tv_time"android:textSize="15sp"android:textColor="#333333"android:text="(`・ω・´)ฅ铲屎官快来"/></RelativeLayout>
</FrameLayout>
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;@Overrideprotected 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<RecyclerAdapter.Holder> {private List<String> data;public RecyclerAdapter() {data = new DataSupport().getData();}@Overridepublic Holder onCreateViewHolder(ViewGroup parent, int viewType) {return new Holder(LayoutInflater.from(RecyclerViewActivity.this).inflate(R.layout.item_view, parent, false));}@Overridepublic void onBindViewHolder(Holder holder, int position) {holder.textView.setText(data.get(position));holder.badge.setBadgeNumber(position);}@Overridepublic 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() {@Overridepublic void onDragStateChanged(int dragState, Badge badge, View targetView) {if (dragState == STATE_SUCCEED) {Toast.makeText(RecyclerViewActivity.this, String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();}}});}}}
}
这篇关于Android 一个可以自由定制外观、支持拖拽消除的MaterialDesign风格Android BadgeView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!