在res/values下新建attrs.xml文件
<declare-styleable name="BitmapSubscriptView">
<attr name="subscript_bitmap" format="reference"/>
<attr name="subscript_orientation">
<enum name="top_right" value="0"/>
<enum name="bottom_right" value="1"/>
<enum name="bottom_center" value="2"/>
<enum name="bottom_left" value="3"/>
<enum name="top_left" value="4"/>
<enum name="top_center" value="5"/>
</attr>
<attr name="subscript_radius" format="dimension"/>
<attr name="subscript_color" format="color|reference"/>
<attr name="angular_offset_x" format="dimension"/>
<attr name="angular_offset_y" format="dimension"/>
<attr name="subscript_text" format="integer"/>
<attr name="subscript_text_color" format="color|reference"/>
</declare-styleable>
創建自定義View
public class BitmapSubscriptView extends View {
public BitmapSubscriptView(Context context) {
this(context, null);
}
public BitmapSubscriptView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BitmapSubscriptView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BitmapSubscriptView);
init(typedArray);
}
}
初始化
public static final int TOP_RIGHT = 0;
public static final int BOTTOM_RIGHT = 1;
public static final int BOTTOM_CENTER = 2;
public static final int BOTTOM_LEFT = 3;
public static final int TOP_LEFT = 4;
public static final int TOP_CENTER = 5;
/**
* 默認類型
*/
@IntDef({TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_CENTER, BOTTOM_LEFT, TOP_LEFT,TOP_CENTER})
@Retention(RetentionPolicy.SOURCE)
private @interface SubscriptOrientation {
}
//文字畫筆
private TextPaint textPaint;
/**
* 獲取文字畫筆
*/
public Paint getTextPaint(){
return textPaint;
}
//文字顏色
private int textColor = ContextCompat.getColor(getContext().getApplicationContext(), R.color.colorWhite);
/**
* 設置角標中文字的顏色
* @param textColor 文字顏色
*/
public void setTextColor(int textColor){
this.textColor = textColor;
textPaint.setColor(textColor);
invalidate();
}
//文字
private String subscriptText = "0";
/**
* 設置角標中的文字
* @param subscriptText 角標數字
*/
public void setSubscriptText(String subscriptText){
this.subscriptText = subscriptText;
invalidate();
}
//角標畫筆
private Paint subscriptPaint;
/**
* 獲取角標畫筆
*/
public Paint getSubscriptPaint(){
return subscriptPaint;
}
//角標顏色
private int subscriptColor = ContextCompat.getColor(getContext().getApplicationContext(), R.color.colorAccent);
/**
* 設置角標填充顏色
* @param subscriptColor 角標顏色
*/
public void setSubscriptColor(int subscriptColor){
this.subscriptColor = subscriptColor;
subscriptPaint.setColor(subscriptColor);
invalidate();
}
//角標半徑
private float subscriptRadius ;
/**
* 設置角標半徑
* @param subscriptRadius 半徑
*/
public void setSubscriptRadius(float subscriptRadius){
if (blankBitmap != null){
blankBitmap = null;
}
this.subscriptRadius = subscriptRadius;
invalidate();
}
//角標X軸偏移量
private float angularOffsetX = 0 ;
/**
* 設置角標X軸偏移量
* @param angularOffsetX 正數:向右;負數:向左
*/
public void setAngularOffsetX(float angularOffsetX){
this.angularOffsetX = angularOffsetX;
invalidate();
}
//角標Y軸偏移量
private float angularOffsetY = 0 ;
/**
* 設置角標Y軸偏移量
* @param angularOffsetY 正數:向下;負數:向上
*/
public void setAngularOffsetY(float angularOffsetY){
this.angularOffsetY = angularOffsetY;
invalidate();
}
//角標位置
private int subscriptOrientation;
/**
* 設置角標位置
* @param subscriptOrientation 右上,右下,中下,左下,左上,左中
*/
public void setSubscriptOrientation(@SubscriptOrientation int subscriptOrientation){
this.subscriptOrientation = subscriptOrientation;
invalidate();
}
//空白位圖
private Bitmap blankBitmap;
//位圖
private Bitmap bitmap;
/**
* 設置位圖
* @param id id
* @param isRGB 是否使用RGB_565壓縮圖片
*/
public void setBitmap(@DrawableRes int id,boolean isRGB){
if (blankBitmap != null){
blankBitmap = null;
}
bitmap = getBitmapResources(getResources(), id, isRGB);
invalidate();
}
/**
* 設置位圖
* @param bitmap bitmap
*/
public void setBitmap(Bitmap bitmap) {
if (blankBitmap != null){
blankBitmap = null;
}
this.bitmap = bitmap;
invalidate();
}
//屏幕寬高
private int width;
private int height;
private void init(TypedArray typedArray) {
subscriptOrientation = typedArray.getInt(R.styleable.BitmapSubscriptView_subscript_orientation,0);
angularOffsetX = typedArray.getDimension(R.styleable.BitmapSubscriptView_angular_offset_x,angularOffsetX);
angularOffsetY = typedArray.getDimension(R.styleable.BitmapSubscriptView_angular_offset_y,angularOffsetY);
subscriptRadius = typedArray.getDimension(R.styleable.BitmapSubscriptView_subscript_radius,0);
subscriptColor = typedArray.getColor(R.styleable.BitmapSubscriptView_subscript_color,subscriptColor);
subscriptText = typedArray.getString(R.styleable.BitmapSubscriptView_subscript_text);
textColor = typedArray.getColor(R.styleable.BitmapSubscriptView_subscript_text_color,textColor);
int id = typedArray.getResourceId(R.styleable.BitmapSubscriptView_subscript_bitmap,R.drawable.ic_launcher);
bitmap = getBitmapResources(getResources(),id,false);
typedArray.recycle();
if (subscriptText == null){
subscriptText = "0";
}
subscriptPaint = new Paint();
subscriptPaint.setColor(subscriptColor);
subscriptPaint.setStyle(Paint.Style.FILL);
subscriptPaint.setAntiAlias(true);
textPaint = new TextPaint();
textPaint.setColor(textColor);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setAntiAlias(true);
screenWidth();
}
/**
* 讀取資源文件夾下圖片
*
* @param res getResources
* @param id 文件id
* @param isRGB 是否使用RGB_565壓縮圖片
* @return bitmap
*/
public Bitmap getBitmapResources(Resources res, int id, boolean isRGB) {
TypedValue value = new TypedValue();
InputStream is = res.openRawResource(id, value);
if (isRGB) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeStream(new BufferedInputStream(is), null, options);
}
return BitmapFactory.decodeStream(new BufferedInputStream(is));
}
/**
* 獲取屏幕寬高
*/
private void screenWidth() {
WindowManager wm = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
width = size.x;
height = size.y;
}
}
繪製
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//最短邊距
int length = Math.min(getWidth(),getHeight());
//如果沒有設置角標半徑,或者角標直徑大於等於最短邊距,那麼subscriptRadius = 最短邊距的6分之1
if (subscriptRadius == 0){
subscriptRadius = length/6f;
}else if (2*subscriptRadius >= length){
subscriptRadius = length/6f;
}
if (blankBitmap == null){
//新圖繪製區域高寬
float width = getWidth() - 2*subscriptRadius;
float height = getHeight() - 2*subscriptRadius;
//縮放比例
float scale = Math.min(width/bitmap.getWidth(),height/bitmap.getHeight());
//新圖大小
int a = (int) (bitmap.getWidth()*scale);
int b = (int) (bitmap.getHeight()*scale);
//創建空白位圖
blankBitmap = Bitmap.createBitmap(a, b,Bitmap.Config.ARGB_8888);
Canvas blankCanvas = new Canvas(blankBitmap);
//將傳入的圖片繪製到空白位圖上
Matrix matrix = new Matrix();
matrix.setScale(scale,scale);
blankCanvas.drawBitmap(bitmap, matrix,subscriptPaint);
}
// 新圖X座標 =(控件寬 - 新圖寬)/ 2
float x = (getWidth() - blankBitmap.getWidth())/2f;
float y = (getHeight() - blankBitmap.getHeight())/2f;
//繪製新圖
canvas.drawBitmap(blankBitmap,x,y,subscriptPaint);
// 角標X座標 = (控件寬 + 新圖寬)/2
float subscriptX;
float subscriptY;
switch (subscriptOrientation){
case BOTTOM_RIGHT:
subscriptX = (getWidth() + blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case BOTTOM_CENTER:
subscriptX = getWidth()/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case BOTTOM_LEFT:
subscriptX = (getWidth() - blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case TOP_LEFT:
subscriptX = (getWidth() - blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
case TOP_CENTER:
subscriptX = getWidth()/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
default:
subscriptX = (getWidth() + blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
}
//繪製角標
canvas.drawCircle(subscriptX , subscriptY , subscriptRadius ,subscriptPaint);
textPaint.setTextSize(subscriptRadius/2);
//繪製角標中的數字
canvas.drawText(subscriptText,subscriptX, subscriptY - textPaint.ascent()/2f ,textPaint);
}
測量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
//限制控件大小不能大於屏幕寬高
setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? Math.min(measureWidth, width) : Math.min(bitmap.getWidth(), width)
, (measureHeightMode == MeasureSpec.EXACTLY) ? Math.min(measureHeight, height) : Math.min(bitmap.getHeight(), height));
}
使用
//設置圖片
bitmapSubscriptView.setBitmap(R.drawable.test,false)
bitmapSubscriptView.setBitmap(bitmap)
//設置角標顏色和半徑
bitmapSubscriptView.setSubscriptColor(R.color.colorAccent)
bitmapSubscriptView.setSubscriptRadius(10f)
//設置角標方位和偏移量
bitmapSubscriptView.setSubscriptOrientation(BitmapSubscriptView.TOP_LEFT)
bitmapSubscriptView.setAngularOffsetX(100f)
bitmapSubscriptView.setAngularOffsetY(100f)
//設置角標文字和顏色
bitmapSubscriptView.setSubscriptText("1")
bitmapSubscriptView.setTextColor(R.color.colorWhite)
app:subscript_bitmap="@drawable/test"
app:subscript_radius="40dp"
app:subscript_orientation="bottom_right"
app:subscript_color="@color/colorAccent"
app:subscript_text="1"
app:subscript_text_color="@color/colorWhite"
app:angular_offset_x="-10dp"
app:angular_offset_y="10dp"
完整代碼
public class BitmapSubscriptView extends View {
public static final int TOP_RIGHT = 0;
public static final int BOTTOM_RIGHT = 1;
public static final int BOTTOM_CENTER = 2;
public static final int BOTTOM_LEFT = 3;
public static final int TOP_LEFT = 4;
public static final int TOP_CENTER = 5;
/**
* 默認類型
*/
@IntDef({TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_CENTER, BOTTOM_LEFT, TOP_LEFT,TOP_CENTER})
@Retention(RetentionPolicy.SOURCE)
private @interface SubscriptOrientation {
}
public BitmapSubscriptView(Context context) {
this(context, null);
}
public BitmapSubscriptView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BitmapSubscriptView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BitmapSubscriptView);
init(typedArray);
}
//文字畫筆
private TextPaint textPaint;
/**
* 獲取文字畫筆
*/
public Paint getTextPaint(){
return textPaint;
}
//文字顏色
private int textColor = ContextCompat.getColor(getContext().getApplicationContext(), R.color.colorWhite);
/**
* 設置角標中文字的顏色
* @param textColor 文字顏色
*/
public void setTextColor(int textColor){
this.textColor = textColor;
textPaint.setColor(textColor);
invalidate();
}
//文字
private String subscriptText = "0";
/**
* 設置角標中的文字
* @param subscriptText 角標數字
*/
public void setSubscriptText(String subscriptText){
this.subscriptText = subscriptText;
invalidate();
}
//角標畫筆
private Paint subscriptPaint;
/**
* 獲取角標畫筆
*/
public Paint getSubscriptPaint(){
return subscriptPaint;
}
//角標顏色
private int subscriptColor = ContextCompat.getColor(getContext().getApplicationContext(), R.color.colorAccent);
/**
* 設置角標填充顏色
* @param subscriptColor 角標顏色
*/
public void setSubscriptColor(int subscriptColor){
this.subscriptColor = subscriptColor;
subscriptPaint.setColor(subscriptColor);
invalidate();
}
//角標半徑
private float subscriptRadius ;
/**
* 設置角標半徑
* @param subscriptRadius 半徑
*/
public void setSubscriptRadius(float subscriptRadius){
this.subscriptRadius = subscriptRadius;
invalidate();
}
//角標X軸偏移量
private float angularOffsetX = 0 ;
/**
* 設置角標X軸偏移量
* @param angularOffsetX 正數:向右;負數:向左
*/
public void setAngularOffsetX(float angularOffsetX){
this.angularOffsetX = angularOffsetX;
invalidate();
}
//角標Y軸偏移量
private float angularOffsetY = 0 ;
/**
* 設置角標Y軸偏移量
* @param angularOffsetY 正數:向下;負數:向上
*/
public void setAngularOffsetY(float angularOffsetY){
this.angularOffsetY = angularOffsetY;
invalidate();
}
//角標位置
private int subscriptOrientation;
/**
* 設置角標位置
* @param subscriptOrientation 右上,右下,中下,左下,左上,左中
*/
public void setSubscriptOrientation(@SubscriptOrientation int subscriptOrientation){
this.subscriptOrientation = subscriptOrientation;
invalidate();
}
//空白位圖
private Bitmap blankBitmap;
//位圖
private Bitmap bitmap;
/**
* 設置位圖
* @param id id
* @param isRGB 是否使用RGB_565壓縮圖片
*/
public void setBitmap(@DrawableRes int id,boolean isRGB){
bitmap = getBitmapResources(getResources(), id, isRGB);
invalidate();
}
/**
* 設置位圖
* @param bitmap bitmap
*/
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
invalidate();
}
//屏幕寬高
private int width;
private int height;
private void init(TypedArray typedArray) {
subscriptOrientation = typedArray.getInt(R.styleable.BitmapSubscriptView_subscript_orientation,0);
angularOffsetX = typedArray.getDimension(R.styleable.BitmapSubscriptView_angular_offset_x,angularOffsetX);
angularOffsetY = typedArray.getDimension(R.styleable.BitmapSubscriptView_angular_offset_y,angularOffsetY);
subscriptRadius = typedArray.getDimension(R.styleable.BitmapSubscriptView_subscript_radius,0);
subscriptColor = typedArray.getColor(R.styleable.BitmapSubscriptView_subscript_color,subscriptColor);
subscriptText = typedArray.getString(R.styleable.BitmapSubscriptView_subscript_text);
textColor = typedArray.getColor(R.styleable.BitmapSubscriptView_subscript_text_color,textColor);
int id = typedArray.getResourceId(R.styleable.BitmapSubscriptView_subscript_bitmap,R.drawable.ic_launcher);
bitmap = getBitmapResources(getResources(),id,false);
typedArray.recycle();
if (subscriptText == null){
subscriptText = "0";
}
subscriptPaint = new Paint();
subscriptPaint.setColor(subscriptColor);
subscriptPaint.setStyle(Paint.Style.FILL);
subscriptPaint.setAntiAlias(true);
textPaint = new TextPaint();
textPaint.setColor(textColor);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setAntiAlias(true);
screenWidth();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//最短邊距
int length = Math.min(getWidth(),getHeight());
//如果沒有設置角標半徑,或者角標直徑大於等於最短邊距,那麼subscriptRadius = 最短邊距的6分之1
if (subscriptRadius == 0){
subscriptRadius = length/6f;
}else if (2*subscriptRadius >= length){
subscriptRadius = length/6f;
}
if (blankBitmap == null){
//新圖繪製區域高寬
float width = getWidth() - 2*subscriptRadius;
float height = getHeight() - 2*subscriptRadius;
//縮放比例
float scale = Math.min(width/bitmap.getWidth(),height/bitmap.getHeight());
//新圖大小
int a = (int) (bitmap.getWidth()*scale);
int b = (int) (bitmap.getHeight()*scale);
//創建空白位圖
blankBitmap = Bitmap.createBitmap(a, b,Bitmap.Config.ARGB_8888);
Canvas blankCanvas = new Canvas(blankBitmap);
//將傳入的圖片繪製到空白位圖上
Matrix matrix = new Matrix();
matrix.setScale(scale,scale);
blankCanvas.drawBitmap(bitmap, matrix,subscriptPaint);
}
// 新圖X座標 =(控件寬 - 新圖寬)/ 2
float x = (getWidth() - blankBitmap.getWidth())/2f;
float y = (getHeight() - blankBitmap.getHeight())/2f;
//繪製新圖
canvas.drawBitmap(blankBitmap,x,y,subscriptPaint);
// 角標X座標 = (控件寬 + 新圖寬)/2
float subscriptX;
float subscriptY;
switch (subscriptOrientation){
case BOTTOM_RIGHT:
subscriptX = (getWidth() + blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case BOTTOM_CENTER:
subscriptX = getWidth()/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case BOTTOM_LEFT:
subscriptX = (getWidth() - blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() + blankBitmap.getHeight())/2f + angularOffsetY;
break;
case TOP_LEFT:
subscriptX = (getWidth() - blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
case TOP_CENTER:
subscriptX = getWidth()/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
default:
subscriptX = (getWidth() + blankBitmap.getWidth())/2f + angularOffsetX;
subscriptY = (getHeight() - blankBitmap.getHeight())/2f + angularOffsetY;
break;
}
//繪製角標
canvas.drawCircle(subscriptX , subscriptY , subscriptRadius ,subscriptPaint);
textPaint.setTextSize(subscriptRadius/2);
//繪製角標中的數字
canvas.drawText(subscriptText,subscriptX, subscriptY - textPaint.ascent()/2f ,textPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
//限制控件大小不能大於屏幕寬高
setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? Math.min(measureWidth, width) : Math.min(bitmap.getWidth(), width)
, (measureHeightMode == MeasureSpec.EXACTLY) ? Math.min(measureHeight, height) : Math.min(bitmap.getHeight(), height));
}
/**
* 讀取資源文件夾下圖片
*
* @param res getResources
* @param id 文件id
* @param isRGB 是否使用RGB_565壓縮圖片
* @return bitmap
*/
public Bitmap getBitmapResources(Resources res, int id, boolean isRGB) {
TypedValue value = new TypedValue();
InputStream is = res.openRawResource(id, value);
if (isRGB) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeStream(new BufferedInputStream(is), null, options);
}
return BitmapFactory.decodeStream(new BufferedInputStream(is));
}
/**
* 獲取屏幕寬高
*/
private void screenWidth() {
WindowManager wm = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
width = size.x;
height = size.y;
}
}
}