简介
自定义drawable,带箭头对话框背景,三角形+矩形组合。应用于对话框背景、提示语背景等。
可设置箭头显示方向、箭头大小、箭头导圆角尺寸、矩形尺寸、矩形导圆角尺寸、背景颜色、drawable padding值(影响宿主控件padding)。
希望能给大家带来方便。
一、效果图
示例代码:
ArrowsDrawable drawable = new ArrowsDrawable(ArrowsDrawable.BOTTOM, ConvertUtils.dp2px(8));
drawable.setArrowsRadius(ConvertUtils.dp2px(3));
drawable.setArrowsHeight(ConvertUtils.dp2px(8));
drawable.setArrowsWidth(ConvertUtils.dp2px(12));
drawable.setPadding(ConvertUtils.dp2px(10));
drawable.setColor(Color.DKGRAY);
textView.setBackground(drawable);
二、API详解
构造方法
//箭头方向,箭头高度
public ArrowsDrawable(@Position int arrowsPosition, float arrowsHeight)
设置drawable padding
drawable设置padding之后,会影响使用该drawable控件的padding。例如示例中drawable设置了padding,即使用者TextView设置了相同的padding值,这是drawable的特性。另外,该api将箭头高度加到对应的padding值中了,比如箭头方向朝上,drawable设置4个padding均为10dp, 那么drawable paddingTop的值 = 10dp + 箭头高度值,其余均是10dp。
public void setPadding(int p)
public void setPadding(int left, int top, int right, int bottom)
设置箭头方向
LEFT, RIGHT, TOP, BOTTOM
public void setArrowsPosition(@Position int arrowsPosition)
设置箭头圆角
设置箭头突出角的圆角尺寸,给该角导圆角
public void setArrowsRadius(float arrowsRadius)
其它API
//设置箭头高度
drawable.setArrowsHeight(ConvertUtils.dp2px(8));
//设置箭头宽度,默认是箭头高度的2倍
drawable.setArrowsWidth(ConvertUtils.dp2px(12));
//设置背景色
drawable.setColor(Color.DKGRAY);
扩展
如果 drawable需要设置margin效果, 可以通过InsetDrawable实现。
InsetDrawable insetDrawable = new InsetDrawable(arrowsDrawable, padding);
tv.setBackground(insetDrawable);
三、源码
package com.ttkx.deviceinfo.bkchart;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.GradientDrawable;
import android.view.Gravity;
import com.blankj.utilcode.util.ConvertUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
/**
* 带箭头的Drawable
* <p>
* Created by liuyu
* on 2021/2/4
*/
public class ArrowsDrawable extends GradientDrawable {
//箭头显示位置
public static final int LEFT = Gravity.LEFT;
public static final int TOP = Gravity.TOP;
public static final int RIGHT = Gravity.RIGHT;
public static final int BOTTOM = Gravity.BOTTOM;
public static final int CENTER = Gravity.CENTER;
@IntDef({LEFT, RIGHT, TOP, BOTTOM})
@Retention(RetentionPolicy.SOURCE)
public @interface Position {
}
private Paint mPaint;
private int arrowsPosition = BOTTOM;
private float arrowsRadius = ConvertUtils.dp2px(2);
private float arrowsHeight = ConvertUtils.dp2px(5);
private float arrowsWidth;
private float mRadius = ConvertUtils.dp2px(8);
private float arrowsPadding = mRadius + ConvertUtils.dp2px(10);//箭头padding
private Path mPath;
public ArrowsDrawable(@Position int arrowsPosition, float arrowsHeight) {
this.arrowsPosition = arrowsPosition;
this.arrowsHeight = arrowsHeight;
initPaint();
}
private Rect mPadding;
@Override
public boolean getPadding(Rect padding) {
if (mPadding != null) {
padding.set(mPadding);
return true;
} else {
return super.getPadding(padding);
}
}
public void setPadding(int p) {
setPadding(p, p, p, p);
}
/**
* 设置drawable padding
*
* @param left
* @param top
* @param right
* @param bottom
*/
public void setPadding(int left, int top, int right, int bottom) {
if (mPadding == null) {
mPadding = new Rect();
}
switch (arrowsPosition) {
case LEFT:
left += arrowsHeight;
break;
case TOP:
top += arrowsHeight;
break;
case RIGHT:
right += arrowsHeight;
break;
default:
bottom += arrowsHeight;
break;
}
mPadding.set(left, top, right, bottom);
invalidateSelf();
//super.setPadding(left, top, right, bottom);api=29,10.0
}
/**
* 设置箭头方向
*
* LEFT, RIGHT, TOP, BOTTOM
* @param arrowsPosition
*/
public void setArrowsPosition(@Position int arrowsPosition) {
this.arrowsPosition = arrowsPosition;
invalidateSelf();
}
/**
* 设置箭头圆角
*
* @param arrowsRadius
*/
public void setArrowsRadius(float arrowsRadius) {
this.arrowsRadius = arrowsRadius;
invalidateSelf();
}
/**
* 设置箭头高度
*
* @param arrowsHeight
*/
public void setArrowsHeight(float arrowsHeight) {
this.arrowsHeight = arrowsHeight;
invalidateSelf();
}
/**
* 设置箭头宽度
*
* @param arrowsWidth
*/
public void setArrowsWidth(float arrowsWidth) {
this.arrowsWidth = arrowsWidth;
}
/**
* 设置箭头离矩形边的距离(参照:矩形的左边或上边)
*
* @param arrowsPadding
*/
public void setArrowsPadding(float arrowsPadding) {
this.arrowsPadding = arrowsPadding;
invalidateSelf();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(1);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPath = new Path();
}
@Override
protected void onBoundsChange(Rect r) {
super.onBoundsChange(r);
int width = r.width();
int height = r.height();
float aw = this.arrowsWidth;
if (aw <= 0) {
aw = arrowsHeight * 2;//默认情况下箭头宽度是高度的2倍
}
float ar = aw / 2;
//三个点的坐标和矩形
float px1 = r.left + arrowsPadding;
float py1 = r.top + height - this.arrowsHeight;
float px2 = px1 + aw;
float py2 = py1;
float px3 = px1 + ar;
float py3 = py1 + this.arrowsHeight;
//矩形起始点坐标
float sx = r.left;
float sy = r.top;
//矩形宽高度
float rectWidth = width;
float rectHeight = height - this.arrowsHeight;
switch (arrowsPosition) {
case LEFT:
px1 = r.left + this.arrowsHeight;
py1 = r.top + arrowsPadding + this.arrowsHeight;
px2 = px1;
py2 = py1 + aw;
px3 = px1 - this.arrowsHeight;
py3 = py1 + ar;
sx = px1;
sy = r.top;
rectWidth = width - this.arrowsHeight;
rectHeight = height;
break;
case TOP:
px1 = r.left + arrowsPadding;
py1 = r.top + this.arrowsHeight;
px2 = px1 + aw;
py2 = py1;
px3 = px1 + ar;
py3 = py1 - this.arrowsHeight;
sx = r.left;
sy = r.top + this.arrowsHeight;
rectWidth = width;
rectHeight = height - this.arrowsHeight;
break;
case RIGHT:
px1 = r.left + width - this.arrowsHeight;
py1 = r.top + arrowsPadding;
px2 = px1;
py2 = py1 + aw;
px3 = px1 + this.arrowsHeight;
py3 = py1 + ar;
sx = r.left;
sy = r.top;
rectWidth = width - this.arrowsHeight;
rectHeight = height;
break;
}
mPath.reset();
addTrianglePath(mPath, arrowsRadius, px1, py1, px2, py2, px3, py3);
RectF rectF = new RectF(sx, sy, sx + rectWidth, sy + rectHeight);
mPath.addRoundRect(rectF, mRadius, mRadius, Path.Direction.CW);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawPath(mPath, mPaint);
}
/**
* 添加三角形path
*
* @param path 路径
* @param radiu 三角形突出角圆角半径
* @param x1 与内容框连接1点X坐标 (起始点)
* @param y1 与内容框连接1点y坐标
* @param x2 与内容框连接2点X坐标
* @param y2 与内容框连接2点Xy坐标
* @param x3 三角形突出角x坐标
* @param y3 三角形突出角y坐标
*/
private void addTrianglePath(Path path, float radiu, float x1, float y1, float x2, float y2, float x3, float y3) {
//起始点
path.moveTo(x1, y1);
path.lineTo(x2, y2);
float[] ps2 = getCornerCoordinateEnd(radiu, x2, y2, x3, y3);
path.lineTo(ps2[0], ps2[1]);
float[] ps3 = getCornerCoordinateStart(radiu, x3, y3, x1, y1);
//绘制圆角
path.cubicTo(ps2[0], ps2[1], x3, y3, ps3[0], ps3[1]);
//关闭三角形
path.close();
}
/**
* 获取圆角坐标(圆角半径长度相对于起始点x1,y1)
*
* @param radiu 圆角半径
* @param x1 起始点x坐标
* @param y1 起始点y坐标
* @param x2 结束点x坐标
* @param y2 结束点y坐标
* @return
*/
private float[] getCornerCoordinateStart(float radiu, float x1, float y1, float x2, float y2) {
double degree = MathHelper.getDegree(x1, y1, x2, y2);
double lx = MathHelper.getRightSideFromDegree(degree, radiu);
double ly = MathHelper.getLeftSideFromDegree(degree, radiu);
double v2 = x1 + lx;
double v3 = y1 + ly;
return new float[]{(float) v2, (float) v3};
}
/**
* 获取圆角坐标(圆角半径长度相对于结束点x2,y2)
*
* @param radiu 圆角半径
* @param x1 起始点x坐标
* @param y1 起始点y坐标
* @param x2 结束点x坐标
* @param y2 结束点y坐标
* @return
*/
private float[] getCornerCoordinateEnd(float radiu, float x1, float y1, float x2, float y2) {
double degree = MathHelper.getDegree(x1, y1, x2, y2);
double lx = MathHelper.getRightSideFromDegree(degree, radiu);
double ly = MathHelper.getLeftSideFromDegree(degree, radiu);
double v2 = x2 - lx;
double v3 = y2 - ly;
return new float[]{(float) v2, (float) v3};
}
@Override
public void setColor(int color) {
if (mPaint != null) {
mPaint.setColor(color);
}
invalidateSelf();
}
/**
* 设置矩形的圆角
*
* @param radius
*/
@Override
public void setCornerRadius(float radius) {
mRadius = radius;
}
}