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

[feature/calendar2] 커스텀뷰 캘린더 #7

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
10 changes: 0 additions & 10 deletions .idea/runConfigurations.xml

This file was deleted.

38 changes: 19 additions & 19 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.3"

defaultConfig {
Expand All @@ -25,39 +25,39 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility(JavaVersion.VERSION_1_8)
targetCompatibility(JavaVersion.VERSION_1_8)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
sourceCompatibility(JavaVersion.VERSION_1_8)
targetCompatibility(JavaVersion.VERSION_1_8)
sourceCompatibility(JavaVersion.VERSION_11)
targetCompatibility(JavaVersion.VERSION_11)

버전 11로 올리기!

Copy link
Member Author

Choose a reason for hiding this comment

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

왁 큰일날뻔!! 감사형광등~

}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = JavaVersion.VERSION_1_8
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
jvmTarget = JavaVersion.VERSION_1_8
jvmTarget = JavaVersion.VERSION_11.toString()

}
buildFeatures {
viewBinding = true
viewBinding true
}
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha02'
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-rc01'
Copy link
Member

Choose a reason for hiding this comment

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

이거 벌써 rc 나왔구나 코루틴 사용하기 더 편해지겠누

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
def activity_version = "1.2.0"
def fragment_version = "1.3.0"
def activity_version = "1.3.1"
def fragment_version = "1.3.6"
implementation "androidx.activity:activity-ktx:$activity_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
}
7 changes: 6 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CatchMeSample">
<activity android:name="com.teamcatchme.calendar.CalendarActivity">
<activity
android:name="com.teamcatchme.calendar_customview.CustomCalendarActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.teamcatchme.calendar.CalendarActivity">

</activity>
<activity android:name="com.teamcatchme.add_action_seojin.AddActionActivity">

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.teamcatchme.calendar_customview

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.View
import androidx.core.content.res.ResourcesCompat
import com.teamcatchme.catchmesample.R

