目录
效果展示
代码分析
demo中的效果是手指拖动红色方块,文字自动排版,达到动态环绕的效果。
这里是通过继承View复写onDraw方法实现的。
package com.example.allen.flowtext;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class FlowTextView extends View {
private String text = "";
private Paint paint;
private Paint imgPaint;
//图片宽高
private static int IMG_WIDTH = 170;
private static int IMG_HEIGHT = 170;
//文字高度,和文字宽度(所有文字的)
private int textHeight, textWidth;
//控件宽高(这里是重点表示排版的算法逻辑,onMeasure方法就没做定义,直接写死以充满整个父控件)
private int width, height;
//图片的矩形区域
private Rect imgRect;
//图片的左下角坐标(控件坐标,左上角为0,0)
private int rectX, rectY;
//单个文字的宽高
private float wordWidth, wordHeight;
//当前绘制到的位置坐标
private float currentX, currentY;
public FlowTextView(Context context) {
super(context);
}
//初始化,文字样式可以在此设置
public FlowTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
imgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setTextSize(50f);
imgPaint.setColor(Color.RED);
}
public FlowTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);//主意布局中不可以为at_most
width = getMeasuredWidth();
height = getMeasuredHeight();
measureTextHeight();
imgRect = new Rect(rectX, rectY, rectX + IMG_WIDTH, rectY + IMG_HEIGHT);
currentX = 0;
currentY = wordHeight;
}
public void setText(String text) {
this.text = text;
rectX = 200;
rectY = 200;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentX = 0;
currentY = wordHeight;
for (int i = 0; i < text.length(); i++) {
String str = text.substring(i, i + 1);
if (currentX > width - wordWidth) {//换行
currentY += wordHeight;
currentX = 0;
}
if (currentX <= imgRect.left && currentY < imgRect.bottom && currentY >= imgRect.top) {
if (currentX + wordWidth > imgRect.left) {
currentX = imgRect.right;
}
}
if (currentY - wordHeight < imgRect.bottom && currentX < imgRect.right && currentX + wordWidth > imgRect.left) {
if (currentY > imgRect.top) {
currentY = imgRect.bottom + wordHeight;
}
}
canvas.drawText(str, currentX, currentY, paint);
currentX += wordWidth;
}
RectF rectF = new RectF(imgRect);
canvas.drawRoundRect(rectF, 40f, 40f, imgPaint);
}
private boolean isDrag;
private int offsetX, offsetY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
if (imgRect.contains(x, y)) {
isDrag = true;
offsetX = x - rectX;
offsetY = y - rectY;
}
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getX();
int moveY = (int) event.getY();
if (isDrag) {
rectX = moveX - offsetX;
rectY = moveY - offsetY;
if (rectX < 0)
rectX = 0;
if (rectY < 0) {
rectY = 0;
}
if (rectX > width - IMG_WIDTH) {
rectX = width - IMG_WIDTH;
}
if (rectY > height - IMG_HEIGHT) {
rectY = height - IMG_HEIGHT;
}
}
imgRect.left = rectX;
imgRect.top = rectY;
imgRect.right = imgRect.left + IMG_WIDTH;
imgRect.bottom = imgRect.top + IMG_HEIGHT;
invalidate();
break;
case MotionEvent.ACTION_UP:
isDrag = false;
offsetX = 0;
offsetY = 0;
break;
}
return true;
}
private void measureTextHeight() {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
textWidth = rect.width();//文字宽
textHeight = rect.height();//文字高
//单个文字宽高
wordWidth = textWidth * 1.0f / text.length();
wordHeight = textHeight * 1.0f;
}
}
重点是onDraw方法,绘制是以单个文字为单元,每次绘制时判断图片位置是否冲突。另外还多了个换行逻辑。