Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[둘리] 뷰 챌린지 미션 2단계 제출합니다. #38

Merged
merged 28 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dc16a7d
feat: button bindingAdapter로 isSelected 생성
hyemdooly Oct 9, 2023
dee7ae1
feat: 선택된 버튼 색깔 생성
hyemdooly Oct 9, 2023
74f09cb
feat: 브러쉬 요소 추가
hyemdooly Oct 9, 2023
4e324fd
feat: 브러쉬 선택 기능 UI 추가
hyemdooly Oct 9, 2023
2e8ae10
feat: 함수 일부 네이밍 변경 및 Circle, Rectangle 그리기 기능 추가
hyemdooly Oct 9, 2023
4c813d6
refactor: 함수 분리 및 중복 코드 삭제
hyemdooly Oct 9, 2023
c56475f
feat: 지우개 기능 구현
hyemdooly Oct 9, 2023
dc53032
refactor: Drawing 클래스에서 Path, Paint 복사하도록 수정
hyemdooly Oct 9, 2023
7da23af
feat: 전체 삭제, undo, redo 구현
hyemdooly Oct 9, 2023
e22dfd6
refactor: 함수 순서 수정
hyemdooly Oct 9, 2023
87daa31
refactor: SettingState sealed class -> enum class로 변경, 상수 위치 이동
hyemdooly Oct 9, 2023
0b210df
refactor: width로 굵기 네이밍 통일
hyemdooly Oct 9, 2023
943e72c
refactor: Brush -> Tool로 네이밍 변경
hyemdooly Oct 10, 2023
1cf3673
refactor: enum class 상수 네이밍 변경
hyemdooly Oct 10, 2023
316933e
refactor: Drawing 클래스 세분화
hyemdooly Oct 10, 2023
2d10fa3
refactor: tool이 drawing 객체를 리턴하도록 수정
hyemdooly Oct 10, 2023
f67b8ac
refactor: CanvasView가 하던 일을 Drawing 클래스로 이동
hyemdooly Oct 11, 2023
ee553e0
refactor: undo, redo 코드 줄 수 줄이기
hyemdooly Oct 11, 2023
cd6ff78
design: 도구에 따른 설정 화면 변경
hyemdooly Oct 11, 2023
dd8e201
refactor: 도구에 따라 버튼 동적 생성
hyemdooly Oct 11, 2023
26e18fe
refactor: brushes -> tools로 네이밍 수정
hyemdooly Oct 11, 2023
f412171
refactor: 함수명 수정
hyemdooly Oct 11, 2023
acb317f
refactor: tool 바꿀 때 설정창 닫도록 수정
hyemdooly Oct 11, 2023
f55275e
fix: Rectangle 버그 수정
hyemdooly Oct 11, 2023
b10fda7
refactor: 도구 모음 일치
hyemdooly Oct 11, 2023
d4a46c4
refactor: tool -> drawingTool로 네이밍 변경
hyemdooly Oct 11, 2023
1d90521
refactor: Drawings 일급컬렉션 생성
hyemdooly Oct 11, 2023
1264668
refactor: 팩토리 함수 삭제
hyemdooly Oct 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
}

android {
Expand Down
54 changes: 46 additions & 8 deletions app/src/main/java/woowacourse/paint/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.slider.RangeSlider
import woowacourse.paint.canvas.DrawingTool
import woowacourse.paint.databinding.ActivityMainBinding
import woowacourse.paint.databinding.ItemToolBinding
import woowacourse.paint.utils.toUiModel

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
Expand All @@ -13,12 +16,15 @@ class MainActivity : AppCompatActivity() {
private val adapter = ColorsAdapter { model ->
viewModel.pickColor(model)
}
private val toolButtons = mutableListOf<ItemToolBinding>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupBinding()
setupViewModel()
setupToolbar()
setupCanvas()
setupTools()
setupColors()
setupWidthSlider()
}
Expand All @@ -40,6 +46,42 @@ class MainActivity : AppCompatActivity() {
adapter.submitList(colors)
}
}
viewModel.selectedDrawingTool.observe(this) { tool ->
canvasView.setupTools(tool)
changeButtonSelectedStatus(tool)
}
}

private fun changeButtonSelectedStatus(drawingTool: DrawingTool) {
toolButtons.forEach { button ->
button.root.isSelected = button.drawingTool == drawingTool
}
viewModel.setSettingState(PaintChangingState.NOTHING)
}

private fun setupToolbar() {
setSupportActionBar(binding.tbPaint)
binding.ivRedo.setOnClickListener {
canvasView.redo()
}
binding.ivUndo.setOnClickListener {
canvasView.undo()
}
binding.ivClear.setOnClickListener {
canvasView.eraseAll()
}
}

