package com.com.mr_wrong.Image.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PointF; import android.graphics.Rect; import android.support.v4.view.MotionEventCompat; import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.widget.ImageView; import com.com.mr_wrong.Image.model.BubblePropertyModel; import com.com.mr_wrong.Image.utils.DensityUtils; import com.example.mr_wrong.androidstudioproject.R; /** * Created by Abner on 15/6/7. * QQ 230877476 * Email nimengbo@gmail.com */ public class BubbleTextView extends ImageView { private static final String TAG = "BubbleTextView"; private Bitmap deleteBitmap; private Bitmap flipVBitmap; private Bitmap topBitmap; private Bitmap resizeBitmap; private Bitmap mBitmap; private Bitmap originBitmap; private Rect dst_delete; private Rect dst_resize; private Rect dst_flipV; private Rect dst_top; private int deleteBitmapWidth; private int deleteBitmapHeight; private int resizeBitmapWidth; private int resizeBitmapHeight; private int flipVBitmapWidth; private int flipVBitmapHeight; //置顶 private int topBitmapWidth; private int topBitmapHeight; private Paint localPaint; private int mScreenwidth, mScreenHeight; private static final float BITMAP_SCALE = 0.7f; private PointF mid = new PointF(); private OperationListener operationListener; private float lastRotateDegree; //是否是第二根手指放下 private boolean isPointerDown = false; //手指移动距离必须超过这个数值 private final float pointerLimitDis = 20f; private final float pointerZoomCoeff = 0.09f; private final float moveLimitDis = 0.5f; /** * 对角线的长度 */ private float lastLength; private boolean isInResize = false; private Matrix matrix = new Matrix(); /** * 是否在四条线内部 */ private boolean isInSide; private float lastX, lastY; /** * 是否在编辑模式 */ private boolean isInEdit = true; private float MIN_SCALE = 0.5f; private float MAX_SCALE = 1.5f; private double halfDiagonalLength; private float oringinWidth = 0; private DisplayMetrics dm; /** * 文字部分 */ private final String defaultStr; //显示的字符串 private String mStr = ""; //字号默认16sp private final float mDefultSize = 16; private float mFontSize = 16; //最大最小字号 private final float mMaxFontSize = 25; private final float mMinFontSize = 14; //字离旁边的距离 private final float mDefaultMargin = 20; private float mMargin = 20; //绘制文字的画笔 private TextPaint mFontPaint; private Canvas canvasText; private Paint.FontMetrics fm; //由于系统基于字体的底部来绘制文本,所有需要加上字体的高度。 private float baseline; boolean isInit = true; //双指缩放时的初始距离 private float oldDis; //是否按下 private boolean isDown = false; //是否移动 private boolean isMove = false; //是否抬起手 private boolean isUp = false; //是否在顶部 private boolean isTop = true; private boolean isInBitmap; private final int fontColor; private final long bubbleId; public BubbleTextView(Context context, AttributeSet attrs) { super(context, attrs); defaultStr = getContext().getString(R.string.double_click_input_text); this.fontColor = Color.BLACK; bubbleId = 0; init(); } public BubbleTextView(Context context) { super(context); defaultStr = getContext().getString(R.string.double_click_input_text); this.fontColor = Color.BLACK; bubbleId = 0; init(); } public BubbleTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); defaultStr = getContext().getString(R.string.double_click_input_text); this.fontColor = Color.BLACK; bubbleId = 0; init(); } /** * @param context * @param fontColor * @param bubbleId some fuck id */ public BubbleTextView(Context context, int fontColor, long bubbleId) { super(context); defaultStr = getContext().getString(R.string.double_click_input_text); this.fontColor = fontColor; this.bubbleId = bubbleId; init(); } private void init() { dm = getResources().getDisplayMetrics(); dst_delete = new Rect(); dst_resize = new Rect(); dst_flipV = new Rect(); dst_top = new Rect(); localPaint = new Paint(); localPaint.setColor(getResources().getColor(R.color.red_e73a3d)); localPaint.setAntiAlias(true); localPaint.setDither(true); localPaint.setStyle(Paint.Style.STROKE); localPaint.setStrokeWidth(2.0f); mScreenwidth = dm.widthPixels; mScreenHeight = dm.heightPixels; mFontSize = mDefultSize; mFontPaint = new TextPaint(); mFontPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mFontSize, dm)); mFontPaint.setColor(fontColor); mFontPaint.setTextAlign(Paint.Align.CENTER); mFontPaint.setAntiAlias(true); fm = mFontPaint.getFontMetrics(); baseline = fm.descent - fm.ascent; isInit = true; mStr = defaultStr; } @Override protected void onDraw(Canvas canvas) { if (mBitmap != null) { float[] arrayOfFloat = new float[9]; matrix.getValues(arrayOfFloat); float f1 = 0.0F * arrayOfFloat[0] + 0.0F * arrayOfFloat[1] + arrayOfFloat[2]; float f2 = 0.0F * arrayOfFloat[3] + 0.0F * arrayOfFloat[4] + arrayOfFloat[5]; float f3 = arrayOfFloat[0] * this.mBitmap.getWidth() + 0.0F * arrayOfFloat[1] + arrayOfFloat[2]; float f4 = arrayOfFloat[3] * this.mBitmap.getWidth() + 0.0F * arrayOfFloat[4] + arrayOfFloat[5]; float f5 = 0.0F * arrayOfFloat[0] + arrayOfFloat[1] * this.mBitmap.getHeight() + arrayOfFloat[2]; float f6 = 0.0F * arrayOfFloat[3] + arrayOfFloat[4] * this.mBitmap.getHeight() + arrayOfFloat[5]; float f7 = arrayOfFloat[0] * this.mBitmap.getWidth() + arrayOfFloat[1] * this.mBitmap.getHeight() + arrayOfFloat[2]; float f8 = arrayOfFloat[3] * this.mBitmap.getWidth() + arrayOfFloat[4] * this.mBitmap.getHeight() + arrayOfFloat[5]; canvas.save(); //先往文字上绘图 mBitmap = originBitmap.copy(Bitmap.Config.ARGB_8888, true); canvasText.setBitmap(mBitmap); canvasText.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); float left = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, dm); float scalex = arrayOfFloat[Matrix.MSCALE_X]; float skewy = arrayOfFloat[Matrix.MSKEW_Y]; float rScale = (float) Math.sqrt(scalex * scalex + skewy * skewy); float size = rScale * 0.75f * mDefultSize; if (size > mMaxFontSize) { mFontSize = mMaxFontSize; } else if (size < mMinFontSize) { mFontSize = mMinFontSize; } else { mFontSize = size; } mFontPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mFontSize, dm)); String[] texts = autoSplit(mStr, mFontPaint, mBitmap.getWidth() - left * 3); float height = (texts.length * (baseline + fm.leading) + baseline); float top = (mBitmap.getHeight() - height) / 2; //基于底线开始画的 top += baseline; for (String text : texts) { if (TextUtils.isEmpty(text)) { continue; } canvasText.drawText(text, mBitmap.getWidth() / 2, top, mFontPaint); //坐标以控件左上角为原点 top += baseline + fm.leading; //添加字体行间距 } canvas.drawBitmap(mBitmap, matrix, null); //删除在右上角 dst_delete.left = (int) (f3 - deleteBitmapWidth / 2); dst_delete.right = (int) (f3 + deleteBitmapWidth / 2); dst_delete.top = (int) (f4 - deleteBitmapHeight / 2); dst_delete.bottom = (int) (f4 + deleteBitmapHeight / 2); //拉伸等操作在右下角 dst_resize.left = (int) (f7 - resizeBitmapWidth / 2); dst_resize.right = (int) (f7 + resizeBitmapWidth / 2); dst_resize.top = (int) (f8 - resizeBitmapHeight / 2); dst_resize.bottom = (int) (f8 + resizeBitmapHeight / 2); //置顶在左上角 dst_top.left = (int) (f1 - topBitmapWidth / 2); dst_top.right = (int) (f1 + topBitmapWidth / 2); dst_top.top = (int) (f2 - topBitmapHeight / 2); dst_top.bottom = (int) (f2 + topBitmapHeight / 2); //水平镜像在右下角 // dst_flipV.left = (int) (f5 - topBitmapWidth / 2); // dst_flipV.right = (int) (f5 + topBitmapWidth / 2); // dst_flipV.top = (int) (f6 - topBitmapHeight / 2); // dst_flipV.bottom = (int) (f6 + topBitmapHeight / 2); if (isInEdit) { canvas.drawLine(f1, f2, f3, f4, localPaint); canvas.drawLine(f3, f4, f7, f8, localPaint); canvas.drawLine(f5, f6, f7, f8, localPaint); canvas.drawLine(f5, f6, f1, f2, localPaint); canvas.drawBitmap(deleteBitmap, null, dst_delete, null); canvas.drawBitmap(resizeBitmap, null, dst_resize, null); // canvas.drawBitmap(flipVBitmap, null, dst_flipV, null); canvas.drawBitmap(topBitmap, null, dst_top, null); } canvas.restore(); } } public void setText(String text) { // if (TextUtils.isEmpty(text)) { // mStr = defaultStr; // mFontSize = mDefultSize; // mMargin = mDefaultMargin; // } else { mStr = text; // } invalidate(); } @Override public void setImageResource(int resId) { matrix.reset(); //使用拷贝 不然会对资源文件进行引用而修改 setBitmap(BitmapFactory.decodeResource(getResources(), resId)); } public void setImageResource(int resId, BubblePropertyModel model) { matrix.reset(); //使用拷贝 不然会对资源文件进行引用而修改 setBitmap(BitmapFactory.decodeResource(getResources(), resId), model); } public void setBitmap(Bitmap bitmap, BubblePropertyModel model) { mFontSize = mDefultSize; originBitmap = bitmap; mBitmap = originBitmap.copy(Bitmap.Config.ARGB_8888, true); canvasText = new Canvas(mBitmap); setDiagonalLength(); initBitmaps(); int w = mBitmap.getWidth(); int h = mBitmap.getHeight(); oringinWidth = w; mStr = model.getText(); float scale = model.getScaling() * mScreenwidth / mBitmap.getWidth(); if (scale > MAX_SCALE) { scale = MAX_SCALE; } else if (scale < MIN_SCALE) { scale = MIN_SCALE; } float degree = (float) Math.toDegrees(model.getDegree()); matrix.postRotate(-degree, w >> 1, h >> 1); matrix.postScale(scale, scale, w >> 1, h >> 1); float midX = model.getxLocation() * mScreenwidth; float midY = model.getyLocation() * mScreenwidth; float offset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 22, dm); midX = midX - (w * scale) / 2 - offset; midY = midY - (h * scale) / 2 - offset; matrix.postTranslate(midX, midY); invalidate(); } public void setBitmap(Bitmap bitmap) { mFontSize = mDefultSize; originBitmap = bitmap; mBitmap = originBitmap.copy(Bitmap.Config.ARGB_8888, true); canvasText = new Canvas(mBitmap); setDiagonalLength(); initBitmaps(); int w = mBitmap.getWidth(); int h = mBitmap.getHeight(); oringinWidth = w; float topbarHeight = DensityUtils.dip2px(getContext(), 50); //Y坐标为 (顶部操作栏+正方形图)/2 matrix.postTranslate(mScreenwidth / 2 - w / 2, (mScreenwidth) / 2 - h / 2); invalidate(); } private void setDiagonalLength() { halfDiagonalLength = Math.hypot(mBitmap.getWidth(), mBitmap.getHeight()) / 2; } private void initBitmaps() { float minWidth = mScreenwidth / 8; if (mBitmap.getWidth() < minWidth) { MIN_SCALE = 1f; } else { MIN_SCALE = 1.0f * minWidth / mBitmap.getWidth(); } if (mBitmap.getWidth() > mScreenwidth) { MAX_SCALE = 1; } else { MAX_SCALE = 1.0f * mScreenwidth / mBitmap.getWidth(); } topBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_top_enable); deleteBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_delete); flipVBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_flip); resizeBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_resize); deleteBitmapWidth = (int) (deleteBitmap.getWidth() * BITMAP_SCALE); deleteBitmapHeight = (int) (deleteBitmap.getHeight() * BITMAP_SCALE); resizeBitmapWidth = (int) (resizeBitmap.getWidth() * BITMAP_SCALE); resizeBitmapHeight = (int) (resizeBitmap.getHeight() * BITMAP_SCALE); flipVBitmapWidth = (int) (flipVBitmap.getWidth() * BITMAP_SCALE); flipVBitmapHeight = (int) (flipVBitmap.getHeight() * BITMAP_SCALE); topBitmapWidth = (int) (topBitmap.getWidth() * BITMAP_SCALE); topBitmapHeight = (int) (topBitmap.getHeight() * BITMAP_SCALE); } private long preClicktime; private final long doubleClickTimeLimit = 200; @Override public boolean onTouchEvent(MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); boolean handled = true; isInBitmap = false; switch (action) { case MotionEvent.ACTION_DOWN: if (isInButton(event, dst_delete)) { if (operationListener != null) { operationListener.onDeleteClick(); } isDown = false; } else if (isInResize(event)) { isInResize = true; lastRotateDegree = rotationToStartPoint(event); midPointToStartPoint(event); lastLength = diagonalLength(event); isDown = false; } else if (isInButton(event, dst_flipV)) { PointF localPointF = new PointF(); midDiagonalPoint(localPointF); matrix.postScale(-1.0F, 1.0F, localPointF.x, localPointF.y); isDown = false; invalidate(); } else if (isInButton(event, dst_top)) { //置顶 bringToFront(); if (operationListener != null) { operationListener.onTop(this); } isDown = false; } else if (isInBitmap(event)) { isInSide = true; lastX = event.getX(0); lastY = event.getY(0); isDown = true; isMove = false; isPointerDown = false; isUp = false; isInBitmap = true; long currentTime = System.currentTimeMillis(); Log.d(TAG, (currentTime - preClicktime) + ""); if (currentTime - preClicktime > doubleClickTimeLimit) { preClicktime = currentTime; } else { if (isInEdit && operationListener != null) { operationListener.onClick(this); } } } else { handled = false; } break; case MotionEvent.ACTION_POINTER_DOWN: if (spacing(event) > pointerLimitDis) { oldDis = spacing(event); isPointerDown = true; midPointToStartPoint(event); } else { isPointerDown = false; } isInSide = false; isInResize = false; break; case MotionEvent.ACTION_MOVE: //双指缩放 if (isPointerDown) { float scale; float disNew = spacing(event); if (disNew == 0 || disNew < pointerLimitDis) { scale = 1; } else { scale = disNew / oldDis; //缩放缓慢 scale = (scale - 1) * pointerZoomCoeff + 1; } float scaleTemp = (scale * Math.abs(dst_flipV.left - dst_resize.left)) / oringinWidth; if (((scaleTemp <= MIN_SCALE)) && scale < 1 || (scaleTemp >= MAX_SCALE) && scale > 1) { scale = 1; } else { lastLength = diagonalLength(event); } matrix.postScale(scale, scale, mid.x, mid.y); invalidate(); } else if (isInResize) { matrix.postRotate((rotationToStartPoint(event) - lastRotateDegree) * 2, mid.x, mid.y); lastRotateDegree = rotationToStartPoint(event); float scale = diagonalLength(event) / lastLength; if (((diagonalLength(event) / halfDiagonalLength <= MIN_SCALE)) && scale < 1 || (diagonalLength(event) / halfDiagonalLength >= MAX_SCALE) && scale > 1) { scale = 1; if (!isInResize(event)) { isInResize = false; } } else { lastLength = diagonalLength(event); } matrix.postScale(scale, scale, mid.x, mid.y); invalidate(); } else if (isInSide) { //TODO 移动区域判断 不能超出屏幕 float x = event.getX(0); float y = event.getY(0); //判断手指抖动距离 加上isMove判断 只要移动过 都是true if (!isMove && Math.abs(x - lastX) < moveLimitDis && Math.abs(y - lastY) < moveLimitDis) { isMove = false; } else { isMove = true; } matrix.postTranslate(x - lastX, y - lastY); lastX = x; lastY = y; invalidate(); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isInResize = false; isInSide = false; isPointerDown = false; isUp = true; break; } if (handled && operationListener != null) { operationListener.onEdit(this); } // //判断是不是做了点击动作 必须在编辑状态 且在图片内 并且是双击 // if (isDoubleClick && isDown && !isPointerDown && !isMove && isUp && isInBitmap && isInEdit && operationListener != null) { // operationListener.onClick(this); // } return handled; } public BubblePropertyModel calculate(BubblePropertyModel model) { float[] v = new float[9]; matrix.getValues(v); // translation is simple float tx = v[Matrix.MTRANS_X]; float ty = v[Matrix.MTRANS_Y]; Log.d(TAG, "tx : " + tx + " ty : " + ty); // calculate real scale float scalex = v[Matrix.MSCALE_X]; float skewy = v[Matrix.MSKEW_Y]; float rScale = (float) Math.sqrt(scalex * scalex + skewy * skewy); Log.d(TAG, "rScale : " + rScale); // calculate the degree of rotation float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI)); Log.d(TAG, "rAngle : " + rAngle); float minX = (dst_top.centerX() + dst_resize.centerX()) / 2; float minY = (dst_top.centerY() + dst_resize.centerY()) / 2; Log.d(TAG, "midX : " + minX + " midY : " + minY); model.setDegree((float) Math.toRadians(rAngle)); model.setBubbleId(bubbleId); //TODO 占屏幕百分比 float precentWidth = (mBitmap.getWidth() * rScale) / mScreenwidth; model.setScaling(precentWidth); Log.d(TAG, " x " + (minX / mScreenwidth) + " y " + (minY / mScreenwidth)); model.setxLocation(minX / mScreenwidth); model.setyLocation(minY / mScreenwidth); model.setText(mStr); return model; } /** * 是否在四条线内部 * * @return */ private boolean isInBitmap(MotionEvent event) { float[] arrayOfFloat1 = new float[9]; this.matrix.getValues(arrayOfFloat1); //左上角 float f1 = 0.0F * arrayOfFloat1[0] + 0.0F * arrayOfFloat1[1] + arrayOfFloat1[2]; float f2 = 0.0F * arrayOfFloat1[3] + 0.0F * arrayOfFloat1[4] + arrayOfFloat1[5]; //右上角 float f3 = arrayOfFloat1[0] * this.mBitmap.getWidth() + 0.0F * arrayOfFloat1[1] + arrayOfFloat1[2]; float f4 = arrayOfFloat1[3] * this.mBitmap.getWidth() + 0.0F * arrayOfFloat1[4] + arrayOfFloat1[5]; //左下角 float f5 = 0.0F * arrayOfFloat1[0] + arrayOfFloat1[1] * this.mBitmap.getHeight() + arrayOfFloat1[2]; float f6 = 0.0F * arrayOfFloat1[3] + arrayOfFloat1[4] * this.mBitmap.getHeight() + arrayOfFloat1[5]; //右下角 float f7 = arrayOfFloat1[0] * this.mBitmap.getWidth() + arrayOfFloat1[1] * this.mBitmap.getHeight() + arrayOfFloat1[2]; float f8 = arrayOfFloat1[3] * this.mBitmap.getWidth() + arrayOfFloat1[4] * this.mBitmap.getHeight() + arrayOfFloat1[5]; float[] arrayOfFloat2 = new float[4]; float[] arrayOfFloat3 = new float[4]; //确定X方向的范围 arrayOfFloat2[0] = f1;//左上的左 arrayOfFloat2[1] = f3;//右上的右 arrayOfFloat2[2] = f7;//右下的右 arrayOfFloat2[3] = f5;//左下的左 //确定Y方向的范围 arrayOfFloat3[0] = f2;//左上的上 arrayOfFloat3[1] = f4;//右上的上 arrayOfFloat3[2] = f8; arrayOfFloat3[3] = f6; return pointInRect(arrayOfFloat2, arrayOfFloat3, event.getX(0), event.getY(0)); } /** * 判断点是否在一个矩形内部 * * @param xRange * @param yRange * @param x * @param y * @return */ private boolean pointInRect(float[] xRange, float[] yRange, float x, float y) { //四条边的长度 double a1 = Math.hypot(xRange[0] - xRange[1], yRange[0] - yRange[1]); double a2 = Math.hypot(xRange[1] - xRange[2], yRange[1] - yRange[2]); double a3 = Math.hypot(xRange[3] - xRange[2], yRange[3] - yRange[2]); double a4 = Math.hypot(xRange[0] - xRange[3], yRange[0] - yRange[3]); //待检测点到四个点的距离 double b1 = Math.hypot(x - xRange[0], y - yRange[0]); double b2 = Math.hypot(x - xRange[1], y - yRange[1]); double b3 = Math.hypot(x - xRange[2], y - yRange[2]); double b4 = Math.hypot(x - xRange[3], y - yRange[3]); double u1 = (a1 + b1 + b2) / 2; double u2 = (a2 + b2 + b3) / 2; double u3 = (a3 + b3 + b4) / 2; double u4 = (a4 + b4 + b1) / 2; //矩形的面积 double s = a1 * a2; double ss = Math.sqrt(u1 * (u1 - a1) * (u1 - b1) * (u1 - b2)) + Math.sqrt(u2 * (u2 - a2) * (u2 - b2) * (u2 - b3)) + Math.sqrt(u3 * (u3 - a3) * (u3 - b3) * (u3 - b4)) + Math.sqrt(u4 * (u4 - a4) * (u4 - b4) * (u4 - b1)); return Math.abs(s - ss) < 0.5; } private boolean isInButton(MotionEvent event, Rect rect) { int left = rect.left; int right = rect.right; int top = rect.top; int bottom = rect.bottom; return event.getX(0) >= left && event.getX(0) <= right && event.getY(0) >= top && event.getY(0) <= bottom; } private boolean isInResize(MotionEvent event) { int left = -20 + this.dst_resize.left; int top = -20 + this.dst_resize.top; int right = 20 + this.dst_resize.right; int bottom = 20 + this.dst_resize.bottom; return event.getX(0) >= left && event.getX(0) <= right && event.getY(0) >= top && event.getY(0) <= bottom; } private void midPointToStartPoint(MotionEvent event) { float[] arrayOfFloat = new float[9]; matrix.getValues(arrayOfFloat); float f1 = 0.0f * arrayOfFloat[0] + 0.0f * arrayOfFloat[1] + arrayOfFloat[2]; float f2 = 0.0f * arrayOfFloat[3] + 0.0f * arrayOfFloat[4] + arrayOfFloat[5]; float f3 = f1 + event.getX(0); float f4 = f2 + event.getY(0); mid.set(f3 / 2, f4 / 2); } private void midDiagonalPoint(PointF paramPointF) { float[] arrayOfFloat = new float[9]; this.matrix.getValues(arrayOfFloat); float f1 = 0.0F * arrayOfFloat[0] + 0.0F * arrayOfFloat[1] + arrayOfFloat[2]; float f2 = 0.0F * arrayOfFloat[3] + 0.0F * arrayOfFloat[4] + arrayOfFloat[5]; float f3 = arrayOfFloat[0] * this.mBitmap.getWidth() + arrayOfFloat[1] * this.mBitmap.getHeight() + arrayOfFloat[2]; float f4 = arrayOfFloat[3] * this.mBitmap.getWidth() + arrayOfFloat[4] * this.mBitmap.getHeight() + arrayOfFloat[5]; float f5 = f1 + f3; float f6 = f2 + f4; paramPointF.set(f5 / 2.0F, f6 / 2.0F); } /** * 在滑动过车中X,Y是不会改变的,这里减Y,减X,其实是相当于把X,Y当做原点 * * @param event * @return */ private float rotationToStartPoint(MotionEvent event) { float[] arrayOfFloat = new float[9]; matrix.getValues(arrayOfFloat); float x = 0.0f * arrayOfFloat[0] + 0.0f * arrayOfFloat[1] + arrayOfFloat[2]; float y = 0.0f * arrayOfFloat[3] + 0.0f * arrayOfFloat[4] + arrayOfFloat[5]; double arc = Math.atan2(event.getY(0) - y, event.getX(0) - x); return (float) Math.toDegrees(arc); } /** * 触摸点到矩形中点的距离 * * @param event * @return */ private float diagonalLength(MotionEvent event) { float diagonalLength = (float) Math.hypot(event.getX(0) - mid.x, event.getY(0) - mid.y); return diagonalLength; } /** * Determine the space between the first two fingers */ private float spacing(MotionEvent event) { if (event.getPointerCount() == 2) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } else { return 0; } } public interface OperationListener { void onDeleteClick(); void onEdit(BubbleTextView bubbleTextView); void onClick(BubbleTextView bubbleTextView); void onTop(BubbleTextView bubbleTextView); } public void setOperationListener(OperationListener operationListener) { this.operationListener = operationListener; } public void setInEdit(boolean isInEdit) { this.isInEdit = isInEdit; invalidate(); } /** * 自动分割文本 * * @param content 需要分割的文本 * @param p 画笔,用来根据字体测量文本的宽度 * @param width 指定的宽度 * @return 一个字符串数组,保存每行的文本 */ private String[] autoSplit(String content, Paint p, float width) { int length = content.length(); float textWidth = p.measureText(content); if (textWidth <= width) { return new String[]{content}; } int start = 0, end = 1, i = 0; int lines = (int) Math.ceil(textWidth / width); //计算行数 String[] lineTexts = new String[lines]; while (start < length) { if (p.measureText(content, start, end) > width) { //文本宽度超出控件宽度时 lineTexts[i++] = (String) content.subSequence(start, end); start = end; } if (end == length) { //不足一行的文本 lineTexts[i] = (String) content.subSequence(start, end); break; } end += 1; } return lineTexts; } public String getmStr() { return mStr; } }