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 |
可以参考: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="https://schemas.android.com/apk/res/android" xmlns:tools="https://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="https://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 : https://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(), mBadgeBackgroundRect.width() / 2f, mBadgeBackgroundBorderPaint); } else { canvas.drawRoundRect(mBadgeBackgroundRect, mBadgeBackgroundRect.height() / 2, mBadgeBackgroundRect.height() / 2, mBadgeBackgroundBorderPaint); } } else { canvas.drawRect(mBadgeBackgroundRect, mBadgeBackgroundBorderPaint); } } private void createClipLayer() { if (mBadgeText == null) { return; } if (!mDrawableBackgroundClip) { return; } if (mBitmapClip != null && !mBitmapClip.isRecycled()) { mBitmapClip.recycle(); } float radius = getBadgeCircleRadius(); if (mBadgeText.isEmpty() || mBadgeText.length() == 1) { mBitmapClip = Bitmap.createBitmap((int) radius * 2, (int) radius * 2, Bitmap.Config.ARGB_4444); Canvas srcCanvas = new Canvas(mBitmapClip); srcCanvas.drawCircle(srcCanvas.getWidth() / 2f, srcCanvas.getHeight() / 2f, srcCanvas.getWidth() / 2f, mBadgeBackgroundPaint); } else { mBitmapClip = Bitmap.createBitmap((int) (mBadgeTextRect.width() + mBadgePadding * 2), (int) (mBadgeTextRect.height() + mBadgePadding), Bitmap.Config.ARGB_4444); Canvas srcCanvas = new Canvas(mBitmapClip); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { srcCanvas.drawRoundRect(0, 0, srcCanvas.getWidth(), srcCanvas.getHeight(), srcCanvas.getHeight() / 2f, srcCanvas.getHeight() / 2f, mBadgeBackgroundPaint); } else { srcCanvas.drawRoundRect(new RectF(0, 0, srcCanvas.getWidth(), srcCanvas.getHeight()), srcCanvas.getHeight() / 2f, srcCanvas.getHeight() / 2f, mBadgeBackgroundPaint); } } } private float getBadgeCircleRadius() { if (mBadgeText.isEmpty()) { return mBadgePadding; } else if (mBadgeText.length() == 1) { return mBadgeTextRect.height() > mBadgeTextRect.width() ? mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f : mBadgeTextRect.width() / 2f + mBadgePadding * 0.5f; } else { return mBadgeBackgroundRect.height() / 2f; } } private void findBadgeCenter() { float rectWidth = mBadgeTextRect.height() > mBadgeTextRect.width() ? mBadgeTextRect.height() : mBadgeTextRect.width(); switch (mBadgeGravity) { case Gravity.START | Gravity.TOP: mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f; mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f; break; case Gravity.START | Gravity.BOTTOM: mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f; mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f); break; case Gravity.END | Gravity.TOP: mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f); mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f; break; case Gravity.END | Gravity.BOTTOM: mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f); mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f); break; case Gravity.CENTER: mBadgeCenter.x = mWidth / 2f; mBadgeCenter.y = mHeight / 2f; break; case Gravity.CENTER | Gravity.TOP: mBadgeCenter.x = mWidth / 2f; mBadgeCenter.y = mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f; break; case Gravity.CENTER | Gravity.BOTTOM: mBadgeCenter.x = mWidth / 2f; mBadgeCenter.y = mHeight - (mGravityOffsetY + mBadgePadding + mBadgeTextRect.height() / 2f); break; case Gravity.CENTER | Gravity.START: mBadgeCenter.x = mGravityOffsetX + mBadgePadding + rectWidth / 2f; mBadgeCenter.y = mHeight / 2f; break; case Gravity.CENTER | Gravity.END: mBadgeCenter.x = mWidth - (mGravityOffsetX + mBadgePadding + rectWidth / 2f); mBadgeCenter.y = mHeight / 2f; break; } initRowBadgeCenter(); } private void measureText() { mBadgeTextRect.left = 0; mBadgeTextRect.top = 0; if (TextUtils.isEmpty(mBadgeText)) { mBadgeTextRect.right = 0; mBadgeTextRect.bottom = 0; } else { mBadgeTextPaint.setTextSize(mBadgeTextSize); mBadgeTextRect.right = mBadgeTextPaint.measureText(mBadgeText); mBadgeTextFontMetrics = mBadgeTextPaint.getFontMetrics(); mBadgeTextRect.bottom = mBadgeTextFontMetrics.descent - mBadgeTextFontMetrics.ascent; } createClipLayer(); } private void initRowBadgeCenter() { int[] screenPoint = new int[2]; getLocationOnScreen(screenPoint); mRowBadgeCenter.x = mBadgeCenter.x + screenPoint[0]; mRowBadgeCenter.y = mBadgeCenter.y + screenPoint[1]; } protected void animateHide(PointF center) { if (mBadgeText == null) { return; } if (mAnimator == null || !mAnimator.isRunning()) { screenFromWindow(true); mAnimator = new BadgeAnimator(createBadgeBitmap(), center, this); mAnimator.start(); setBadgeNumber(0); } } public void reset() { mDragCenter.x = -1000; mDragCenter.y = -1000; mDragQuadrant = 4; screenFromWindow(false); getParent().requestDisallowInterceptTouchEvent(false); invalidate(); } @Override public void hide(boolean animate) { if (animate && mActivityRoot != null) { initRowBadgeCenter(); animateHide(mRowBadgeCenter); } else { setBadgeNumber(0); } } /** * @param badgeNumber equal to zero badge will be hidden, less than zero show dot */ @Override public Badge setBadgeNumber(int badgeNumber) { mBadgeNumber = badgeNumber; if (mBadgeNumber < 0) { mBadgeText = ""; } else if (mBadgeNumber > 99) { mBadgeText = mExact ? String.valueOf(mBadgeNumber) : "99+"; } else if (mBadgeNumber > 0 && mBadgeNumber <= 99) { mBadgeText = String.valueOf(mBadgeNumber); } else if (mBadgeNumber == 0) { mBadgeText = null; } measureText(); invalidate(); return this; } @Override public int getBadgeNumber() { return mBadgeNumber; } @Override public Badge setBadgeText(String badgeText) { mBadgeText = badgeText; mBadgeNumber = 1; measureText(); invalidate(); return this; } @Override public String getBadgeText() { return mBadgeText; } @Override public Badge setExactMode(boolean isExact) { mExact = isExact; if (mBadgeNumber > 99) { setBadgeNumber(mBadgeNumber); } return this; } @Override public boolean isExactMode() { return mExact; } @Override public Badge setShowShadow(boolean showShadow) { mShowShadow = showShadow; invalidate(); return this; } @Override public boolean isShowShadow() { return mShowShadow; } @Override public Badge setBadgeBackgroundColor(int color) { mColorBackground = color; if (mColorBackground == Color.TRANSPARENT) { mBadgeTextPaint.setXfermode(null); } else { mBadgeTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); } invalidate(); return this; } @Override public Badge stroke(int color, float width, boolean isDpValue) { mColorBackgroundBorder = color; mBackgroundBorderWidth = isDpValue ? DisplayUtil.dp2px(getContext(), width) : width; invalidate(); return this; } @Override public int getBadgeBackgroundColor() { return mColorBackground; } @Override public Badge setBadgeBackground(Drawable drawable) { return setBadgeBackground(drawable, false); } @Override public Badge setBadgeBackground(Drawable drawable, boolean clip) { mDrawableBackgroundClip = clip; mDrawableBackground = drawable; createClipLayer(); invalidate(); return this; } @Override public Drawable getBadgeBackground() { return mDrawableBackground; } @Override public Badge setBadgeTextColor(int color) { mColorBadgeText = color; invalidate(); return this; } @Override public int getBadgeTextColor() { return mColorBadgeText; } @Override public Badge setBadgeTextSize(float size, boolean isSpValue) { mBadgeTextSize = isSpValue ? DisplayUtil.dp2px(getContext(), size) : size; measureText(); invalidate(); return this; } @Override public float getBadgeTextSize(boolean isSpValue) { return isSpValue ? DisplayUtil.px2dp(getContext(), mBadgeTextSize) : mBadgeTextSize; } @Override public Badge setBadgePadding(float padding, boolean isDpValue) { mBadgePadding = isDpValue ? DisplayUtil.dp2px(getContext(), padding) : padding; createClipLayer(); invalidate(); return this; } @Override public float getBadgePadding(boolean isDpValue) { return isDpValue ? DisplayUtil.px2dp(getContext(), mBadgePadding) : mBadgePadding; } @Override public boolean isDraggable() { return mDraggable; } /** * @param gravity only support Gravity.START | Gravity.TOP , Gravity.END | Gravity.TOP , * Gravity.START | Gravity.BOTTOM , Gravity.END | Gravity.BOTTOM , * Gravity.CENTER , Gravity.CENTER | Gravity.TOP , Gravity.CENTER | Gravity.BOTTOM , * Gravity.CENTER | Gravity.START , Gravity.CENTER | Gravity.END */ @Override public Badge setBadgeGravity(int gravity) { if (gravity == (Gravity.START | Gravity.TOP) || gravity == (Gravity.END | Gravity.TOP) || gravity == (Gravity.START | Gravity.BOTTOM) || gravity == (Gravity.END | Gravity.BOTTOM) || gravity == (Gravity.CENTER) || gravity == (Gravity.CENTER | Gravity.TOP) || gravity == (Gravity.CENTER | Gravity.BOTTOM) || gravity == (Gravity.CENTER | Gravity.START) || gravity == (Gravity.CENTER | Gravity.END)) { mBadgeGravity = gravity; invalidate(); } else { throw new IllegalStateException("only support Gravity.START | Gravity.TOP , Gravity.END | Gravity.TOP , " + "Gravity.START | Gravity.BOTTOM , Gravity.END | Gravity.BOTTOM , Gravity.CENTER" + " , Gravity.CENTER | Gravity.TOP , Gravity.CENTER | Gravity.BOTTOM ," + "Gravity.CENTER | Gravity.START , Gravity.CENTER | Gravity.END"); } return this; } @Override public int getBadgeGravity() { return mBadgeGravity; } @Override public Badge setGravityOffset(float offset, boolean isDpValue) { return setGravityOffset(offset, offset, isDpValue); } @Override public Badge setGravityOffset(float offsetX, float offsetY, boolean isDpValue) { mGravityOffsetX = isDpValue ? DisplayUtil.dp2px(getContext(), offsetX) : offsetX; mGravityOffsetY = isDpValue ? DisplayUtil.dp2px(getContext(), offsetY) : offsetY; invalidate(); return this; } @Override public float getGravityOffsetX(boolean isDpValue) { return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetX) : mGravityOffsetX; } @Override public float getGravityOffsetY(boolean isDpValue) { return isDpValue ? DisplayUtil.px2dp(getContext(), mGravityOffsetY) : mGravityOffsetY; } private void updataListener(int state) { if (mDragStateChangedListener != null) mDragStateChangedListener.onDragStateChanged(state, this, mTargetView); } @Override public Badge setOnDragStateChangedListener(OnDragStateChangedListener l) { mDraggable = l != null; mDragStateChangedListener = l; return this; } @Override public PointF getDragCenter() { if (mDraggable && mDragging) return mDragCenter; return null; } private class BadgeContainer extends ViewGroup { @Override protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { if(!(getParent() instanceof RelativeLayout)){ super.dispatchRestoreInstanceState(container); } } public BadgeContainer(Context context) { super(context); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { View targetView = null, badgeView = null; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (!(child instanceof QBadgeView)) { targetView = child; } else { badgeView = child; } } if (targetView == null) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } else { targetView.measure(widthMeasureSpec, heightMeasureSpec); if (badgeView != null) { badgeView.measure(MeasureSpec.makeMeasureSpec(targetView.getMeasuredWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(targetView.getMeasuredHeight(), MeasureSpec.EXACTLY)); } setMeasuredDimension(targetView.getMeasuredWidth(), targetView.getMeasuredHeight()); } } } }
9.模拟数据类:
import java.util.ArrayList; import java.util.List; /** * 模拟数据类 */ public class DataSupport { private List<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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list_view); listview = (ListView) findViewById(R.id.listview); listview.setAdapter(new ListAdapter()); } class ListAdapter extends BaseAdapter { private List<String> data; public ListAdapter() { data = new DataSupport().getData(); } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { Holder holder; if (convertView == null) { holder = new Holder(); convertView = LayoutInflater.from(ListViewActivity.this).inflate(R.layout.item_view, parent, false); holder.textView = (TextView) convertView.findViewById(R.id.tv_content); holder.badge = new QBadgeView(ListViewActivity.this).bindTarget(convertView.findViewById(R.id.imageview)); holder.badge.setBadgeTextSize(12, true); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } holder.textView.setText(data.get(position)); holder.badge.setBadgeNumber(position); holder.badge.setOnDragStateChangedListener(new Badge.OnDragStateChangedListener() { @Override public void onDragStateChanged(int dragState, Badge badge, View targetView) { if (dragState == STATE_SUCCEED) { Toast.makeText(ListViewActivity.this, String.valueOf(position), Toast.LENGTH_SHORT).show(); } } }); return convertView; } class Holder { TextView textView; Badge badge; } } }
2.布局activity_list_view.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://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"> <ListView android: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="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:id="@+id/root" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp"> <ImageView android:id="@+id/imageview" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginRight="10dp" android:src="@mipmap/ic_launcher" /> <TextView android: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"/> <TextView android: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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view); recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new RecyclerAdapter()); } class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.Holder> { private List<String> data; public RecyclerAdapter() { data = new DataSupport().getData(); } @Override public Holder onCreateViewHolder(ViewGroup parent, int viewType) { return new Holder(LayoutInflater.from(RecyclerViewActivity.this).inflate(R.layout.item_view, parent, false)); } @Override public void onBindViewHolder(Holder holder, int position) { holder.textView.setText(data.get(position)); holder.badge.setBadgeNumber(position); } @Override public int getItemCount() { return data.size(); } class Holder extends RecyclerView.ViewHolder { TextView textView; Badge badge; public Holder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.tv_content); badge = new QBadgeView(RecyclerViewActivity.this).bindTarget(itemView.findViewById(R.id.root)); badge.setBadgeGravity(Gravity.CENTER | Gravity.END); badge.setBadgeTextSize(14, true); badge.setBadgePadding(6, true); badge.setOnDragStateChangedListener(new Badge.OnDragStateChangedListener() { @Override public void onDragStateChanged(int dragState, Badge badge, View targetView) { if (dragState == STATE_SUCCEED) { Toast.makeText(RecyclerViewActivity.this, String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } } }); } } } }
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算