convert MyCanvas to kotlin
This commit is contained in:
parent
ba838bd5cf
commit
fb95526ac0
|
@ -1,277 +0,0 @@
|
|||
package com.simplemobiletools.draw;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MyCanvas extends View {
|
||||
private Paint mPaint;
|
||||
private MyPath mPath;
|
||||
private Map<MyPath, PaintOptions> mPaths;
|
||||
private PathsChangedListener mListener;
|
||||
|
||||
private PaintOptions mPaintOptions;
|
||||
private float mCurX;
|
||||
private float mCurY;
|
||||
private float mStartX;
|
||||
private float mStartY;
|
||||
private boolean mIsSaving = false;
|
||||
private boolean mIsStrokeWidthBarEnabled = false;
|
||||
|
||||
public MyCanvas(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mPath = new MyPath();
|
||||
mPaint = new Paint();
|
||||
mPaintOptions = new PaintOptions();
|
||||
mPaint.setColor(mPaintOptions.getColor());
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
mPaint.setStrokeWidth(mPaintOptions.getStrokeWidth());
|
||||
mPaint.setAntiAlias(true);
|
||||
|
||||
mPaths = new LinkedHashMap<>();
|
||||
mPaths.put(mPath, mPaintOptions);
|
||||
pathsUpdated();
|
||||
}
|
||||
|
||||
public void setListener(PathsChangedListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
if (mPaths.size() <= 0)
|
||||
return;
|
||||
|
||||
MyPath lastKey = null;
|
||||
for (MyPath key : mPaths.keySet()) {
|
||||
lastKey = key;
|
||||
}
|
||||
|
||||
mPaths.remove(lastKey);
|
||||
pathsUpdated();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setColor(int newColor) {
|
||||
mPaintOptions.setColor(newColor);
|
||||
if (mIsStrokeWidthBarEnabled) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStrokeWidth(float newStrokeWidth) {
|
||||
mPaintOptions.setStrokeWidth(newStrokeWidth);
|
||||
if (mIsStrokeWidthBarEnabled) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIsStrokeWidthBarEnabled(boolean isStrokeWidthBarEnabled) {
|
||||
mIsStrokeWidthBarEnabled = isStrokeWidthBarEnabled;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
final Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(Color.WHITE);
|
||||
mIsSaving = true;
|
||||
draw(canvas);
|
||||
mIsSaving = false;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public Map<MyPath, PaintOptions> getPaths() {
|
||||
return mPaths;
|
||||
}
|
||||
|
||||
public void addPath(MyPath path, PaintOptions options) {
|
||||
mPaths.put(path, options);
|
||||
pathsUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
for (Map.Entry<MyPath, PaintOptions> entry : mPaths.entrySet()) {
|
||||
changePaint(entry.getValue());
|
||||
canvas.drawPath(entry.getKey(), mPaint);
|
||||
}
|
||||
|
||||
changePaint(mPaintOptions);
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
|
||||
if (mIsStrokeWidthBarEnabled && !mIsSaving) {
|
||||
drawPreviewCircle(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawPreviewCircle(Canvas canvas) {
|
||||
Resources res = getResources();
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
float y = getHeight() - res.getDimension(R.dimen.preview_dot_offset_y);
|
||||
canvas.drawCircle(getWidth() / 2, y, mPaintOptions.getStrokeWidth() / 2, mPaint);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setColor(Utils.shouldUseWhite(mPaintOptions.getColor()) ? Color.WHITE : Color.BLACK);
|
||||
mPaint.setStrokeWidth(res.getDimension(R.dimen.preview_dot_stroke_size));
|
||||
|
||||
y = getHeight() - res.getDimension(R.dimen.preview_dot_offset_y);
|
||||
float radius = (mPaintOptions.getStrokeWidth() + res.getDimension(R.dimen.preview_dot_stroke_size)) / 2;
|
||||
canvas.drawCircle(getWidth() / 2, y, radius, mPaint);
|
||||
changePaint(mPaintOptions);
|
||||
}
|
||||
|
||||
private void changePaint(PaintOptions paintOptions) {
|
||||
mPaint.setColor(paintOptions.getColor());
|
||||
mPaint.setStrokeWidth(paintOptions.getStrokeWidth());
|
||||
}
|
||||
|
||||
public void clearCanvas() {
|
||||
mPath.reset();
|
||||
mPaths.clear();
|
||||
pathsUpdated();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void actionDown(float x, float y) {
|
||||
mPath.reset();
|
||||
mPath.moveTo(x, y);
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
}
|
||||
|
||||
private void actionMove(float x, float y) {
|
||||
mPath.quadTo(mCurX, mCurY, (x + mCurX) / 2, (y + mCurY) / 2);
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
}
|
||||
|
||||
private void actionUp() {
|
||||
mPath.lineTo(mCurX, mCurY);
|
||||
|
||||
// draw a dot on click
|
||||
if (mStartX == mCurX && mStartY == mCurY) {
|
||||
mPath.lineTo(mCurX, mCurY + 2);
|
||||
mPath.lineTo(mCurX + 1, mCurY + 2);
|
||||
mPath.lineTo(mCurX + 1, mCurY);
|
||||
}
|
||||
|
||||
mPaths.put(mPath, mPaintOptions);
|
||||
pathsUpdated();
|
||||
mPath = new MyPath();
|
||||
mPaintOptions = new PaintOptions(mPaintOptions.getColor(), mPaintOptions.getStrokeWidth());
|
||||
}
|
||||
|
||||
private void pathsUpdated() {
|
||||
if (mListener != null && mPaths != null) {
|
||||
mListener.pathsChanged(mPaths.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mStartX = x;
|
||||
mStartY = y;
|
||||
actionDown(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
actionMove(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
actionUp();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface PathsChangedListener {
|
||||
void pathsChanged(int cnt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState savedState = new SavedState(superState);
|
||||
|
||||
savedState.mPaths = mPaths;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if (!(state instanceof SavedState)) {
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
SavedState savedState = (SavedState) state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
|
||||
mPaths = savedState.mPaths;
|
||||
pathsUpdated(); // This doesn't seem to be necessary
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
Map<MyPath, PaintOptions> mPaths;
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt(mPaths.size());
|
||||
for (Map.Entry<MyPath, PaintOptions> entry : mPaths.entrySet()) {
|
||||
out.writeSerializable(entry.getKey());
|
||||
PaintOptions paintOptions = entry.getValue();
|
||||
out.writeInt(paintOptions.getColor());
|
||||
out.writeFloat(paintOptions.getStrokeWidth());
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
int size = in.readInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MyPath key = (MyPath) in.readSerializable();
|
||||
PaintOptions paintOptions = new PaintOptions(in.readInt(), in.readFloat());
|
||||
mPaths.put(key, paintOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
package com.simplemobiletools.draw
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import java.util.*
|
||||
|
||||
class MyCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
|
||||
private val mPaint: Paint
|
||||
private var mPath: MyPath? = null
|
||||
private var mPaths: MutableMap<MyPath, PaintOptions>? = null
|
||||
private var mListener: PathsChangedListener? = null
|
||||
|
||||
private var mPaintOptions: PaintOptions? = null
|
||||
private var mCurX = 0f
|
||||
private var mCurY = 0f
|
||||
private var mStartX = 0f
|
||||
private var mStartY = 0f
|
||||
private var mIsSaving = false
|
||||
private var mIsStrokeWidthBarEnabled = false
|
||||
|
||||
init {
|
||||
|
||||
mPath = MyPath()
|
||||
mPaint = Paint()
|
||||
mPaintOptions = PaintOptions()
|
||||
mPaint.color = mPaintOptions!!.color
|
||||
mPaint.style = Paint.Style.STROKE
|
||||
mPaint.strokeJoin = Paint.Join.ROUND
|
||||
mPaint.strokeCap = Paint.Cap.ROUND
|
||||
mPaint.strokeWidth = mPaintOptions!!.strokeWidth
|
||||
mPaint.isAntiAlias = true
|
||||
|
||||
mPaths = LinkedHashMap<MyPath, PaintOptions>()
|
||||
mPaths!!.put(mPath!!, mPaintOptions!!)
|
||||
pathsUpdated()
|
||||
}
|
||||
|
||||
fun setListener(listener: PathsChangedListener) {
|
||||
this.mListener = listener
|
||||
}
|
||||
|
||||
fun undo() {
|
||||
if (mPaths!!.isEmpty())
|
||||
return
|
||||
|
||||
var lastKey: MyPath? = null
|
||||
for (key in mPaths!!.keys) {
|
||||
lastKey = key
|
||||
}
|
||||
|
||||
mPaths!!.remove(lastKey)
|
||||
pathsUpdated()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun setColor(newColor: Int) {
|
||||
mPaintOptions!!.color = newColor
|
||||
if (mIsStrokeWidthBarEnabled) {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun setStrokeWidth(newStrokeWidth: Float) {
|
||||
mPaintOptions!!.strokeWidth = newStrokeWidth
|
||||
if (mIsStrokeWidthBarEnabled) {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun setIsStrokeWidthBarEnabled(isStrokeWidthBarEnabled: Boolean) {
|
||||
mIsStrokeWidthBarEnabled = isStrokeWidthBarEnabled
|
||||
invalidate()
|
||||
}
|
||||
|
||||
val bitmap: Bitmap
|
||||
get() {
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.drawColor(Color.WHITE)
|
||||
mIsSaving = true
|
||||
draw(canvas)
|
||||
mIsSaving = false
|
||||
return bitmap
|
||||
}
|
||||
|
||||
val paths: Map<MyPath, PaintOptions>
|
||||
get() = mPaths!!
|
||||
|
||||
fun addPath(path: MyPath, options: PaintOptions) {
|
||||
mPaths!!.put(path, options)
|
||||
pathsUpdated()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
for ((key, value) in mPaths!!) {
|
||||
changePaint(value)
|
||||
canvas.drawPath(key, mPaint)
|
||||
}
|
||||
|
||||
changePaint(mPaintOptions!!)
|
||||
canvas.drawPath(mPath!!, mPaint)
|
||||
|
||||
if (mIsStrokeWidthBarEnabled && !mIsSaving) {
|
||||
drawPreviewCircle(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawPreviewCircle(canvas: Canvas) {
|
||||
val res = resources
|
||||
mPaint.style = Paint.Style.FILL
|
||||
|
||||
var y = height - res.getDimension(R.dimen.preview_dot_offset_y)
|
||||
canvas.drawCircle((width / 2).toFloat(), y, mPaintOptions!!.strokeWidth / 2, mPaint)
|
||||
mPaint.style = Paint.Style.STROKE
|
||||
mPaint.color = if (Utils.shouldUseWhite(mPaintOptions!!.color)) Color.WHITE else Color.BLACK
|
||||
mPaint.strokeWidth = res.getDimension(R.dimen.preview_dot_stroke_size)
|
||||
|
||||
y = height - res.getDimension(R.dimen.preview_dot_offset_y)
|
||||
val radius = (mPaintOptions!!.strokeWidth + res.getDimension(R.dimen.preview_dot_stroke_size)) / 2
|
||||
canvas.drawCircle((width / 2).toFloat(), y, radius, mPaint)
|
||||
changePaint(mPaintOptions!!)
|
||||
}
|
||||
|
||||
private fun changePaint(paintOptions: PaintOptions) {
|
||||
mPaint.color = paintOptions.color
|
||||
mPaint.strokeWidth = paintOptions.strokeWidth
|
||||
}
|
||||
|
||||
fun clearCanvas() {
|
||||
mPath!!.reset()
|
||||
mPaths!!.clear()
|
||||
pathsUpdated()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun actionDown(x: Float, y: Float) {
|
||||
mPath!!.reset()
|
||||
mPath!!.moveTo(x, y)
|
||||
mCurX = x
|
||||
mCurY = y
|
||||
}
|
||||
|
||||
private fun actionMove(x: Float, y: Float) {
|
||||
mPath!!.quadTo(mCurX, mCurY, (x + mCurX) / 2, (y + mCurY) / 2)
|
||||
mCurX = x
|
||||
mCurY = y
|
||||
}
|
||||
|
||||
private fun actionUp() {
|
||||
mPath!!.lineTo(mCurX, mCurY)
|
||||
|
||||
// draw a dot on click
|
||||
if (mStartX == mCurX && mStartY == mCurY) {
|
||||
mPath!!.lineTo(mCurX, mCurY + 2)
|
||||
mPath!!.lineTo(mCurX + 1, mCurY + 2)
|
||||
mPath!!.lineTo(mCurX + 1, mCurY)
|
||||
}
|
||||
|
||||
mPaths!!.put(mPath!!, mPaintOptions!!)
|
||||
pathsUpdated()
|
||||
mPath = MyPath()
|
||||
mPaintOptions = PaintOptions(mPaintOptions!!.color, mPaintOptions!!.strokeWidth)
|
||||
}
|
||||
|
||||
private fun pathsUpdated() {
|
||||
if (mListener != null && mPaths != null) {
|
||||
mListener!!.pathsChanged(mPaths!!.size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
val x = event.x
|
||||
val y = event.y
|
||||
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
mStartX = x
|
||||
mStartY = y
|
||||
actionDown(x, y)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> actionMove(x, y)
|
||||
MotionEvent.ACTION_UP -> actionUp()
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
invalidate()
|
||||
return true
|
||||
}
|
||||
|
||||
interface PathsChangedListener {
|
||||
fun pathsChanged(cnt: Int)
|
||||
}
|
||||
|
||||
public override fun onSaveInstanceState(): Parcelable {
|
||||
val superState = super.onSaveInstanceState()
|
||||
val savedState = SavedState(superState)
|
||||
|
||||
savedState.mPaths = mPaths
|
||||
return savedState
|
||||
}
|
||||
|
||||
public override fun onRestoreInstanceState(state: Parcelable) {
|
||||
if (state !is SavedState) {
|
||||
super.onRestoreInstanceState(state)
|
||||
return
|
||||
}
|
||||
val savedState = state
|
||||
super.onRestoreInstanceState(savedState.superState)
|
||||
|
||||
mPaths = savedState.mPaths
|
||||
pathsUpdated() // This doesn't seem to be necessary
|
||||
}
|
||||
|
||||
internal class SavedState : View.BaseSavedState {
|
||||
var mPaths: MutableMap<MyPath, PaintOptions>? = null
|
||||
|
||||
constructor(superState: Parcelable) : super(superState)
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
super.writeToParcel(out, flags)
|
||||
out.writeInt(mPaths!!.size)
|
||||
for ((key, paintOptions) in mPaths!!) {
|
||||
out.writeSerializable(key)
|
||||
out.writeInt(paintOptions.color)
|
||||
out.writeFloat(paintOptions.strokeWidth)
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) : super(parcel) {
|
||||
val size = parcel.readInt()
|
||||
for (i in 0..size - 1) {
|
||||
val key = parcel.readSerializable() as MyPath
|
||||
val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat())
|
||||
mPaths!!.put(key, paintOptions)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
|
||||
override fun newArray(size: Int): Array<SavedState> = arrayOf()
|
||||
|
||||
override fun createFromParcel(source: Parcel) = SavedState(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import java.security.InvalidParameterException
|
|||
import java.util.*
|
||||
|
||||
// https://stackoverflow.com/a/8127953
|
||||
internal class MyPath : Path(), Serializable {
|
||||
class MyPath : Path(), Serializable {
|
||||
|
||||
private val actions = LinkedList<Action>()
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.simplemobiletools.draw
|
|||
|
||||
import android.graphics.Color
|
||||
|
||||
internal class PaintOptions {
|
||||
class PaintOptions {
|
||||
var color = Color.BLACK
|
||||
var strokeWidth = 5f
|
||||
|
||||
|
|
Loading…
Reference in New Issue