Skip to content

Commit

Permalink
Merge branch 'release/1.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mvojtkovszky committed Apr 29, 2022
2 parents 27ce826 + 8d88baf commit 354c2ce
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 65 deletions.
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# CHANGELOG

## 1.4.0 (2022-04-29)
* add `DrawingViewState` containing all the needed information to draw image on canvas
* bump Kotlin to 1.6.21, Gradle plugin to 7.1.3
* bump buildToolsVersion to 32.0.0, compileSdkVersion and targetSdkVersion to 32

## 1.3.0 (2020-08-05)
* bump Gradle plugin to 7.0.0
* update publish scripts

## 1.2.0 (2021-03-24)
* add `undoAll()`, `redoAll()`, `clearRedoHistory()`, `isDrawingEmpty()`
* add explicit callbacks parameter names to `listenerEmptyState` and `listenerDrawingInProgress`
* add option to define canvas colour
* remove reference to additional canvas
* better documentation of public methods and parameters

## 1.1.0 (2021-03-23)
* add `sizeChanged` flag to determine when to create new canvas instead of creating it from
bitmap on every size change
* bump Kotlin to 1.4.31, Gradle plugin to 4.1.3, Build tools to 30.0.3

## 1.0.2 (2020-12-05)
* add documentation
* remove deprecated Kotlin extensions from example app
* bump to Kotlin 1.4.20 and Gradle plugin 4.1.1

## 1.0.1 (2020-08-24)
* add license
* bump Kotlin to 1.4.0, Gradle plugin to 4.0.1 and build tools to 30.0.2

## 1.0.0 (2020-05-26)
* initial public release
11 changes: 3 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
Expand All @@ -40,10 +35,10 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation project(":drawingview")

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
9 changes: 6 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
package="com.vojtkovszky.drawingview.example">

<application
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = "1.5.21" /* https://github.com/JetBrains/kotlin */
dokka_version = '1.5.0' /* https://github.com/Kotlin/dokka/releases */
kotlin_version = "1.6.21" /* https://github.com/JetBrains/kotlin */
dokka_version = '1.6.20' /* https://github.com/Kotlin/dokka/releases */
}

