最近公司需求做一个气泡弹框,又不想让UI给切割.9图,咋办呢,自己动手呗,这么好的学习机会
首先看一下最终的效果图,激励一下(Android小白第一个作品,不喜勿喷,谢谢)
文章后有源码,可以依据源码一阅读。
讲一下解决思路:
1.根据字串的长度获取气泡中间矩形的宽度
2.根据矩形的高的一半作为半径,画圆弧
3.设置气泡底部小三角形的底边长,画一个等腰直角三角形
4.绘制文字,由于数字需要不一样的颜色,这里我将“距离你”,“米”,还有数字分别获取了宽度(绘制的时候起点坐标需要设置)
接下来讲一下每一个步骤的主要技术
准备工作:控件所在xml代码,宽和高均为wrap_content,这里注意,外层LinearLayout背景需要设置成透明#00000000,
画笔模式设置为填充paint.setStyle(Paint.Style.FILL_AND_STROKE);
1.获取字串宽度使用paint的measureText方法,获取字串总长度textWidth,并在onMeasure方法中调用setMeasureDimension方法设置控件需要占据的长和宽。(不调用setMeasureDimension方法,将无法正常显示控件)
2.画圆弧,调用方法path.arcTo(rectF oval, float startAngle,float sweepAngle,boolean forceMoveTo)
参数介绍:1.圆所在矩形 2.开始角度 3.弧的角度 4.是否将上次移动的终点与弧起点连接
3.这一步其实没什么好讲的,计算好点的坐标,直接path.lineTo即可。
4.这一步计算text的长度,进行绘制,这个难点不大。但是这里遇到了一个问题.
即由字串起点x=width/2-textWdith/2,y=height/2-fontSize/2画出来的字串,始终没有完全居中。
search之后发现,canvas.drawText(text, x, y, paint);中的y,其实是baseline在屏幕上的位置,而baseline却不是在字串的中间,这里需要经过处理
到这里,基本是已经完成了气泡的绘制了,但是看上去太平面了,所以我们给它加上阴影效果
this.setLayerType(View.LAYER_TYPE_SOFTWARE,paint);
paint.setShadowLayer(10F,1F,1F,Color.GRAY);
两行代码,so easy!!!
当然要显示出阴影效果,那么必须在你绘制的图形边缘有一部分的空间,不然无法显示。
最后给出我的源码:
public class BubbleViewextends View {
Paint paint;
int textWidth;
int rectHeight =80; //矩形高
int circleR = rectHeight/2;//圆弧半径
String textPre ="距离你";
int textPreWidth;
String textEnd ="米";
int textNumWidth;
String textNum ="";
String text;
private int fontSize =30;//字体大小
private int padding =20;//内容边距
private int bottomLine =26;//等腰直角三角形底边长
int width;//view总宽
int height = padding + rectHeight + bottomLine/2 + padding; //view总高
private Path path;
private String distance;
public BubbleView(Context context) {
super(context);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
resetPaint();
this.setLayerType(View.LAYER_TYPE_SOFTWARE,paint);
paint.setShadowLayer(10F,1F,1F,Color.GRAY);
text = textPre + textNum + textEnd;
textWidth = (int) paint.measureText(text);
textPreWidth = (int) paint.measureText(textPre);
textNumWidth = (int) paint.measureText(textNum);
path =new Path();
}
public String getDistance(){
return distance;
}
public void setText(String textNum){
this.textNum = textNum;
init();
invalidate();
}
public void resetPaint() {
//初始化画笔
paint =new Paint();
//设置抗锯齿
paint.setAntiAlias(true);
//设置填充
paint.setStyle(Paint.Style.FILL_AND_STROKE);
//设置防抖动
paint.setDither(true);
paint.setTextSize(fontSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
init();
drawRect(canvas);
}
private void drawRect(Canvas canvas) {
paint.setColor(Color.parseColor("#ffffff"));
RectF rectF =new RectF(padding, padding, padding + circleR*2, padding + circleR*2);
path.arcTo(rectF, 270, -180,false);
path.lineTo(width/2 - bottomLine/2, padding + circleR*2);
path.lineTo(width/2, padding + circleR*2 + bottomLine/2);
path.lineTo(width/2 + bottomLine/2, padding + circleR*2);
path.lineTo(padding + circleR + textWidth, padding + circleR *2);
RectF rectF1 =new RectF(padding + circleR + textWidth - circleR, padding, padding + circleR + textWidth + circleR, padding + circleR*2);
path.arcTo(rectF1, 90, -180,false);
path.lineTo(padding + circleR, padding);
canvas.drawPath(path, paint);
resetPaint();
paint.setColor(Color.parseColor("#333333"));
Rect bounds =new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (height - bottomLine/2 - fontMetrics.descent + fontMetrics.ascent) /2 - fontMetrics.ascent;
canvas.drawText(textPre, width/2-textWidth/2, baseline, paint);
canvas.drawText(textEnd, width/2-textWidth/2 + textPreWidth + textNumWidth, baseline, paint);
resetPaint();
paint.setColor(Color.parseColor("#ff9500"));
canvas.drawText(textNum, width/2-textWidth/2 + textPreWidth, baseline, paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = padding + circleR + textWidth + circleR + padding;
setMeasuredDimension(width, height);
}
}