Android自定义消息提示容器
2019-06-05 本文已影响5人
Android师哥
night_rain.png
自定义View,一是为了满足设计需求,二是开发者进阶的标志之一。随心所欲就是我等奋斗的目标!!!
效果
效果图实现逻辑
-
知识点
1、dispatchDraw(Canvas canvas)
绘制子View,在这里绘制可以保证在其子View的上层
2、TextPaint
默认从底部开始绘制文字,setTextAlign()
可以设置绘制文本的对齐方式 -
实现思路
1、确认绘制背景的高度,因为不管是否开启自适应,高度始终不变。代码里体现在countDefaultTextBackgroundSize()
中
2、根据自定义属性,确定绘制区域。代码里体现在countHintTextPosition()
中
3、位置确定着手开始绘制背景和数字。dispatchDraw()
全部代码
/**
* 自定义消息提示容器
* {@link #countHintTextPosition() 计算绘制位置}
* {@link #countDefaultTextBackgroundSize() 计算背景高度}
*
* @attr customHintTextColor //消息数字颜色
* @attr customHintTextSize //消息数字尺寸
* @attr customHintTextBackground //消息背景颜色
* @attr customSelfAdaption //背景是否自适应数字大小
* @attr customHintTextMarginLeft //距离容器左侧外边距
* @attr customHintTextMarginRight //距离容器右侧外边距
* @attr customHintTextMarginTop //距离容器顶部外边距
* @attr customHintTextMarginBottom //距离容器底部外边距
* @attr customHintPosition //消息提示相对容器位置
*/
public class CustomMessageHintContainerView extends LinearLayout {
private static final String TAG = "CustomMessageHintContai";
//提示文字默认长度
private final int DEFAULT_LENGTH = 3;
//左上
public static final int POSITION_TOP_LIFT = 101;
//左下
public static final int POSITION_BOTTOM_LIFT = 102;
//右上
public static final int POSITION_TOP_RIGHT = 103;
//右下
public static final int POSITION_BOTTOM_RIGHT = 104;
/**
* 提示文字内容
*/
private String mMessageHintTextContent;
//绘制提示文字背景的画笔
private Paint mPaint;
//绘制提示文字的画笔
private TextPaint mTextPaint;
//提示文字的颜色
private int mMessageHintTextColor;
//提示文字的尺寸
private float mMessageHintTextSize;
//提示文字的背景颜色
private int mMessageHintBackgroundColor;
//提示内容相对容器的位置
private int mMessageHintPosition;
//是否根据文字自适应背景大小
private boolean isSelfAdaption;
//提示文字外边距
private float mMessageHintMarginLeft;
private float mMessageHintMarginTop;
private float mMessageHintMarginRight;
private float mMessageHintMarginBottom;
//默认背景的高度
private float mDefaultBackgroundHeight = 0;
public CustomMessageHintContainerView(Context context) {
this(context, null);
}
public CustomMessageHintContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomMessageHintContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始默认属性
initDefaultAttrs();
//初始自定义属性
initAttrs(context, attrs);
//初始画笔
initPaint();
//计算默认情况下需要的背景尺寸
countDefaultTextBackgroundSize();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
RectF mRectF = countHintTextPosition();
if (mRectF != null) {
float backgroundWidth = mRectF.width();
float backgroundHeight = mRectF.height();
float radius = backgroundHeight / 2.0F;
//绘制背景
canvas.drawRoundRect(mRectF, radius, radius, mPaint);
Rect measureTextSize = measureTextSize("8");
//绘制文字
if (!isSelfAdaption) {
if (Integer.parseInt(mMessageHintTextContent) > 99) {
canvas.drawText("99+", backgroundWidth / 2.0F + mRectF.left, radius + mRectF.top + measureTextSize.height() / 2.0F, mTextPaint);
} else {
canvas.drawText(mMessageHintTextContent, backgroundWidth / 2.0F + mRectF.left, radius + mRectF.top + measureTextSize.height() / 2.0F, mTextPaint);
}
} else {
canvas.drawText(mMessageHintTextContent, backgroundWidth / 2.0F + mRectF.left, radius + mRectF.top + measureTextSize.height() / 2.0F, mTextPaint);
}
}
}
/**
* 计算默认背景尺寸
*/
private void countDefaultTextBackgroundSize() {
if (!TextUtils.isEmpty(mMessageHintTextContent)) {
StringBuffer mStringBuffer = new StringBuffer();
for (int i = 0; i < DEFAULT_LENGTH; i++) {
mStringBuffer.append("8");
}
Rect mDefaultTextRect = measureTextSize(mStringBuffer.toString());
mDefaultBackgroundHeight = (float) Math.ceil(Math.sqrt((Math.pow(mDefaultTextRect.width(), 2) + Math.pow(mDefaultTextRect.height(), 2))));
}
}
/**
* 计算提示文字显示位置
*/
private RectF countHintTextPosition() {
RectF mRectF = null;
if (!TextUtils.isEmpty(mMessageHintTextContent)) {
if (mDefaultBackgroundHeight == 0) {
countDefaultTextBackgroundSize();
}
//显示内容的文字尺寸
Rect mTextContentRect = measureTextSize(mMessageHintTextContent);
int mMeasuredWidth = this.getMeasuredWidth();
int mMeasuredHeight = this.getMeasuredHeight();
mRectF = new RectF();
switch (mMessageHintPosition) {
case POSITION_TOP_LIFT:
mRectF.top = mMessageHintMarginTop;
mRectF.bottom = mMessageHintMarginTop + mDefaultBackgroundHeight;
mRectF.left = mMessageHintMarginLeft;
if (isSelfAdaption) {
if (mMessageHintTextContent.length() <= DEFAULT_LENGTH) {
mRectF.right = mMessageHintMarginLeft + mDefaultBackgroundHeight;
} else {
mRectF.right = mMessageHintMarginLeft + mTextContentRect.width() + mDefaultBackgroundHeight / 2;
}
} else {
mRectF.right = mMessageHintMarginLeft + mDefaultBackgroundHeight;
}
break;
case POSITION_BOTTOM_LIFT:
mRectF.left = mMessageHintMarginLeft;
mRectF.bottom = mMeasuredHeight - mMessageHintMarginBottom;
mRectF.top = mMeasuredHeight - mMessageHintMarginBottom - mDefaultBackgroundHeight;
if (isSelfAdaption) {
if (mMessageHintTextContent.length() <= DEFAULT_LENGTH) {
mRectF.right = mMessageHintMarginLeft + mDefaultBackgroundHeight;
} else {
mRectF.right = mMessageHintMarginLeft + mTextContentRect.width() + mDefaultBackgroundHeight / 2;
}
} else {
mRectF.right = mMessageHintMarginLeft + mDefaultBackgroundHeight;
}
break;
case POSITION_TOP_RIGHT:
mRectF.top = mMessageHintMarginTop;
mRectF.bottom = mMessageHintMarginTop + mDefaultBackgroundHeight;
mRectF.right = mMeasuredWidth - mMessageHintMarginRight;
if (isSelfAdaption) {
if (mMessageHintTextContent.length() <= DEFAULT_LENGTH) {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mDefaultBackgroundHeight);
} else {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mTextContentRect.width() + mDefaultBackgroundHeight / 2);
}
} else {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mDefaultBackgroundHeight);
}
break;
case POSITION_BOTTOM_RIGHT:
mRectF.right = mMeasuredWidth - mMessageHintMarginRight;
mRectF.bottom = mMeasuredHeight - mMessageHintMarginBottom;
mRectF.top = mMeasuredHeight - mMessageHintMarginBottom - mDefaultBackgroundHeight;
if (isSelfAdaption) {
if (mMessageHintTextContent.length() <= DEFAULT_LENGTH) {
mRectF.left = mMeasuredWidth - mMessageHintMarginRight - mDefaultBackgroundHeight;
} else {
mRectF.left = mMeasuredWidth - mMessageHintMarginRight - mTextContentRect.width() - mDefaultBackgroundHeight / 2;
}
} else {
mRectF.left = mMeasuredWidth - mMessageHintMarginRight - mDefaultBackgroundHeight;
}
break;
default:
mRectF.top = mMessageHintMarginTop;
mRectF.bottom = mMessageHintMarginTop + mDefaultBackgroundHeight;
mRectF.right = mMeasuredWidth - mMessageHintMarginRight;
if (isSelfAdaption) {
if (mMessageHintTextContent.length() <= DEFAULT_LENGTH) {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mDefaultBackgroundHeight);
} else {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mTextContentRect.width() + mDefaultBackgroundHeight / 2);
}
} else {
mRectF.left = mMeasuredWidth - (mMessageHintMarginRight + mDefaultBackgroundHeight);
}
}
}
return mRectF;
}
//初始化画笔
private void initPaint() {
//绘制背景画笔
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(mMessageHintBackgroundColor);
//绘制文字画笔
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mMessageHintTextColor);
mTextPaint.setTextSize(mMessageHintTextSize);
mTextPaint.setTextAlign(TextPaint.Align.CENTER);
}
/**
* 初始默认属性
*/
private void initDefaultAttrs() {
//默认文字颜色为白色
mMessageHintTextColor = Color.WHITE;
//默认文字大小12SP
mMessageHintTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());
//默认提示文字背景颜色红色
mMessageHintBackgroundColor = Color.RED;
//默认提示文字位置在右上角
mMessageHintPosition = POSITION_TOP_RIGHT;
//默认开启文字自适应
isSelfAdaption = true;
//默认外边距
mMessageHintMarginLeft = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
mMessageHintMarginRight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
mMessageHintMarginTop = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
mMessageHintMarginBottom = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics());
}
/**
* 初始化自定义属性
*/
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomMessageHintContainerView);
isSelfAdaption = mTypedArray.getBoolean(R.styleable.CustomMessageHintContainerView_customSelfAdaption, isSelfAdaption);
mMessageHintBackgroundColor = mTypedArray.getColor(R.styleable.CustomMessageHintContainerView_customHintTextBackground, mMessageHintBackgroundColor);
mMessageHintTextColor = mTypedArray.getColor(R.styleable.CustomMessageHintContainerView_customHintTextColor, mMessageHintTextColor);
mMessageHintTextSize = mTypedArray.getDimension(R.styleable.CustomMessageHintContainerView_customHintTextSize, mMessageHintTextSize);
mMessageHintPosition = mTypedArray.getInt(R.styleable.CustomMessageHintContainerView_customHintPosition, mMessageHintPosition);
mMessageHintMarginLeft = mTypedArray.getDimension(R.styleable.CustomMessageHintContainerView_customHintTextMarginLeft, mMessageHintMarginLeft);
mMessageHintMarginRight = mTypedArray.getDimension(R.styleable.CustomMessageHintContainerView_customHintTextMarginRight, mMessageHintMarginRight);
mMessageHintMarginTop = mTypedArray.getDimension(R.styleable.CustomMessageHintContainerView_customHintTextMarginTop, mMessageHintMarginTop);
mMessageHintMarginBottom = mTypedArray.getDimension(R.styleable.CustomMessageHintContainerView_customHintTextMarginBottom, mMessageHintMarginBottom);
mTypedArray.recycle();
}
/**
* 测量文字的尺寸
*
* @return 一个汉字的矩阵
*/
private Rect measureTextSize(String content) {
Rect mRect = null;
if (!TextUtils.isEmpty(content)) {
if (mTextPaint != null) {
mRect = new Rect();
mTextPaint.getTextBounds(content, 0, content.length(), mRect);
}
}
return mRect;
}
/**
* 设置提示消息数
*
* @param number 提示数
*/
public void setMessageNumber(int number) {
this.mMessageHintTextContent = String.valueOf(number);
invalidate();
}
/**
* 设置背景根据内容自适应
*
* @param isSelfAdaption 是否开启自适应
*/
public void setSelfAdaption(boolean isSelfAdaption) {
this.isSelfAdaption = isSelfAdaption;
invalidate();
}
/**
* 设置消息相对容器的位置
*
* @param messageHintPosition 位置
* <p> See
* {@link #POSITION_TOP_RIGHT 右上角},
* {@link #POSITION_BOTTOM_LIFT 左下角},
* {@link #POSITION_BOTTOM_RIGHT 右下角},
* {@link #POSITION_TOP_LIFT 左上角}
*/
public void setMessageHintPosition(int messageHintPosition) {
this.mMessageHintPosition = messageHintPosition;
invalidate();
}
/**
* 获取提示消息数
*
* @return 消息数
*/
public int getMessageHintCount() {
if (TextUtils.isEmpty(mMessageHintTextContent)) {
return 0;
}
return Integer.parseInt(mMessageHintTextContent);
}
/**
* 获取自适应状态
*
* @return 状态
*/
public boolean getSelfAdaptionStatus() {
return isSelfAdaption;
}
}
自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomMessageHintContainerView">
<attr name="customHintTextColor" format="color" />
<attr name="customHintTextSize" format="dimension" />
<attr name="customHintTextBackground" format="color" />
<attr name="customSelfAdaption" format="boolean" />
<attr name="customHintTextMarginLeft" format="dimension" />
<attr name="customHintTextMarginRight" format="dimension" />
<attr name="customHintTextMarginTop" format="dimension" />
<attr name="customHintTextMarginBottom" format="dimension" />
<attr name="customHintPosition">
<enum name="top_left" value="101" />
<enum name="bottom_left" value="102" />
<enum name="top_right" value="103" />
<enum name="bottom_right" value="104" />
</attr>
</declare-styleable>
</resources>