class CalendarItemView @JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
private val date: Int? = null,
private val catchuList: Array<Int> = arrayOf(),
private val isPrevious: Boolean = false
) :
Comment on lines +11 to +19
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
class CalendarItemView @JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
private val date: Int? = null,
private val catchuList: Array<Int> = arrayOf(),
private val isPrevious: Boolean = false
) :
class CalendarItemView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
private val date: Int? = null,
private val catchuList: Array<Int> = arrayOf(),
private val isPrevious: Boolean = false
) : View(context, attrs, defStyleAttr) {

우선 생성자 형식은 저런데 뷰의 생성자에 저런 인자들이 들어가도 되나 나도 자세히 몰라서 확인해봐야할 듯

Copy link
Member Author

Choose a reason for hiding this comment

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

오키 일단 해보고 우리 리얼 레포에 PR날리기 전까지 나도 확인해보겠음!!
안 돌아가진 않는데 위험할 수는 있을 것 같아

View(context, attrs, defStyleAttr) {
private var paint: Paint = Paint()

private fun drawDateRect(canvas: Canvas, date: Int) {
paint.textSize = 36f
Copy link
Member

Choose a reason for hiding this comment

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

실제에서는 이건 dp로 맞춰줘야할 것 같습니다.

if (isPrevious) paint.color = Color.GRAY
canvas.drawText(
date.toString(),
(width / 2).toFloat(),
((height / 2) - ((paint.descent() + paint.ascent()) / 2)),
paint
)
}

private fun drawDateWithCatchuRect(canvas: Canvas, date: Int, catchuList: Array<Int>) {
paint.textSize = 30f
paint.color = Color.GRAY
val contextResources = context.resources
val catchuDrawable =
ResourcesCompat.getDrawable(contextResources, R.drawable.ic_cachu1, null);
val catchuBitmap = drawableToBitmap(requireNotNull(catchuDrawable))
canvas.drawBitmap(
requireNotNull(catchuBitmap),
(width / 2 - catchuBitmap.width / 2).toFloat(),
0f,
null
)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
canvas.drawBitmap(
requireNotNull(catchuBitmap),
(width / 2 - catchuBitmap.width / 2).toFloat(),
0f,
null
)
runCatching {
canvas.drawBitmap(
requireNotNull(catchuBitmap),
(width / 2 - catchuBitmap.width / 2).toFloat(),
0f,
null
)
}.onFailure { Timber.e(it) }

requireNotNull 같은 단정문이 있으면 익셉션이 터지기 때문에 방어로직을 하나 가져야할 것 같아

canvas.drawText(
date.toString(),
(width / 2).toFloat(),
(catchuBitmap.height + 28).toFloat(),
paint
)
// 누르면 프라그먼트 뿅 하게 리스너 추가하기
}

override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
paint.textAlign = Paint.Align.CENTER
if (canvas == null) return;
if (date != null) {
if (catchuList.isNotEmpty()) drawDateWithCatchuRect(canvas, date, catchuList)
else drawDateRect(canvas, date)
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (date != null) {
if (catchuList.isNotEmpty()) drawDateWithCatchuRect(canvas, date, catchuList)
else drawDateRect(canvas, date)
}
date?.run {
if (catchuList.isNotEmpty()) drawDateWithCatchuRect(canvas, it, catchuList)
else drawDateRect(canvas, it)
}

이런식으로 밀고갈 수 있을 것 같아

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.teamcatchme.calendar_customview

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.core.view.children
import java.util.*

class CalendarView @JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {
fun drawCalendar(dateObject: Date) {
val dateCalendar = Calendar.getInstance()
dateCalendar.time = dateObject
dateCalendar.set(Calendar.DATE, 1)
val year = dateCalendar.get(Calendar.YEAR)
val month = dateCalendar.get(Calendar.MONTH) + 1
val firstDayOfMonth = dateCalendar.get(Calendar.DAY_OF_WEEK) - 1
val lastDateOfMonth = dateCalendar.getActualMaximum(Calendar.DATE)
Comment on lines +23 to +24
Copy link
Member

Choose a reason for hiding this comment

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

변수명 굳

dateCalendar.set(Calendar.MONTH, month - 2)
val lastDateOfLastMonth = dateCalendar.getActualMaximum(Calendar.DATE)
if (firstDayOfMonth != 6) {
for (blank in (lastDateOfLastMonth - firstDayOfMonth)..lastDateOfLastMonth) {
addView(CalendarItemView(context = context, date = blank, isPrevious = true))
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for (blank in (lastDateOfLastMonth - firstDayOfMonth)..lastDateOfLastMonth) {
addView(CalendarItemView(context = context, date = blank, isPrevious = true))
}
((lastDateOfLastMonth - firstDayOfMonth)..lastDateOfLastMonth).forEach { blank ->
addView(CalendarItemView(context = context, date = blank, isPrevious = true))
}

}
for (date in 1..lastDateOfMonth) {
if (date % 2 == 0) addView(
CalendarItemView(
context = context,
date = date,
catchuList = arrayOf(1, 2, 3)
)
)
else addView(CalendarItemView(context = context, date = date))
}
}

override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
val iWidth = (width / 7).toFloat()
val iHeight = (width / 7).toFloat()
var index = 0
children.forEach { view ->
val left = (index % 7) * iWidth
val top = (index / 7) * iHeight
view.layout(left.toInt(), top.toInt(), (left + iWidth).toInt(), (top + iHeight).toInt())
index++
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.teamcatchme.calendar_customview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.teamcatchme.catchmesample.R
import com.teamcatchme.catchmesample.databinding.ActivityCustomCalendarBinding

class CustomCalendarActivity : AppCompatActivity() {
lateinit var binding: ActivityCustomCalendarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCustomCalendarBinding.inflate(layoutInflater)
setContentView(binding.root)
initCalendar()
}

private fun initCalendar() {
val calendarAdapter = CustomCalendarAdapter(this)
binding.viewpagerCustomCalendar.apply {
adapter = calendarAdapter
setCurrentItem(CustomCalendarAdapter.START_POSITION, true)
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.teamcatchme.calendar_customview

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import java.util.*

class CustomCalendarAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {

private var startTime: Calendar = Calendar.getInstance()

override fun getItemCount(): Int = Int.MAX_VALUE

override fun createFragment(position: Int): Fragment {
val millsId = getItemId(position)
return CustomCalendarFragment(millsId)
}

override fun getItemId(position: Int): Long {
val start = startTime.clone() as Calendar
start.add(Calendar.MONTH, position - START_POSITION)
return start.timeInMillis
}

companion object {
const val START_POSITION = Int.MAX_VALUE / 2
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.teamcatchme.calendar_customview

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.teamcatchme.catchmesample.databinding.FragmentCustomCalendarBinding
import java.util.*

class CustomCalendarFragment(private val millsId: Long) : Fragment() {
Copy link
Member

Choose a reason for hiding this comment

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

프래그먼트 생성자는 무조건 빈 걸로 둬야한다고 하네요! 알아두시면 좋을 것 같습니다

그래서 정적 패토리 패턴을 쓰라고 했던거 ㅇㅇ

private var _binding: FragmentCustomCalendarBinding? = null
private val binding: FragmentCustomCalendarBinding
get() = requireNotNull(_binding)

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCustomCalendarBinding.inflate(inflater, container, false)
initView()
return binding.root
}

private fun initView() {
val date = Date(millsId)
binding.text.text = date.toString()
binding.calendarView.drawCalendar(date)
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

}
23 changes: 23 additions & 0 deletions app/src/main/java/com/teamcatchme/calendar_customview/utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.teamcatchme.calendar_customview

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable


fun drawableToBitmap(drawable: Drawable): Bitmap? {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap =
Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
drawable.draw(canvas)
return bitmap
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
fun drawableToBitmap(drawable: Drawable): Bitmap? {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap =
Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
drawable.draw(canvas)
return bitmap
}
fun Drawable.ToBitmap(): Bitmap? {
if (this is BitmapDrawable) {
return bitmap
}
val bitmap =
Bitmap.createBitmap(
intrinsicWidth,
intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
draw(canvas)
return bitmap
}

이런식으로도 변형 가능(이건 그냥 내 스타일이어서 취사 선택 해주시길 바람!)

14 changes: 14 additions & 0 deletions app/src/main/res/layout/activity_custom_calendar.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
tools:context="com.teamcatchme.calendar_customview.CustomCalendarActivity">

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager_custom_calendar"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
18 changes: 18 additions & 0 deletions app/src/main/res/layout/fragment_custom_calendar.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />

<com.teamcatchme.calendar_customview.CalendarView
android:id="@+id/calendar_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/text" />
</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.android.tools.build:gradle:4.2.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0-M1"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down