private fun setupTools() {
viewModel.drawingTools.forEach { toolAssigned ->
val toolButtonBinding = ItemToolBinding.inflate(layoutInflater, binding.llTools, true)
with(toolButtonBinding) {
onClick = viewModel::pickTool
drawingTool = toolAssigned
name = getString(toolAssigned.toUiModel().toolNameId)
}
toolButtons.add(toolButtonBinding)
}
}

private fun setupCanvas() {
Expand Down Expand Up @@ -67,17 +109,13 @@ class MainActivity : AppCompatActivity() {
}

private fun setupWidthSlider() {
binding.rsThicknessChanger.valueFrom = minWidth
binding.rsThicknessChanger.valueTo = maxWidth
binding.rsThicknessChanger.addOnChangeListener(
binding.rsWidthChanger.valueFrom = MainViewModel.MIN_WIDTH
binding.rsWidthChanger.valueTo = MainViewModel.MAX_WIDTH
binding.rsWidthChanger.setValues(viewModel.width.value)
binding.rsWidthChanger.addOnChangeListener(
RangeSlider.OnChangeListener { _, value, _ ->
viewModel.pickWidth(value)
},
)
}

companion object {
private const val minWidth = 0f
private const val maxWidth = 100f
}
}
29 changes: 17 additions & 12 deletions app/src/main/java/woowacourse/paint/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import woowacourse.paint.canvas.DrawingTool
import woowacourse.paint.canvas.PaletteColor
import woowacourse.paint.model.ColorUiModel

class MainViewModel : ViewModel() {
private val _paintChangingState =
MutableLiveData<PaintChangingState>(PaintChangingState.Nothing)
MutableLiveData(PaintChangingState.NOTHING)
val paintChangingState: LiveData<PaintChangingState>
get() = _paintChangingState

Expand All @@ -18,6 +19,12 @@ class MainViewModel : ViewModel() {
val colors: LiveData<List<ColorUiModel>>
get() = _colors

val drawingTools = DrawingTool.values().toList()

private var _selectedTool = MutableLiveData(drawingTools.first())
val selectedDrawingTool: LiveData<DrawingTool>
get() = _selectedTool

val selectedColor: LiveData<PaletteColor>
get() = Transformations.map(_colors) { colors ->
colors.firstOrNull { it.isPicked }?.color ?: DEFAULT_SELECTED_COLOR
Expand All @@ -27,20 +34,16 @@ class MainViewModel : ViewModel() {
val width: LiveData<Float>
get() = _width

fun setColorSettingState() {
if (_paintChangingState.value == PaintChangingState.ColorChanging) {
_paintChangingState.value = PaintChangingState.Nothing
fun setSettingState(state: PaintChangingState) {
if (_paintChangingState.value == state) {
_paintChangingState.value = PaintChangingState.NOTHING
return
}
_paintChangingState.value = PaintChangingState.ColorChanging
_paintChangingState.value = state
}

fun setWidthSettingState() {
if (_paintChangingState.value == PaintChangingState.WidthChanging) {
_paintChangingState.value = PaintChangingState.Nothing
return
}
_paintChangingState.value = PaintChangingState.WidthChanging
fun pickTool(drawingTool: DrawingTool) {
_selectedTool.value = drawingTool
}

fun pickColor(model: ColorUiModel) {
Expand All @@ -53,7 +56,9 @@ class MainViewModel : ViewModel() {
}

companion object {
const val DEFAULT_WIDTH = 0F
const val DEFAULT_WIDTH = 1F
const val MIN_WIDTH = 1f
const val MAX_WIDTH = 100f
val DEFAULT_SELECTED_COLOR = PaletteColor.RED
}
}
6 changes: 2 additions & 4 deletions app/src/main/java/woowacourse/paint/PaintChangingState.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package woowacourse.paint

sealed class PaintChangingState {
object Nothing : PaintChangingState()
object ColorChanging : PaintChangingState()
object WidthChanging : PaintChangingState()
enum class PaintChangingState {
NOTHING, COLOR_CHANGING, WIDTH_CHANGING
}
93 changes: 46 additions & 47 deletions app/src/main/java/woowacourse/paint/canvas/CanvasView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,83 +3,82 @@ package woowacourse.paint.canvas
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.annotation.ColorInt
import woowacourse.paint.canvas.drawing.Drawings

class CanvasView(context: Context, attr: AttributeSet) : View(
context,
attr,
) {
private var path = Path()
private var paint = Paint()
private var startPoint: Point = Point(0f, 0f)
private val lines = mutableListOf<Line>()
private var drawingTool = DrawingTool.PEN
private val paint = Paint().apply {
isAntiAlias = true
style = Paint.Style.FILL_AND_STROKE
strokeCap = Paint.Cap.ROUND
xfermode = if (drawingTool == DrawingTool.ERASER) {
PorterDuffXfermode(PorterDuff.Mode.CLEAR)
} else {
null
}
}
private val drawings = Drawings()

fun initPaint(width: Float, color: PaletteColor) {
paint = getPaint(width, color.colorCode)
init {
setLayerType(LAYER_TYPE_HARDWARE, null)
}

fun initPaint(width: Float, selectedColor: PaletteColor) {
setupWidth(width)
setupColor(selectedColor)
}

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
lines.forEach { line ->
canvas.drawPath(line.path, line.paint)
}
canvas.drawPath(path, paint)
drawings.drawAll(canvas)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x
val y = event.y

when (event.action) {
MotionEvent.ACTION_DOWN -> drawDot(x, y)
MotionEvent.ACTION_MOVE -> drawLine(x, y)
MotionEvent.ACTION_DOWN -> drawings.add(drawingTool.draw(paint, ::invalidate))
MotionEvent.ACTION_UP -> drawings.checkLastDrawingEmpty()
else -> super.onTouchEvent(event)
}
return true
}

private fun drawDot(x: Float, y: Float) {
startPoint = Point(x, y)
path.moveTo(startPoint.x, startPoint.y)
path.lineTo(x, y)
invalidate()
return drawings.onDrawingTouchEvent(event)
}

private fun drawLine(x: Float, y: Float) {
path.moveTo(startPoint.x, startPoint.y)
path.lineTo(x, y)
startPoint = Point(x, y)
invalidate()
fun setupTools(selectedDrawingTool: DrawingTool) {
drawingTool = selectedDrawingTool
if (drawingTool == DrawingTool.ERASER) {
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
return
}
paint.xfermode = null
}

fun setupWidth(width: Float) {
addLine()
paint = getPaint(width, paint.color)
paint.strokeWidth = width
}

fun setupColor(color: PaletteColor) {
addLine()
paint = getPaint(paint.strokeWidth, color.colorCode)
paint.color = color.colorCode
}

private fun addLine() {
if (path.isEmpty) return
lines.add(Line(path, paint))
path = Path()
fun eraseAll() {
drawings.clear()
invalidate()
}

private fun getPaint(width: Float, @ColorInt selectedColor: Int): Paint {
return Paint().apply {
isAntiAlias = true
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
color = selectedColor
strokeWidth = width
strokeCap = Paint.Cap.ROUND
}
fun undo() {
drawings.undo()
invalidate()
}

fun redo() {
drawings.redo()
invalidate()
}
}
28 changes: 28 additions & 0 deletions app/src/main/java/woowacourse/paint/canvas/DrawingTool.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package woowacourse.paint.canvas

import android.graphics.Paint
import woowacourse.paint.canvas.drawing.Circle
import woowacourse.paint.canvas.drawing.Drawing
import woowacourse.paint.canvas.drawing.Line
import woowacourse.paint.canvas.drawing.Rectangle

enum class DrawingTool(val hasWidth: Boolean, val hasColor: Boolean) {
PEN(true, true) {
override fun draw(paint: Paint, invalidate: () -> Unit): Drawing =
Line(paint, invalidate)
},
RECTANGLE(false, true) {
override fun draw(paint: Paint, invalidate: () -> Unit): Drawing =
Rectangle(paint, invalidate)
},
CIRCLE(false, true) {
override fun draw(paint: Paint, invalidate: () -> Unit): Drawing =
Circle(paint, invalidate)
},
ERASER(true, false) {
override fun draw(paint: Paint, invalidate: () -> Unit): Drawing =
Line(paint, invalidate)
}, ;

abstract fun draw(paint: Paint, invalidate: () -> Unit): Drawing
}
6 changes: 0 additions & 6 deletions app/src/main/java/woowacourse/paint/canvas/Line.kt

This file was deleted.

28 changes: 28 additions & 0 deletions app/src/main/java/woowacourse/paint/canvas/drawing/Circle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package woowacourse.paint.canvas.drawing

import android.graphics.Paint
import android.graphics.Path
import android.view.MotionEvent
import woowacourse.paint.canvas.Point

class Circle(paint: Paint, private val invalidate: () -> Unit) :
Drawing(Paint(paint)) {
private lateinit var startPoint: Point

override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x
val y = event.y

when (event.action) {
MotionEvent.ACTION_DOWN -> startPoint = Point(x, y)
MotionEvent.ACTION_MOVE -> {
path.reset()
path.addOval(startPoint.x, startPoint.y, x, y, Path.Direction.CW)
invalidate()
}

else -> return true
}
return true
}
}
15 changes: 15 additions & 0 deletions app/src/main/java/woowacourse/paint/canvas/drawing/Drawing.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package woowacourse.paint.canvas.drawing

import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.view.MotionEvent

abstract class Drawing(val paint: Paint) {
val path = Path()
fun onDraw(canvas: Canvas) {
canvas.drawPath(path, paint)
}

abstract fun onTouchEvent(event: MotionEvent): Boolean
tmdgh1592 marked this conversation as resolved.
Show resolved Hide resolved
}
Loading