8000 [하티] 뷰 챌린지 미션 1단계 제출합니다. by sujin9 · Pull Request #10 · woowacourse/android-paint · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[하티] 뷰 챌린지 미션 1단계 제출합니다. #10

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

Merged
merged 18 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
# android-paint

## 기능 요구 사항

- [X] 화면에 자유 곡선을 그릴 수 있다.
- [X] 도구 패널을 추가한다.
- [X] 브러시 색상을 변경할 수 있다. (브러시 색상은 빨, 주, 노, 초, 파로 한정)
- [X] 브러시 굵기를 변경할 수 있다.
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
}
}
Expand All @@ -33,6 +33,9 @@ android {
kotlinOptions {
jvmTarget = "11"
}
dataBinding {
enable = true
}
}

dependencies {
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
10000
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
android:theme="@style/Theme.Paint"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
android:name=".main.MainActivity"
android:exported="true"
android:screenOrientation="portrait"
tools:ignore="LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
14 changes: 0 additions & 14 deletions app/src/main/java/woowacourse/paint/MainActivity.kt

This file was deleted.

41 changes: 41 additions & 0 deletions app/src/main/java/woowacourse/paint/main/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
30 changes: 30 additions & 0 deletions app/src/main/java/woowacourse/paint/main/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -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<BrushSize>()
val brushSize: LiveData<BrushSize>
get() = _brushSize

private val _brushColor = MutableLiveData<PaintColor>()
val brushColor: LiveData<PaintColor>
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
}
}
77 changes: 77 additions & 0 deletions app/src/main/java/woowacourse/paint/main/PaintBoard.kt
Original file line number Diff line number Diff line change
@@ -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) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 xml 상에서만 PaintBoard를 사용할 수 있어요! 동적 생성을 하려면 어떻게 해야할까요?

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()
Comment on lines +38 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수화를 아주 잘 해주셨군요 굿굿

else -> return super.onTouchEvent(event)
}
invalidate()
return true
}

private fun setDefaultPaint() {
currentPaint.apply {
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제 그림판에서는 왜이리 삐쭉빼쭉하지 했는데 여기서 힌트를 얻네요!! 감사합니다 하티😉

isAntiAlias = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 친구는 무슨 역할을 해주는 건가요?

Copy link
Author
@sujin9 sujin9 Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설명드릴게요 핑구! 😆 🌼
(열심히 찾아보았답니다 ㅋㅋ)
혹시 틀린 부분이 있거나, 이해가 되지 않는 부분이 있다면 말씀해주세요 ~ 💘


strokeCap

선의 양 끝 선 처리를 해줍니다.

strokeCap = Paint.Cap.ROUND 를 주석처리한 경우에는, 다음과 같이 보여요.

ref.
https://developer.android.com/reference/android/graphics/Paint#setStrokeCap(android.graphics.Paint.Cap)
https://developer.android.com/reference/android/graphics/Paint.Cap

strokeJoin

선의 꼭지점 처리를 해줍니다.

strokeJoin = Paint.Join.ROUND 를 주석처리한 경우에는, 다음과 같이 보여요.

ref.
https://developer.android.com/reference/android/graphics/Paint#setStrokeJoin(android.graphics.Paint.Join)
https://developer.android.com/reference/android/graphics/Paint.Join

isAntiAlias (setAntiAlias)

true로 설정하는 경우, 선 자체에는 영향이 없고, 선의 가장자리를 부드럽게 해줍니다.
주변 배경과 잘 어울리도록 하는 효과가 있다고 해요 ;)

image

ref.
https://developer.android.com/reference/android/graphics/Paint#isAntiAlias()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정성스러운 답변 정말 감사해요..❣️

style = Paint.Style.STROKE
}
}

private fun startDrawing(event: MotionEvent) {
currentPath = Path()
currentPath.moveTo(event.x, event.y)
currentPath.lineTo(event.x, event.y)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moveDrawing()에서 lineTo로 그려주니 시작할 때는 그냥 moveTo만 해줘도 되지 않을까요?

Copy link
Author
@sujin9 sujin9 Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 저는 화면 위로 드래그를 하면 선이 그려지고, 그냥 터치만 하는 경우에는 점이 찍히도록 구현하고자 했어요.
시작할 때도 lineTo를 해주지 않으면, 화면을 그냥 터치했을 때 점이 찍히지 않더라구요 :(
그래서 해당 라인을 추가하게 되었습니다..!

혹시 핑구도 이런 문제를 겪으셨나요?
해결하셨다면, 어떤 방법으로 하셨는지 궁금해요! :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 그런 부분에 대해서는 생각을 못했네요..! 하티 덕분에 알아가는게 많아지네요 감사합니다 ><

}

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)
}
}
26 changes: 26 additions & 0 deletions app/src/main/java/woowacourse/paint/main/adapter/ColorAdapter.kt
Original file line number Diff line number Diff line change
@@ -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<ColorViewHolder>() {
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
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/woowacourse/paint/model/BrushSize.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package woowacourse.paint.model

data class BrushSize(val width: Float) {
companion object {
const val DEFAULT_SIZE = 50f
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/woowacourse/paint/model/DrawablePath.kt
Original file line number Diff line number Diff line change
@@ -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,
)
12 changes: 12 additions & 0 deletions app/src/main/java/woowacourse/paint/model/DrawablePathHistory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package woowacourse.paint.model

data class DrawablePathHistory(
private val _paths: MutableList<DrawablePath> = mutableListOf(),
) {
val paths: List<DrawablePath>
get() = _paths.toList()

fun add(path: DrawablePath) {
_paths.add(path)
}
}
22 changes: 22 additions & 0 deletions app/src/main/java/woowacourse/paint/model/PaintColor.kt
Original file line number Diff line number Diff line change
@@ -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),
Comment on lines +7 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 그냥 ColorRes값이 없는 enum class로 정의해주고 Custom View에서 색상을 지정해주었는데 이렇게 한 곳에서 관리해주는 것이 더 좋은 것 같아요!👍

;

companion object {
val DEFAULT_COLOR: PaintColor
get() = values().first()

fun getColor(index: Int): PaintColor {
return values()[index]
}
}
}
58 changes: 52 additions & 6 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터 바인딩을 사용하는 이유가 있을까요? 여기서는 뷰바인딩만 사용해도 괜찮다고 보입니다

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data binding을 사용할 가능성을 열어두고 싶기도 했고, 습관적으로 적용해준 이유도 없지않아 있는 것 같네요,, 😅
현재는 코드 수정을 통해 data binding을 활용하고 있답니다! :)

xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools">

</androidx.constraintlayout.widget.ConstraintLayout>
<data>

<variable
name="viewModel"
type="woowacourse.paint.main.MainViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<woowacourse.paint.main.PaintBoard
android:id="@+id/ctv_paint_board"
android:layout_width="0dp"
android:layout_height="0dp"
app:brushColor="@{viewModel.brushColor}"
app:brushSize="@{viewModel.brushSize}"
app:layout_constraintBottom_toTopOf="@id/range_slider_brush_size"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.slider.Slider
android:id="@+id/range_slider_brush_size"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="100"
android:value="@{viewModel.brushSize.width}"
app:layout_constraintBottom_toTopOf="@id/rv_colors"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ctv_paint_board" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_colors"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/range_slider_brush_size"
tools:listitem="@layout/item_color" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Loading
0