提交 22da2224 编写于 作者: H huangziwei

优化SelectableItem代码

上级 5acad5d5
......@@ -21,7 +21,7 @@
### 使用
```
compile 'com.hzw.graffiti:graffiti:4.2'
compile 'com.hzw.graffiti:graffiti:4.3'
```
......@@ -145,6 +145,10 @@ dependencies {
### 更新日志
* 2017-07-17 v4.3(8)
(1)增加贴图功能.
* 2017-03-16 v4.2(7)
(1)美化涂鸦界面.
......
MIN_SDK_VERSION=14
TARGET_SDK_VERSION=22
VERSION_NAME=4.2
VERSION_CODE=7
VERSION_NAME=4.3
VERSION_CODE=8
BUILD_TOOLS_VERSION=23.0.2
COMPILE_SDK_VERSION=23
......@@ -379,8 +379,8 @@ public class GraffitiActivity extends Activity {
return;
}
if (graffitiText == null) {
mGraffitiView.addSelectableItem(new GraffitiText(text, mGraffitiView.getPaintSize(), mGraffitiView.getColor().copy(),
0, mGraffitiView.getRotateDegree(), x, y, mGraffitiView.getOriginalPivotX(), mGraffitiView.getOriginalPivotY()));
mGraffitiView.addSelectableItem(new GraffitiText(mGraffitiView.getPen(), text, mGraffitiView.getPaintSize(), mGraffitiView.getColor().copy(),
0, mGraffitiView.getGraffitiRotateDegree(), x, y, mGraffitiView.getOriginalPivotX(), mGraffitiView.getOriginalPivotY()));
} else {
graffitiText.setText(text);
}
......@@ -431,8 +431,8 @@ public class GraffitiActivity extends Activity {
Bitmap bitmap = ImageUtils.createBitmapFromPath(pathList.get(0), mGraffitiView.getWidth() / 4, mGraffitiView.getHeight() / 4);
if (graffitiBitmap == null) {
mGraffitiView.addSelectableItem(new GraffitiBitmap(bitmap, mGraffitiView.getPaintSize(), mGraffitiView.getColor().copy(),
0, mGraffitiView.getRotateDegree(), x, y, mGraffitiView.getOriginalPivotX(), mGraffitiView.getOriginalPivotY()));
mGraffitiView.addSelectableItem(new GraffitiBitmap(mGraffitiView.getPen(), bitmap, mGraffitiView.getPaintSize(), mGraffitiView.getColor().copy(),
0, mGraffitiView.getGraffitiRotateDegree(), x, y, mGraffitiView.getOriginalPivotX(), mGraffitiView.getOriginalPivotY()));
} else {
graffitiBitmap.setBitmap(bitmap);
}
......@@ -634,7 +634,7 @@ public class GraffitiActivity extends Activity {
findViewById(R.id.graffiti_btn_rotate).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mGraffitiView.rotate(mGraffitiView.getRotateDegree() + 90);
mGraffitiView.rotate(mGraffitiView.getGraffitiRotateDegree() + 90);
}
});
}
......
......@@ -6,134 +6,48 @@ import android.graphics.Paint;
import android.graphics.Rect;
import static cn.hzw.graffiti.DrawUtil.GRAFFITI_PIXEL_UNIT;
import static cn.hzw.graffiti.DrawUtil.restoreRotatePointInGraffiti;
import static cn.hzw.graffiti.DrawUtil.rotatePoint;
import static cn.hzw.graffiti.DrawUtil.rotatePointInGraffiti;
/**
* Created by huangziwei on 2017/3/16.
*/
public class GraffitiBitmap implements Undoable, GraffitiSelectableItem {
public class GraffitiBitmap extends GraffitiSelectableItem {
private final static Paint sPaint = new Paint();
private Bitmap mBitmap;
private float mSize;
private GraffitiColor mColor;
private float mTextRotate; // 文字的旋转角度
private int mRotateDegree; // 图片的旋转角度
float mPivotX, mPivotY;
private float mX, mY;
private Rect mRect = new Rect();
private Rect mSrcRect = new Rect();
private Rect mDstRect = new Rect();
public GraffitiBitmap(Bitmap bitmap, float size, GraffitiColor color, int textRotate, int rotateDegree, float x, float y, float px, float py) {
public GraffitiBitmap(GraffitiView.Pen pen, Bitmap bitmap, float size, GraffitiColor color, int textRotate, int rotateDegree, float x, float y, float px, float py) {
super(pen, size, color, textRotate, rotateDegree, x, y, px, py);
this.mBitmap = bitmap;
this.mSize = size;
this.mColor = color;
this.mTextRotate = textRotate;
this.mRotateDegree = rotateDegree;
this.mX = x;
this.mY = y;
this.mPivotX = px;
this.mPivotY = py;
resetBounds();
}
private void resetBounds() {
mRect.set(0, 0, (int) mSize, (int) (mSize * mBitmap.getHeight() / mBitmap.getWidth()));
mRect.left -= 10 * GRAFFITI_PIXEL_UNIT;
mRect.top -= 10 * GRAFFITI_PIXEL_UNIT;
mRect.right += 10 * GRAFFITI_PIXEL_UNIT;
mRect.bottom += 10 * GRAFFITI_PIXEL_UNIT;
mSrcRect.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
mDstRect.set(0, 0, (int) mSize, (int) (mSize * mBitmap.getHeight()) / mBitmap.getWidth());
}
public float getSize() {
return mSize;
}
public void setSize(float size) {
mSize = size;
resetBounds();
}
public void setXy(int currentRotate, float x, float y) {
float[] xy = restoreRotatePointInGraffiti(currentRotate, mRotateDegree, x, y, mPivotX, mPivotY);
mX = xy[0];
mY = xy[1];
}
public float[] getXy(int currentDegree) {
return rotatePointInGraffiti(currentDegree, mRotateDegree, mX, mY, mPivotX, mPivotY);
resetBounds(getBounds());
}
public void setBitmap(Bitmap bitmap) {
mBitmap = bitmap;
resetBounds();
resetBounds(getBounds());
}
public Bitmap getBitmap() {
return mBitmap;
}
public GraffitiColor getColor() {
return mColor;
}
public void setColor(GraffitiColor color) {
mColor = color;
}
public Rect getBounds(int currentRotate) {
return mRect;
}
public void setItemRotate(float textRotate) {
mTextRotate = textRotate;
}
public float getItemRotate() {
return mTextRotate;
}
public int getGraffitiRotate() {
return mRotateDegree;
}
// 判断xy是否在文字范围内
@Override
public boolean isInIt(int currentRotate, float x, float y, GraffitiView.Pen pen) {
if (pen != GraffitiView.Pen.BITMAP) {
return false;
public void resetBounds(Rect rect) {
if (mBitmap == null) {
return;
}
float[] xy = getXy(currentRotate);
// 把触摸点转换成在文字坐标系(即以文字起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后相对于矩形的触摸点,还原回未变换前的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mRotateDegree + mTextRotate), x, y, 0, 0);
return mRect.contains((int) rectXy[0], (int) rectXy[1]);
}
float size = getSize();
rect.set(0, 0, (int) size, (int) (size * mBitmap.getHeight() / mBitmap.getWidth()));
rect.left -= 10 * GRAFFITI_PIXEL_UNIT;
rect.top -= 10 * GRAFFITI_PIXEL_UNIT;
rect.right += 10 * GRAFFITI_PIXEL_UNIT;
rect.bottom += 10 * GRAFFITI_PIXEL_UNIT;
mSrcRect.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
mDstRect.set(0, 0, (int) size, (int) (size * mBitmap.getHeight()) / mBitmap.getWidth());
@Override
public boolean isCanRotate(int currentRotate, float x, float y) {
float[] xy = getXy(currentRotate);
// 把触摸点转换成在文字坐标系(即以文字起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后矩形中的触摸点,还原回未变换前矩形中的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mRotateDegree + mTextRotate), x, y, 0, 0);
return rectXy[0] >= mRect.right && rectXy[0] <= mRect.right + ITEM_CAN_ROTATE_BOUND * GRAFFITI_PIXEL_UNIT
&& rectXy[1] >= mRect.top && rectXy[1] <= mRect.bottom;
}
@Override
......
......@@ -4,39 +4,123 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import static cn.hzw.graffiti.DrawUtil.GRAFFITI_PIXEL_UNIT;
import static cn.hzw.graffiti.DrawUtil.restoreRotatePointInGraffiti;
import static cn.hzw.graffiti.DrawUtil.rotatePoint;
import static cn.hzw.graffiti.DrawUtil.rotatePointInGraffiti;
/**
* 可选择的涂鸦条目,例如文字、图片
* Created by huangziwei on 2017/7/16.
*/
public interface GraffitiSelectableItem {
public abstract class GraffitiSelectableItem implements Undoable {
public final static int ITEM_CAN_ROTATE_BOUND = 80;
public float getSize();
public void setSize(float size);
public void setXy(int currentRotate, float x, float y);
public float[] getXy(int currentDegree);
int getGraffitiRotate();
public void setItemRotate(float textRotate);
public float getItemRotate();
// 判断xy是否在文字范围内
public boolean isInIt(int currentRotate, float x, float y, GraffitiView.Pen pen);
public boolean isCanRotate(int currentRotate, float x, float y);
public Rect getBounds(int currentRotate);
public GraffitiColor getColor();
public void setColor(GraffitiColor color);
void draw(Canvas canvas, GraffitiView graffitiView, Paint paint);
private GraffitiView.Pen mPen;
private float mSize;
private GraffitiColor mColor;
private float mItemRotate; // item的旋转角度
private int mGraffitiDegree; // 涂鸦图片的旋转角度
float mPivotX, mPivotY;
private float mX, mY;
private Rect mRect = new Rect();
public GraffitiSelectableItem(GraffitiView.Pen pen, float size, GraffitiColor color, int textRotate, int rotateDegree, float x, float y, float px, float py) {
this.mPen = pen;
this.mSize = size;
this.mColor = color;
this.mItemRotate = textRotate;
this.mGraffitiDegree = rotateDegree;
this.mX = x;
this.mY = y;
this.mPivotX = px;
this.mPivotY = py;
resetBounds(mRect);
}
public Rect getBounds() {
return mRect;
}
public float getSize() {
return mSize;
}
public void setSize(float size) {
mSize = size;
resetBounds(mRect);
}
public void setXy(int currentRotate, float x, float y) {
float[] xy = restoreRotatePointInGraffiti(currentRotate, mGraffitiDegree, x, y, mPivotX, mPivotY);
mX = xy[0];
mY = xy[1];
}
public float[] getXy(int currentDegree) {
return rotatePointInGraffiti(currentDegree, mGraffitiDegree, mX, mY, mPivotX, mPivotY);
}
public GraffitiColor getColor() {
return mColor;
}
public void setColor(GraffitiColor color) {
mColor = color;
}
public Rect getBounds(int currentRotate) {
return mRect;
}
public void setItemRotate(float textRotate) {
mItemRotate = textRotate;
}
public float getItemRotate() {
return mItemRotate;
}
public int getGraffitiRotate() {
return mGraffitiDegree;
}
/**
* 是否击中
*/
public boolean isInIt(int currentRotate, float x, float y, GraffitiView.Pen pen) {
if (pen != mPen) {
return false;
}
float[] xy = getXy(currentRotate);
// 把触摸点转换成在文字坐标系(即以文字起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后相对于矩形的触摸点,还原回未变换前的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mGraffitiDegree + mItemRotate), x, y, 0, 0);
return mRect.contains((int) rectXy[0], (int) rectXy[1]);
}
/**
* 是否可以旋转
*/
public boolean isCanRotate(int currentRotate, float x, float y) {
float[] xy = getXy(currentRotate);
// 把触摸点转换成在item坐标系(即以item起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后矩形中的触摸点,还原回未变换前矩形中的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mGraffitiDegree + mItemRotate), x, y, 0, 0);
return rectXy[0] >= mRect.right && rectXy[0] <= mRect.right + ITEM_CAN_ROTATE_BOUND * GRAFFITI_PIXEL_UNIT
&& rectXy[1] >= mRect.top && rectXy[1] <= mRect.bottom;
}
public abstract void resetBounds(Rect rect);
public abstract void draw(Canvas canvas, GraffitiView graffitiView, Paint paint);
}
......@@ -3,135 +3,52 @@ package cn.hzw.graffiti;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
import static cn.hzw.graffiti.DrawUtil.*;
import static cn.hzw.graffiti.DrawUtil.GRAFFITI_PIXEL_UNIT;
/**
* Created by huangziwei on 2017/3/16.
*/
public class GraffitiText implements Undoable, GraffitiSelectableItem {
public class GraffitiText extends GraffitiSelectableItem {
private final static Paint sPaint = new Paint();
private String mText;
private float mSize;
private GraffitiColor mColor;
private float mTextRotate; // 文字的旋转角度
private int mRotateDegree; // 图片的旋转角度
float mPivotX, mPivotY;
private float mX, mY;
private Rect mRect = new Rect();
public GraffitiText(String mText, float mSize, GraffitiColor mColor, int mTextRotate, int mRotateDegree, float mX, float mY, float px, float py) {
this.mText = mText;
this.mSize = mSize;
this.mColor = mColor;
this.mTextRotate = mTextRotate;
this.mRotateDegree = mRotateDegree;
this.mX = mX;
this.mY = mY;
this.mPivotX = px;
this.mPivotY = py;
resetBounds();
}
private void resetBounds() {
sPaint.setTextSize(mSize);
sPaint.setStyle(Paint.Style.FILL);
sPaint.getTextBounds(mText, 0, mText.length(), mRect);
mRect.left -= 10 * GRAFFITI_PIXEL_UNIT;
mRect.top -= 10 * GRAFFITI_PIXEL_UNIT;
mRect.right += 10 * GRAFFITI_PIXEL_UNIT;
mRect.bottom += 10 * GRAFFITI_PIXEL_UNIT;
}
public float getSize() {
return mSize;
}
public void setSize(float size) {
mSize = size;
resetBounds();
}
public void setXy(int currentRotate, float x, float y) {
float[] xy = restoreRotatePointInGraffiti(currentRotate, mRotateDegree, x, y, mPivotX, mPivotY);
mX = xy[0];
mY = xy[1];
}
public float[] getXy(int currentDegree) {
return rotatePointInGraffiti(currentDegree, mRotateDegree, mX, mY, mPivotX, mPivotY);
}
public void setText(String text) {
mText = text;
resetBounds();
public GraffitiText(GraffitiView.Pen pen, String text, float size, GraffitiColor color, int textRotate, int rotateDegree, float x, float y, float px, float py) {
super(pen, size, color, textRotate, rotateDegree, x, y, px, py);
this.mText = text;
resetBounds(getBounds());
}
public String getText() {
return mText;
}
public GraffitiColor getColor() {
return mColor;
}
public void setColor(GraffitiColor color) {
mColor = color;
}
public Rect getBounds(int currentRotate) {
return mRect;
}
public void setItemRotate(float textRotate) {
mTextRotate = textRotate;
}
public float getItemRotate() {
return mTextRotate;
}
public int getGraffitiRotate() {
return mRotateDegree;
public void setText(String text) {
mText = text;
resetBounds(getBounds());
}
// 判断xy是否在文字范围内
@Override
public boolean isInIt(int currentRotate, float x, float y, GraffitiView.Pen pen) {
if (pen != GraffitiView.Pen.TEXT) {
return false;
public void resetBounds(Rect rect) {
if (TextUtils.isEmpty(mText)) {
return;
}
float[] xy = getXy(currentRotate);
// 把触摸点转换成在文字坐标系(即以文字起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后相对于矩形的触摸点,还原回未变换前的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mRotateDegree + mTextRotate), x, y, 0, 0);
return mRect.contains((int) rectXy[0], (int) rectXy[1]);
}
@Override
public boolean isCanRotate(int currentRotate, float x, float y) {
float[] xy = getXy(currentRotate);
// 把触摸点转换成在文字坐标系(即以文字起始点作为坐标原点)内的点
x = x - xy[0];
y = y - xy[1];
// 把变换后矩形中的触摸点,还原回未变换前矩形中的点,然后判断是否矩形中
float[] rectXy = rotatePoint((int) -(currentRotate - mRotateDegree + mTextRotate), x, y, 0, 0);
return rectXy[0] >= mRect.right && rectXy[0] <= mRect.right + ITEM_CAN_ROTATE_BOUND * GRAFFITI_PIXEL_UNIT
&& rectXy[1] >= mRect.top && rectXy[1] <= mRect.bottom;
sPaint.setTextSize(getSize());
sPaint.setStyle(Paint.Style.FILL);
sPaint.getTextBounds(mText, 0, mText.length(), rect);
rect.left -= 10 * GRAFFITI_PIXEL_UNIT;
rect.top -= 10 * GRAFFITI_PIXEL_UNIT;
rect.right += 10 * GRAFFITI_PIXEL_UNIT;
rect.bottom += 10 * GRAFFITI_PIXEL_UNIT;
}
@Override
public void draw(Canvas canvas, GraffitiView graffitiView, Paint paint) {
paint.setTextSize(getSize());
paint.setStyle(Paint.Style.FILL);
canvas.drawText(getText(), 0, 0, paint);
canvas.drawText(mText, 0, 0, paint);
}
}
......
......@@ -230,9 +230,9 @@ public class GraffitiView extends View {
if (isPenSelectable()) {
mIsRotatingSelectedItem = false;
if (mSelectedItem != null) {
if (mSelectedItem.isCanRotate(mRotateDegree, toX(mTouchX), toY(mTouchY))) {
if (mSelectedItem.isCanRotate(mGraffitiRotateDegree, toX(mTouchX), toY(mTouchY))) {
mIsRotatingSelectedItem = true;
float[] xy = mSelectedItem.getXy(mRotateDegree);
float[] xy = mSelectedItem.getXy(mGraffitiRotateDegree);
mRotateTextDiff = mSelectedItem.getItemRotate() -
computeAngle(xy[0], xy[1], toX(mTouchX), toY(mTouchY));
}
......@@ -242,10 +242,10 @@ public class GraffitiView extends View {
GraffitiSelectableItem item;
for (int i = mSelectableStack.size() - 1; i >= 0; i--) {
item = mSelectableStack.get(i);
if (item.isInIt(mRotateDegree, toX(mTouchX), toY(mTouchY), mPen)) {
if (item.isInIt(mGraffitiRotateDegree, toX(mTouchX), toY(mTouchY), mPen)) {
found = true;
mSelectedItem = item;
float[] xy = item.getXy(mRotateDegree);
float[] xy = item.getXy(mGraffitiRotateDegree);
mSelectedItemX = xy[0];
mSelectedItemY = xy[1];
mGraffitiListener.onSelectedItem(mSelectedItem, true);
......@@ -325,11 +325,11 @@ public class GraffitiView extends View {
toY(mLastTouchY),
toX((mTouchX + mLastTouchX) / 2),
toY((mTouchY + mLastTouchY) / 2));
path = GraffitiPath.toPath(mPen, mShape, mPaintSize, mColor.copy(), mCurrPath, mRotateDegree, mOriginalPivotX, mOriginalPivotY,
path = GraffitiPath.toPath(mPen, mShape, mPaintSize, mColor.copy(), mCurrPath, mGraffitiRotateDegree, mOriginalPivotX, mOriginalPivotY,
getCopyLocation());
} else { // 画图形
path = GraffitiPath.toShape(mPen, mShape, mPaintSize, mColor.copy(),
toX(mTouchDownX), toY(mTouchDownY), toX(mTouchX), toY(mTouchY), mRotateDegree, mOriginalPivotX, mOriginalPivotY,
toX(mTouchDownX), toY(mTouchDownY), toX(mTouchX), toY(mTouchY), mGraffitiRotateDegree, mOriginalPivotX, mOriginalPivotY,
getCopyLocation());
}
addPath(path);
......@@ -348,13 +348,13 @@ public class GraffitiView extends View {
if (isPenSelectable()) {
if (mIsRotatingSelectedItem) {
float[] xy = mSelectedItem.getXy(mRotateDegree);
float[] xy = mSelectedItem.getXy(mGraffitiRotateDegree);
mSelectedItem.setItemRotate(mRotateTextDiff + computeAngle(
xy[0], xy[1], toX(mTouchX), toY(mTouchY)
));
} else {
if (mSelectedItem != null) {
mSelectedItem.setXy(mRotateDegree,
mSelectedItem.setXy(mGraffitiRotateDegree,
mSelectedItemX + toX(mTouchX) - toX(mTouchDownX),
mSelectedItemY + toY(mTouchY) - toY(mTouchDownY));
}
......@@ -401,10 +401,10 @@ public class GraffitiView extends View {
onTouchEvent(event);
}
private int mRotateDegree = 0; // 相对于初始图片旋转的角度
private int mGraffitiRotateDegree = 0; // 相对于初始图片旋转的角度
public int getRotateDegree() {
return mRotateDegree;
public int getGraffitiRotateDegree() {
return mGraffitiRotateDegree;
}
/**
......@@ -425,14 +425,14 @@ public class GraffitiView extends View {
degree = 0;
}
if (degree == mRotateDegree) {
if (degree == mGraffitiRotateDegree) {
return;
}
int r = degree - mRotateDegree;
int originalDegree = mRotateDegree;
mRotateDegree = degree;