项目中有一些弹窗和聊天的文字背景框,点9图有点难用,用代码绘制这种有一个尖角的圆角矩形。可以设置三角的位置,整体颜色,圆角大小。

记录一下,还有一些情况没有处理,现在在布局中设置padding,是矩形的内边距,不用考虑尖角的长宽,但是获取padding时会加上,还需要完善
/**
* 有一个尖角的圆角矩形
*/
//TODO 处理padding,以获取更一致的符合预期的padding值
//TODO 处理设置不合理数值的情况,比如offset过大过小等等
public class ChatLinearLayout extends LinearLayout {
int chatBackgroundColor = Color.parseColor("#000000");
// 尖角的位置
int triangleGravity = 0;
private static final int LEFT = 0;
private static final int RIGHT = 1;
private static final int TOP = 2;
private static final int BOTTOM = 3;
// 尖角基准位置,配合offset
// BASE_START 当triangleGravity为LEFT,RIGHT时候,offset就是尖角和ChatLinearLayout的top的距离,BASE_END是到bottom的距离
// 当triangleGravity为TOP、BOTTOM的时候,offset是尖角和ChatLinearLayout的left的距离,BASE_END是到right的距离
// 为CENTER,就是在中间
int base = 2;
private static final int BASE_START = 0;
private static final int BASE_END = 1;
private static final int CENTER = 2;
// 圆角半径
private float cornerRadius = 0;
Paint mPaint = new Paint();
RectF rectF = new RectF();
Path mPath = new Path();
// 尖角宽(x轴上的长度)
private float triangleWidth = 0f;
// 尖角高(y轴上的长度)
private float triangleHeight = 0f;
// 偏移位置,xml中给定的
private float offset;
// 尖角距离左边或者上边的距离
private float triangleOffset = 0f;
private int paddingLeft;
private int paddingRight;
private int paddingTop;
private int paddingBottom;
public ChatLinearLayout(Context context) {
this(context, null);
}
public ChatLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ChatLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ChatLinearLayout, defStyleAttr, 0);
// 颜色
chatBackgroundColor = typedArray.getColor(R.styleable.ChatLinearLayout_chatBackgroundColor, Color.parseColor("#000000"));
// 尖角位置
triangleGravity = typedArray.getInt(R.styleable.ChatLinearLayout_triangleGravity, LEFT);
// 基准
base = typedArray.getInt(R.styleable.ChatLinearLayout_base, CENTER);
// 距离
offset = typedArray.getDimension(R.styleable.ChatLinearLayout_offset, Utils.dp2px(getContext(), 10));
// 圆角半径
cornerRadius = typedArray.getDimension(R.styleable.ChatLinearLayout_cornerRadius, Utils.dp2px(getContext(), 5));
// 尖角宽
triangleWidth = typedArray.getDimension(R.styleable.ChatLinearLayout_triangleWidth, Utils.dp2px(getContext(), 5));
// 尖角高
triangleHeight = typedArray.getDimension(R.styleable.ChatLinearLayout_triangleHeight, Utils.dp2px(getContext(), 8));
mPaint.setColor(chatBackgroundColor);
mPaint.setAntiAlias(true);
setWillNotDraw(false);
typedArray.recycle();
paddingLeft = getPaddingLeft();
paddingRight = getPaddingRight();
paddingTop = getPaddingTop();
paddingBottom = getPaddingBottom();
adjustPadding();
}
// 把尖角算入padding中,会调用invalidate()
private void adjustPadding(){
switch (triangleGravity){
case LEFT:
setPadding((int) (paddingLeft + triangleWidth), paddingTop, paddingRight, paddingBottom);
break;
case RIGHT:
setPadding(paddingLeft, paddingTop, (int) (paddingRight +triangleWidth), paddingBottom);
break;
case TOP:
setPadding(paddingLeft, (int) (paddingTop + triangleHeight), paddingRight, paddingBottom);
break;
case BOTTOM:
setPadding(paddingLeft, paddingTop, paddingRight, (int) (paddingBottom + triangleHeight));
break;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
float measuredWidth = getMeasuredWidth();
float measuredHeight = getMeasuredHeight();
if (base == CENTER) {
triangleOffset = -1;
} else if (base == BASE_END) {
// 与结束位置偏移
if (triangleGravity == LEFT || triangleGravity == RIGHT) {
// 尖角在左右
triangleOffset = measuredHeight - triangleHeight - offset;
} else {
// 在上下
triangleOffset = measuredWidth - triangleWidth - offset;
}
} else {
// 与开始位置偏移
triangleOffset = offset;
}
if (triangleOffset < 0) {
if (triangleGravity == LEFT || triangleGravity == RIGHT) {
triangleOffset = getMeasuredHeight() / 2 - triangleHeight / 2;
} else {
triangleOffset = getMeasuredWidth() / 2 - triangleWidth / 2;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float measuredWidth = getMeasuredWidth();
float measuredHeight = getMeasuredHeight();
mPath.reset();
switch (triangleGravity) {
case LEFT:
// 尖角在左边
// 画矩形
rectF.set(triangleWidth, 0f, measuredWidth, measuredHeight);
// 画尖角
mPath.moveTo(rectF.left, triangleOffset);//上面的点
mPath.lineTo(0, triangleOffset + triangleHeight / 2);// 中间的点
mPath.lineTo(rectF.left, triangleOffset + triangleHeight);// 下面的点
break;
case RIGHT:
// 尖角在右边
// 画矩形
rectF.set(0f, 0f, measuredWidth - triangleWidth, measuredHeight);
// 画尖角
mPath.moveTo(rectF.right, triangleOffset);//上面的点
mPath.lineTo(measuredWidth, triangleOffset + triangleHeight / 2);// 中间的点
mPath.lineTo(rectF.right, triangleOffset + triangleHeight);// 下面的点
break;
case TOP:
// 尖角在上边
// 画矩形
rectF.set(0f, triangleHeight, measuredWidth, measuredHeight);
// 画尖角
mPath.moveTo(triangleOffset + triangleWidth / 2, 0);//中间的点
mPath.lineTo(triangleOffset, triangleHeight);//左边的点
mPath.lineTo(triangleOffset + triangleWidth, triangleHeight);// 右边的点
break;
case BOTTOM:
// 尖角在下边
// 画矩形
rectF.set(0f, 0f, measuredWidth, measuredHeight - triangleHeight);
// 画尖角
mPath.moveTo(triangleOffset + triangleWidth / 2, measuredHeight);//中间的点
mPath.lineTo(triangleOffset, rectF.bottom);//左边的点
mPath.lineTo(triangleOffset + triangleWidth, rectF.bottom);// 右边的点
break;
}
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, mPaint);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
public void setTriangleGravity(int gravity) {
switch (gravity) {
case Gravity.LEFT:
triangleGravity = LEFT;
break;
case Gravity.RIGHT:
triangleGravity = RIGHT;
break;
case Gravity.TOP:
triangleGravity = TOP;
break;
case Gravity.BOTTOM:
triangleGravity = BOTTOM;
break;
default:
triangleGravity = LEFT;
break;
}
adjustPadding();
}
}
attrs里
<declare-styleable name="ChatLinearLayout">
<attr name="triangleGravity">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
<enum name="top" value="2"/>
<enum name="bottom" value="3"/>
</attr>
<attr name="chatBackgroundColor" format="color"/>
<!-- 尖角位置基准 值为center的时候,offset不起作用-->
<attr name="base">
<enum name="start" value="0"/>
<enum name="end" value="1"/>
<enum name="center" value="2"/>
</attr>
<attr name="triangleWidth" format="dimension"/>
<attr name="triangleHeight" format="dimension"/>
<!-- 尖角位置 ,与start 或 end的距离-->
<attr name="offset" format="dimension"/>
<!-- 圆角矩形的圆角半径-->
<attr name="cornerRadius" format="dimension"/>
</declare-styleable>
使用举例
<com.zhulin.android.ejiaoyan.view.ChatLinearLayout
android:id="@+id/root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
app:chatBackgroundColor="@color/main_color"
app:base="end"
app:triangleGravity="top"
app:offset="20dp"
app:triangleWidth="15dp"
app:triangleHeight="10dp"
app:cornerRadius="5dp"
android:orientation="vertical" >
</com.zhulin.android.ejiaoyan.view.ChatLinearLayout>
效果

网友评论