diff --git a/README.md b/README.md index 4edbbfa7..73885c13 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ # android-paint + +## 기능 요구 사항 + +- [X] 화면에 자유 곡선을 그릴 수 있다. +- [X] 도구 패널을 추가한다. + - [X] 브러시 색상을 변경할 수 있다. (브러시 색상은 빨, 주, 노, 초, 파로 한정) + - [X] 브러시 굵기를 변경할 수 있다. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0a229611..0b6547ca 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } @@ -33,6 +33,9 @@ android { kotlinOptions { jvmTarget = "11" } + dataBinding { + enable = true + } } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 983f40c5..abf99bf2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,8 +12,10 @@ android:theme="@style/Theme.Paint" tools:targetApi="31"> + android:name=".main.MainActivity" + android:exported="true" + android:screenOrientation="portrait" + tools:ignore="LockedOrientationActivity"> diff --git a/app/src/main/java/woowacourse/paint/MainActivity.kt b/app/src/main/java/woowacourse/paint/MainActivity.kt deleted file mode 100644 index f7b7b7a5..00000000 --- a/app/src/main/java/woowacourse/paint/MainActivity.kt +++ /dev/null @@ -1,14 +0,0 @@ -package woowacourse.paint - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity - - -class MainActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - } -} diff --git a/app/src/main/java/woowacourse/paint/main/MainActivity.kt b/app/src/main/java/woowacourse/paint/main/MainActivity.kt new file mode 100644 index 00000000..ce99aab0 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/main/MainActivity.kt @@ -0,0 +1,41 @@ +package woowacourse.paint.main + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import woowacourse.paint.databinding.ActivityMainBinding +import woowacourse.paint.main.adapter.ColorAdapter + +class MainActivity : AppCompatActivity() { + private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } + private val viewModel by lazy { ViewModelProvider(this)[MainViewModel::class.java] } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + setViewModel() + + setColorsRecyclerview() + setBrushSizeListener() + } + + private fun setViewModel() { + binding.viewModel = viewModel + binding.lifecycleOwner = this + } + + private fun setColorsRecyclerview() { + binding.rvColors.apply { + adapter = ColorAdapter { + viewModel.setBrushColor(it) + } + setHasFixedSize(true) + } + } + + private fun setBrushSizeListener() { + binding.rangeSliderBrushSize.addOnChangeListener { _, value, _ -> + viewModel.setBrushSize(value) + } + } +} diff --git a/app/src/main/java/woowacourse/paint/main/MainViewModel.kt b/app/src/main/java/woowacourse/paint/main/MainViewModel.kt new file mode 100644 index 00000000..1b196e8a --- /dev/null +++ b/app/src/main/java/woowacourse/paint/main/MainViewModel.kt @@ -0,0 +1,30 @@ +package woowacourse.paint.main + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import woowacourse.paint.model.BrushSize +import woowacourse.paint.model.PaintColor + +class MainViewModel : ViewModel() { + private val _brushSize = MutableLiveData() + val brushSize: LiveData + get() = _brushSize + + private val _brushColor = MutableLiveData() + val brushColor: LiveData + get() = _brushColor + + init { + _brushSize.value = BrushSize(BrushSize.DEFAULT_SIZE) + _brushColor.value = PaintColor.DEFAULT_COLOR + } + + fun setBrushSize(size: Float) { + _brushSize.value = BrushSize(size) + } + + fun setBrushColor(color: PaintColor) { + _brushColor.value = color + } +} diff --git a/app/src/main/java/woowacourse/paint/main/PaintBoard.kt b/app/src/main/java/woowacourse/paint/main/PaintBoard.kt new file mode 100644 index 00000000..21e2ecf7 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/main/PaintBoard.kt @@ -0,0 +1,77 @@ +package woowacourse.paint.main + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import androidx.core.content.ContextCompat +import woowacourse.paint.model.BrushSize +import woowacourse.paint.model.DrawablePath +import woowacourse.paint.model.DrawablePathHistory +import woowacourse.paint.model.PaintColor + +class PaintBoard constructor(context: Context, attrs: AttributeSet) : View(context, attrs) { + private val pathHistory = DrawablePathHistory() + private var currentPath = Path() + private val currentPaint = Paint() + + init { + setDefaultPaint() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + pathHistory.paths.forEach { + canvas.drawPath(it.path, it.paint) + } + canvas.drawPath(currentPath, currentPaint) + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> startDrawing(event) + MotionEvent.ACTION_MOVE -> moveDrawing(event) + MotionEvent.ACTION_UP -> endDrawing() + else -> return super.onTouchEvent(event) + } + invalidate() + return true + } + + private fun setDefaultPaint() { + currentPaint.apply { + strokeCap = Paint.Cap.ROUND + strokeJoin = Paint.Join.ROUND + isAntiAlias = true + style = Paint.Style.STROKE + } + } + + private fun startDrawing(event: MotionEvent) { + currentPath = Path() + currentPath.moveTo(event.x, event.y) + currentPath.lineTo(event.x, event.y) + } + + private fun moveDrawing(event: MotionEvent) { + currentPath.lineTo(event.x, event.y) + } + + private fun endDrawing() { + pathHistory.add(DrawablePath(currentPath, Paint(currentPaint))) + } + + fun setBrushSize(size: BrushSize) { + currentPaint.strokeWidth = size.width + } + + fun setBrushColor(color: PaintColor) { + currentPaint.color = ContextCompat.getColor(context, color.colorRes) + } +} diff --git a/app/src/main/java/woowacourse/paint/main/adapter/ColorAdapter.kt b/app/src/main/java/woowacourse/paint/main/adapter/ColorAdapter.kt new file mode 100644 index 00000000..a7b874f1 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/main/adapter/ColorAdapter.kt @@ -0,0 +1,26 @@ +package woowacourse.paint.main.adapter + +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import woowacourse.paint.main.viewholder.ColorViewHolder +import woowacourse.paint.model.PaintColor + +class ColorAdapter( + private val onColorClickListener: (PaintColor) -> Unit, +) : RecyclerView.Adapter() { + private val colors = PaintColor.values() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColorViewHolder { + return ColorViewHolder(parent) { + onColorClickListener(it) + } + } + + override fun onBindViewHolder(holder: ColorViewHolder, position: Int) { + holder.bind(colors[position]) + } + + override fun getItemCount(): Int { + return colors.size + } +} diff --git a/app/src/main/java/woowacourse/paint/main/viewholder/ColorViewHolder.kt b/app/src/main/java/woowacourse/paint/main/viewholder/ColorViewHolder.kt new file mode 100644 index 00000000..a5deb678 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/main/viewholder/ColorViewHolder.kt @@ -0,0 +1,32 @@ +package woowacourse.paint.main.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat.getColor +import androidx.recyclerview.widget.RecyclerView +import woowacourse.paint.R +import woowacourse.paint.databinding.ItemColorBinding +import woowacourse.paint.model.PaintColor + +class ColorViewHolder( + parent: ViewGroup, + onColorClickListener: (PaintColor) -> Unit, +) : RecyclerView.ViewHolder( + LayoutInflater.from(parent.context).inflate( + R.layout.item_color, + parent, + false, + ), +) { + private val binding = ItemColorBinding.bind(itemView) + + init { + binding.root.setOnClickListener { + onColorClickListener(PaintColor.getColor(adapterPosition)) + } + } + + fun bind(paintColor: PaintColor) { + binding.color = getColor(itemView.context, paintColor.colorRes) + } +} diff --git a/app/src/main/java/woowacourse/paint/model/BrushSize.kt b/app/src/main/java/woowacourse/paint/model/BrushSize.kt new file mode 100644 index 00000000..8b8e876b --- /dev/null +++ b/app/src/main/java/woowacourse/paint/model/BrushSize.kt @@ -0,0 +1,7 @@ +package woowacourse.paint.model + +data class BrushSize(val width: Float) { + companion object { + const val DEFAULT_SIZE = 50f + } +} diff --git a/app/src/main/java/woowacourse/paint/model/DrawablePath.kt b/app/src/main/java/woowacourse/paint/model/DrawablePath.kt new file mode 100644 index 00000000..f2df58b4 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/model/DrawablePath.kt @@ -0,0 +1,9 @@ +package woowacourse.paint.model + +import android.graphics.Paint +import android.graphics.Path + +data class DrawablePath( + val path: Path, + val paint: Paint, +) diff --git a/app/src/main/java/woowacourse/paint/model/DrawablePathHistory.kt b/app/src/main/java/woowacourse/paint/model/DrawablePathHistory.kt new file mode 100644 index 00000000..11c926a3 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/model/DrawablePathHistory.kt @@ -0,0 +1,12 @@ +package woowacourse.paint.model + +data class DrawablePathHistory( + private val _paths: MutableList = mutableListOf(), +) { + val paths: List + get() = _paths.toList() + + fun add(path: DrawablePath) { + _paths.add(path) + } +} diff --git a/app/src/main/java/woowacourse/paint/model/PaintColor.kt b/app/src/main/java/woowacourse/paint/model/PaintColor.kt new file mode 100644 index 00000000..5625d252 --- /dev/null +++ b/app/src/main/java/woowacourse/paint/model/PaintColor.kt @@ -0,0 +1,22 @@ +package woowacourse.paint.model + +import androidx.annotation.ColorRes +import woowacourse.paint.R + +enum class PaintColor(@ColorRes val colorRes: Int) { + RED(R.color.red), + ORANGE(R.color.orange), + YELLOW(R.color.yellow), + GREEN(R.color.green), + BLUE(R.color.blue), + ; + + companion object { + val DEFAULT_COLOR: PaintColor + get() = values().first() + + fun getColor(index: Int): PaintColor { + return values()[index] + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index dd168261..c556e4ef 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,55 @@ - + xmlns:tools="http://schemas.android.com/tools"> - + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_color.xml b/app/src/main/res/layout/item_color.xml new file mode 100644 index 00000000..9c640a8f --- /dev/null +++ b/app/src/main/res/layout/item_color.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index ca1931bc..9534e728 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,9 @@ #FF018786 #FF000000 #FFFFFFFF + #FF0000 + #FF9800 + #FFEB3B + #4CAF50 + #3F51B5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30b11078..bd55e87e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,6 @@ Paint + + 색상 변경 + 굵기 변경