repositories {
Expand All @@ -10,7 +10,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.0.0'
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
}
Expand Down
1 change: 0 additions & 1 deletion drawingview/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ android {
defaultConfig {
minSdkVersion project.properties['minSdkVersion'] as Integer
targetSdkVersion project.properties['targetSdkVersion'] as Integer
versionName project.properties['versionName'] as String
}

sourceSets {
Expand Down
2 changes: 1 addition & 1 deletion drawingview/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ buildTypes=debug,release
groupId=com.github.mvojtkovszky
artifactId=DrawingView
moduleId=drawingview
versionName=1.3.0
versionName=1.4.0
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,19 @@ class DrawingView @JvmOverloads constructor(
strokeWidth = 30f
}

private val drawPathHistory = mutableListOf<Path>()
private val drawPaintHistory = mutableListOf<Paint>()
private val undonePaths = mutableListOf<Path>()
private val undonePaints = mutableListOf<Paint>()
/**
* State holding all necessary information to populate the view and keeping track of it's
* active history
*/
var state = DrawingViewState()
set(value) {
field = value
invalidate()
}

private var xStart: Float = 0f // reference positions for last move (x)
private var yStart: Float = 0f // reference positions for last move (y)
private var isCurrentlyMoving = false
private var isDrawingEmpty = true // track if anything written
set(value) {
field = value
if (value) {
listenerEmptyState?.let { it(true) }
}
}
// endregion

// region Public attributes
Expand Down Expand Up @@ -117,11 +115,13 @@ class DrawingView @JvmOverloads constructor(
override fun onDraw(canvas: Canvas) {
canvas.drawColor(canvasColor)

for (i in drawPathHistory.indices) {
val path = drawPathHistory[i]
val paint = drawPaintHistory[i]
canvas.drawPath(path, paint)
val numStepsInHistory = state.numHistorySteps()
if (numStepsInHistory > 0) {
for (i in 0 until numStepsInHistory) {
canvas.drawPath(state.getPathFromHistory(i), state.getPaintFromHistory(i))
}
}

canvas.drawPath(drawPath, drawPaint)

super.onDraw(canvas)
Expand All @@ -140,16 +140,15 @@ class DrawingView @JvmOverloads constructor(
when (event.action) {

MotionEvent.ACTION_DOWN -> {
undonePaths.clear()
undonePaints.clear()
state.clearRedoHistory()

drawPath.reset()
drawPath.moveTo(touchX, touchY)

xStart = touchX
yStart = touchY

listenerDrawingInProgress?.let { it(true) }
listenerDrawingInProgress?.invoke(true)
}

MotionEvent.ACTION_MOVE -> {
Expand All @@ -169,16 +168,19 @@ class DrawingView @JvmOverloads constructor(
drawPath.addCircle(touchX, touchY, 0.1f, Path.Direction.CW)
}

drawPathHistory.add(drawPath)
drawPaintHistory.add(Paint(drawPaint))
val drawingEmptyBeforeAdding = state.isHistoryEmpty()

state.addToHistory(drawPath, drawPaint)

drawPath = Path()

if (isDrawingEmpty) listenerEmptyState?.let { it(false) }
isDrawingEmpty = false
// moved from empty to no longer empty
if (drawingEmptyBeforeAdding) {
listenerEmptyState?.invoke(false)
}

isCurrentlyMoving = false
listenerDrawingInProgress?.let { it(false) }
listenerDrawingInProgress?.invoke(false)
}

else -> return false
Expand All @@ -193,28 +195,22 @@ class DrawingView @JvmOverloads constructor(
* Clear canvas. Will also clear all history
*/
fun startNew() {
drawPathHistory.clear()
drawPaintHistory.clear()
undonePaths.clear()
undonePaints.clear()

state.startNew()
invalidate()

isDrawingEmpty = true
listenerEmptyState?.invoke(true)
}

/**
* Undo
*/
fun undo() {
if (drawPathHistory.size > 0) {
undonePaths.add(drawPathHistory.removeAt(drawPathHistory.size - 1))
undonePaints.add(drawPaintHistory.removeAt(drawPaintHistory.size - 1))
if (!state.isHistoryEmpty()) {
state.undo()
invalidate()
}

if (drawPathHistory.isEmpty()) {
isDrawingEmpty = true
if (state.isHistoryEmpty()) {
listenerEmptyState?.invoke(true)
}
}

Expand All @@ -224,7 +220,7 @@ class DrawingView @JvmOverloads constructor(
* [redo] or [redoAll]
*/
fun undoAll() {
repeat(drawPathHistory.size) {
repeat(state.numHistorySteps()) {
undo()
}
}
Expand All @@ -233,21 +229,17 @@ class DrawingView @JvmOverloads constructor(
* Redo
*/
fun redo() {
if (undonePaths.size > 0) {
drawPathHistory.add(undonePaths.removeAt(undonePaths.size - 1))
drawPaintHistory.add(undonePaints.removeAt(undonePaints.size - 1))

if (!state.isUndoneEmpty()) {
state.redo()
invalidate()

isDrawingEmpty = false
}
}

/**
* Redo all known undone steps
*/
fun redoAll() {
repeat(undonePaths.size) {
repeat(state.numUndoneSteps()) {
redo()
}
}
Expand All @@ -256,7 +248,7 @@ class DrawingView @JvmOverloads constructor(
* Clears undone history, essentially making [redo] do nothing
*/
fun clearRedoHistory() {
undonePaths.clear()
state.clearRedoHistory()
}

/**
Expand All @@ -271,7 +263,7 @@ class DrawingView @JvmOverloads constructor(
* Determine if canvas is empty.
*/
fun isDrawingEmpty(): Boolean {
return isDrawingEmpty
return state.isHistoryEmpty()
}
// endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.vojtkovszky.drawingview

import android.graphics.Paint
import android.graphics.Path
import java.io.Serializable

/**
* Class holding all the necessary information required to draw paths and paints
* on the canvas.
*/
class DrawingViewState: Serializable {

private val drawPathHistory = mutableListOf<Path>()
private val drawPaintHistory = mutableListOf<Paint>()
private val undonePaths = mutableListOf<Path>()
private val undonePaints = mutableListOf<Paint>()

// add path and paint to history
internal fun addToHistory(path: Path, paint: Paint) {
drawPathHistory.add(path)
drawPaintHistory.add(Paint(paint))
}

// clears redo history by cleaning undone paths and paints
internal fun clearRedoHistory() {
undonePaths.clear()
undonePaints.clear()
}

// return single path from history on index
internal fun getPathFromHistory(index: Int): Path {
return drawPathHistory[index]
}

// return single paint from history on index
internal fun getPaintFromHistory(index: Int): Paint {
return drawPaintHistory[index]
}

// return number of available undone steps
internal fun numUndoneSteps(): Int {
return undonePaths.size
}

// return number of available history steps
internal fun numHistorySteps(): Int {
return drawPathHistory.size
}

// moves latest path and pant from undone list to history list
// by removing from undone list and adding to history list
internal fun redo() {
drawPathHistory.add(undonePaths.removeAt(undonePaths.size - 1))
drawPaintHistory.add(undonePaints.removeAt(undonePaints.size - 1))
}

// reset the history and undone lists
internal fun startNew() {
drawPathHistory.clear()
drawPaintHistory.clear()
undonePaths.clear()
undonePaints.clear()
}

// moves latest path and pant from history list to undone list
// by removing from history list and adding to undone list
internal fun undo() {
undonePaths.add(drawPathHistory.removeAt(drawPathHistory.size - 1))
undonePaints.add(drawPaintHistory.removeAt(drawPaintHistory.size - 1))
}

/**
* Determine if we have any paths or paints in history
*/
fun isHistoryEmpty(): Boolean {
return drawPathHistory.isEmpty() //&& drawPaintHistory.isEmpty()
}

/**
* Determine if we have any paths or paints in undo history
*/
fun isUndoneEmpty(): Boolean {
return undonePaths.isEmpty() //&& undonePaints.isEmpty()
}
}
Loading

0 comments on commit 354c2ce

Please sign in to comment.