Android 自定义消息右上角的数字提示或红点(类似微信或QQ的未读消息提示) 支持自由定制外观、拖拽消除的MaterialDesign风格Android BadgeView,自定义消息右上角的数字提示或红点(类似微信或QQ的未读消息提示)实现消息数量大于0时显示具体消息数量,最大显示99,大于99设置99+效果 一.效果图: 二.添加依赖快速实现: 1.添加依赖:(也可以不添加依赖直接将自定义类写到自己的项目中) 2.代码实现: 3.方法说明: 可以参考:https://github.com/qstumn/BadgeView 三.自定义BadgeView: 1.主函数代码: 2.主函数布局: 3.相关属性: dimens.xml: string.xml: shape_round_rect.xml: 4.实现接口Badge.java: 5.自定义动画BadgeAnimator.java: 6.dp、px转换工具类DisplayUtil.java: 7.数学计算工具类MathUtil.java: 8.自定义QQ消息类QBadgeView.java: 9.模拟数据类: 四.实现QQ或微信聊天列表的消息提醒的话可以往下看: 1.ListViewActivity.java: 2.布局activity_list_view.xml: 3.适配器布局item_view.xml 4.使用RecyclerView实现QQ或微信聊天列表消息数量显示: RecyclerViewActivity.java:
implementation 'q.rorbin:badgeview:1.1.3'
new QBadgeView(context).bindTarget(textview).setBadgeNumber(6);
code
说明
setBadgeNumber
设置Badge数字
setBadgeText
设置Badge文本
setBadgeTextSize
设置文本字体大小
setBadgeTextColor
设置文本颜色
setExactMode
设置是否显示精确模式数值
setBadgeGravity
设置Badge相对于TargetView的位置
setGravityOffset
设置外边距
setBadgePadding
设置内边距
setBadgeBackgroundColor
设置背景色
setBadgeBackground
设置背景图片
setShowShadow
设置是否显示阴影
setOnDragStateChangedListener
打开拖拽消除模式并设置监听
stroke
描边
hide
隐藏Badge
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); } }
<?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>
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> </resources>
<resources> <string name="app_name">My Application</string> <string name="textview">TextView</string> <string name="button">Button</string> </resources>
<?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>
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); } }
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); } } }
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); } }
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)); } }
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()); } } } }
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; } }
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; } } }
<?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>
<?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>
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网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算