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

[둘리] 1, 2단계 영화 티켓 예매 제출합니다. #7

Merged
merged 71 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
81ab027
docs: 클래스와 액티비티 설계도 작성
whk06061 Apr 11, 2023
545de51
feat: Movie 데이터 클래스 작성
whk06061 Apr 11, 2023
766d546
docs: 설계도 수정
whk06061 Apr 11, 2023
4c79b1a
feat: Price 클래스 구현
whk06061 Apr 11, 2023
4396e2c
feat: TicketingInfo 클래스 구현
whk06061 Apr 11, 2023
282882b
refactor: TicketingInfo name->title로 수정
whk06061 Apr 11, 2023
4690eae
feat: ListView, ListAdapter 구현
whk06061 Apr 11, 2023
2bc5a77
feat: 문자열 상수 @string으로 분할
whk06061 Apr 11, 2023
15cf193
feat: MovieListActivity 구현
whk06061 Apr 11, 2023
c75d5a7
feat: activity_ticketing xml 구현
whk06061 Apr 11, 2023
e38faa0
refactor: xml 더미데이터 삭제
whk06061 Apr 11, 2023
2616c9a
refactor: Price 클래스 value class 로 변경
whk06061 Apr 11, 2023
7c5e91f
feat: TicketingActivity 구현
whk06061 Apr 11, 2023
761850e
feat: activity_movie_ticket xml 구현
whk06061 Apr 11, 2023
013a294
feat: MovieTicketActivity 구현
whk06061 Apr 11, 2023
adf25b7
refactor: activity_movie_ticket.xml 글자 볼드 처리 수정
whk06061 Apr 11, 2023
f50a2ce
refactor: ViewHolder 적용
whk06061 Apr 11, 2023
48a66c5
refactor: 불필요한 MainActivity 삭제
whk06061 Apr 11, 2023
47f12c8
refactor: 상영일, 러닝타임 문자 출력 수정
whk06061 Apr 11, 2023
443e903
refactor: activity_ticketing.xml 본문 스크롤 되도록 수정
whk06061 Apr 12, 2023
e7db664
docs: 클래스 설계도 수정
whk06061 Apr 12, 2023
aa74fa9
feat: PlayingTimes 구현
whk06061 Apr 12, 2023
c05ff0e
refactor: PlayintDate, PlayingTime 따로 받도록 수정
whk06061 Apr 12, 2023
fc093c7
refactor: 직렬화되도록 수정
whk06061 Apr 12, 2023
3f5cc8f
feat: 날짜 시간 선택 spinner 구현
whk06061 Apr 12, 2023
b512714
feat: 할인 정책 구현
whk06061 Apr 12, 2023
6d2199c
feat: 화면 회전 시 데이터 유지 구현
whk06061 Apr 12, 2023
520d74b
refactor: 영화 더미 데이터 가져오는 함수 분리
whk06061 Apr 13, 2023
3ddf00b
refactor: 로직 함수로 분리
whk06061 Apr 13, 2023
fe47ef3
feat: Formatter 구현
whk06061 Apr 13, 2023
5525f7b
feat: Intent getSerializable 확장함수 구현
whk06061 Apr 13, 2023
7dce42c
refactor: MovieListAdapter, MovieTicketActivity 리팩터링
whk06061 Apr 13, 2023
70e7f6b
test: FormatterTest 구현
whk06061 Apr 13, 2023
6b3843c
refactor: TicketingActivity.kt 코드 리팩토링
whk06061 Apr 13, 2023
c881fba
refactor: 패키지 구조 분리 및 액티비티 명 수정
whk06061 Apr 13, 2023
4d17d10
refactor: SpinnerListener, Keys 분리
hyemdooly Apr 14, 2023
10f416c
refactor: DummyData 분리
hyemdooly Apr 14, 2023
303b877
refactor: getString 필요없는 format 함수 사용 삭제
hyemdooly Apr 14, 2023
ed85463
refactor: require 수정
hyemdooly Apr 14, 2023
452126c
refactor: ViewHolder 필드들 nullable인 것 수정
hyemdooly Apr 14, 2023
77afa95
refactor: PlayingTimes 리팩터링
hyemdooly Apr 14, 2023
54b5c30
fix: Exception 발생 버그 수정
hyemdooly Apr 14, 2023
8609c35
refactor: Policies 따로 분할, 범용적으로 사용할 수 있도록 수정
hyemdooly Apr 14, 2023
2e67294
test: DiscountPolicy 클래스 변경으로 인한 테스트 수정 및 추가
hyemdooly Apr 14, 2023
187af84
refactor: Formatter 삭제
hyemdooly Apr 14, 2023
6fc6324
refactor: 모든 layout Linear에서 Constraint로 변경
hyemdooly Apr 14, 2023
a5e2e98
refactor: ktlintFormat 적용
hyemdooly Apr 14, 2023
37cf719
refactor: FormatterTest 삭제
hyemdooly Apr 14, 2023
b8c06a2
refactor: null일시 토스트 띄운 후 뒤로가도록 수정
hyemdooly Apr 15, 2023
e2c4c55
refactor: 확장함수명 변경
hyemdooly Apr 15, 2023
0fe291d
refactor: 패키지 변경 및 뷰 값 세팅 클래스 분리
hyemdooly Apr 15, 2023
dc6ad79
refactor: 패키지 변경
hyemdooly Apr 15, 2023
e062f6f
refactor: MovieDetailActivity, TicketResultActivity 함수 분리
hyemdooly Apr 15, 2023
9864e56
refactor: DummyData 추가, 변수명 변경
hyemdooly Apr 15, 2023
5cca9a1
refactor: MovieDTO 추가
hyemdooly Apr 15, 2023
a45c442
refactor: Key들 위치 변경, Keys object 삭제
hyemdooly Apr 16, 2023
9f8e452
refactor: 확장함수명 변경
hyemdooly Apr 16, 2023
d04bd6c
refactor: else문 삭제
hyemdooly Apr 16, 2023
5336de6
refactor: scope function run 대신 with 사용
hyemdooly Apr 17, 2023
77cd778
refactor: package 이동, MovieDTO 및 Mapper 수정
hyemdooly Apr 17, 2023
1c36e77
refactor: view를 객체 내부에서 찾도록 변경
hyemdooly Apr 17, 2023
186d6f6
refactor: key가 없으면 빈 리스트를 반환하는 Map 확장함수 구현
hyemdooly Apr 17, 2023
b1bffce
refactor: 모듈 분리
hyemdooly Apr 18, 2023
b13a913
refactor: ViewHolders Map 생성, set 함수 이동
hyemdooly Apr 18, 2023
f8c3245
refactor: MovieListItemListener 생성 후 Listener 분할
hyemdooly Apr 18, 2023
ae36175
refactor: getKeyFromIndex Map 확장함수 생성
hyemdooly Apr 18, 2023
f2bfce9
refactor: MovieDTO -> MovieModel 네이밍 변경
hyemdooly Apr 18, 2023
1dc76f1
refactor: View Setting 함수 하나로 합치기
hyemdooly Apr 18, 2023
ac7b7e6
refactor: SpinnerAdapter 함수 분리
hyemdooly Apr 18, 2023
39215c6
refactor: TicketingInfo -> Ticket으로 변경, payment 삭제
hyemdooly Apr 18, 2023
b89051c
refactor: TicketModel 생성, 도메인 객체 의존성 제거
hyemdooly Apr 18, 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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# android-movie-ticket
# android-movie-ticket

## 액티비티
- MovieListActivity
- TicketingActivity
- MovieTicketActivity

## 어댑터
- MoviesAdapter

## 데이터
- Movie
- 이미지, 제목, 상영일(PlayingTimes), 러닝타임, 소개
- TicketingInfo
- 영화 이름, 상영일, 몇명, 가격, 무슨 결제
- Price
- 음수 체크
- 티켓 한 장의 가격은 13000원
- 영화 가격에 할인을 적용한다.
- Discount 구현 클래스를 리스트로 받는다.
- PlayingTimes
- 날짜-상영시간 map 타입으로 가지고 있다.
- Discount (인터페이스)
- calculate 메소드
10 changes: 8 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
android:theme="@style/Theme.Movie"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".activity.ticketresult.TicketResultActivity"
android:exported="false" />
<activity
android:name=".activity.moviedetail.MovieDetailActivity"
android:exported="false" />
<activity
android:name=".activity.movielist.MovieListActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -22,4 +28,4 @@
</activity>
</application>

</manifest>
</manifest>
11 changes: 0 additions & 11 deletions app/src/main/java/woowacourse/movie/MainActivity.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package woowacourse.movie.activity.moviedetail

import android.R
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import woowacourse.movie.model.PlayingTimes
import java.time.LocalDate

class DateSpinnerListener(private val playingTimes: PlayingTimes, private val dates: List<LocalDate>, private val spinnerTime: Spinner) : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>?, view: View?, index: Int, p3: Long) {
val times = playingTimes.times[dates[index]] ?: emptyList()
spinnerTime.adapter = ArrayAdapter(spinnerTime.context, R.layout.simple_spinner_item, times)
}

override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package woowacourse.movie.activity.moviedetail

import android.os.Bundle
import android.view.MenuItem
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import woowacourse.movie.R
import woowacourse.movie.model.MovieDTO
import woowacourse.movie.util.Keys
import woowacourse.movie.util.getVersionDependentSerializableExtra

class MovieDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_detail)
val movieDTO: MovieDTO? = intent.getVersionDependentSerializableExtra(Keys.MOVIE_KEY)
if (movieDTO != null) {
initMovieDetailView(movieDTO)
initReservationInfoView(savedInstanceState, movieDTO)
} else {
Toast.makeText(this, DATA_LOADING_ERROR_MESSAGE, Toast.LENGTH_LONG).show()
finish()
}

Choose a reason for hiding this comment

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

else문을 사용하지말라고 배웠는데 MovieDetailActivity에서처럼 null처리를 위한 else문도 사용해서는 안될까요?
if(movie == null)로 토스트를 띄우고 finish를 하면 되겠지만, 뒤에 init문에서 movie뒤에 ?를 붙여주고싶지 않아서 else문을 작성했습니다!

개발에 "사용해선 안된다"는 사실 존재 하지 않아요. 코드가 어떻든 보이기는 동일하게 보일 수 있으니까요.
객체지향 생활체조 연습을 위해 제약을 둔 것 뿐이랍니다.
그렇기에 꼭 연습해보세요 :)
레벨 1때에 else 구문을 어떻게 지웠는지 상기해보며 작성해볼까요?

Copy link
Author

Choose a reason for hiding this comment

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

이제서야 return이 생각나서 else문을 삭제했습니다 ㅎㅎ

supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

private fun initMovieDetailView(movieDTO: MovieDTO) {
MovieDetailViewInitializer(movieDTO).run {

Choose a reason for hiding this comment

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

run 스코프 함수를 선택한 이유가 무엇인가요?
다른 스코프 함수들과 어떻게 다를까요?

Copy link
Author

@hyemdooly hyemdooly Apr 17, 2023

Choose a reason for hiding this comment

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

scope function를 살펴보면 두 가지 기준으로 차이점이 나뉩니다.

  1. 객체 reference를 호출하는 방법이 it인가, this인가 : it - let, also | this - run, with, apply
  2. 반환값이 Lambda의 결과인가, 객체 reference인가 : Lambda result - let, run, with | 객체 reference - apply, also

사실 run을 선택한 이유는 뜻 그대로 실행한다는 의미로 사용했는데요..!
위 기준에서 서로 겹치는 run과 with에 대해 좀 더 살펴보았습니다.
run과 with의 가장 큰 차이점은 (지금 코드에서 사용한 run의 경우) run은 확장함수이나 with는 아니라는 점, 그리고 run은 nullable 변수에서 사용 가능하나 with에서는 그렇지 않다는 점입니다.
with는 "이미 생성된" 객체에 대해 코드 중복을 줄이고 싶을 때, run은 객체 생성 후 "객체 저장 없이" 원하는 값만 받아오고 싶을 때도 사용할 수 있습니다.
따라서, 현재 코드에서는 객체가 nullable이 아니며, return값이 필요 없고 한 객체에 대해 여러 함수를 사용하려고 하고 있기 때문에 의미적으로 with가 보기 좋아보입니다!

Copy link

@malibinYun malibinYun Apr 17, 2023

Choose a reason for hiding this comment

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

좋은 공부가 되었기를 바라요 :)

조금 추가하면,
with는 사실 나머지 4개와는 다르게 확장함수가 아니랍니다.
그래서 run과의 비교군으로는 잘 맞지 않아요.

사실 run을 선택한 이유는 뜻 그대로 실행한다는 의미로 사용했는데요..!

저도 공감해요.
다만, run의 반환 값은 Lambda의 결과 라는 것이에요.
그렇기에 이 반환 값을 사용하지 않으므로, 적절하지 않다고 생각해요.

저는 이런 경우, 수신 객체를 가지고 또 무언가를 수행하니, also를 주로 활용해요.
확장 함수의 결과가 변환되지도 않기에 활용해요.

확장 함수에 대한 비교로 말씀 드렸는데, 둘리 의견 처럼 with가 더 적절해 보이네요 :)

제 의견은 정답이 아니니, 이런 의견도 있다 정도로 생각해주시면 좋겠어요 😊


다만, 이러한 일들이 정말 스코프 함수가 필요했는 지 더 고민해보면 좋겠어요.
정말 외부에서 일일이 MovieDetailViewInitializer 객체의 메서드를 호출해주어야할까요?

Copy link
Author

Choose a reason for hiding this comment

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

일일히 호출하기 보다 생성자에서 Movie 데이터를 받아서 한번에 처리할 수 있을 것 같습니다. :)

그리고 한 가지 더 질문드리고 싶은 부분이 있습니다!

inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

with를 찾아볼 때 블로그에 반환값이 없다라는 말을 자주 봤는데요!
with의 정의를 보면 반환 타입이 block의 결과인데... 반환은 있는게 맞지만, 이 반환값을 어딘가에 저장해서 사용하지는 않는다는 의미인건가요?

Choose a reason for hiding this comment

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

with의 정의를 보면 반환 타입이 block의 결과인데...

정확히 짚으셨네요!
저는 사실 한글로 된 블로그에 그리 신뢰도가 높지 않아요.
생각보다 다른 블로그의 글을 그대로 옮겨다가 적는 경우가 꽤 많아요.
그렇기에 잘못된 정보도 그대로 옮기는 경우를 많이 보았어요.

저는 둘리가 건강한 검색을 하고 있다고 생각해요.
믿을 건 영어 공식 문서와 실제 코드입니다 🙂

initImageView(findViewById(R.id.img_movie))
initTitle(findViewById(R.id.text_title))
initPlayingDate(findViewById(R.id.text_playing_date))
initRunningTime(findViewById(R.id.text_running_time))
initDescription(findViewById(R.id.text_description))
}
}

private fun initReservationInfoView(savedInstanceState: Bundle?, movieDTO: MovieDTO) {
val savedCount = savedInstanceState?.getInt(Keys.COUNT_KEY) ?: DEFAULT_COUNT
val savedDate = savedInstanceState?.getInt(Keys.SPINNER_DATE_KEY) ?: DEFAULT_POSITION
val savedTime = savedInstanceState?.getInt(Keys.SPINNER_TIME_KEY) ?: DEFAULT_POSITION

ReservationInfoViewInitializer(movieDTO).run {
initCount(savedCount, findViewById(R.id.text_count))
initMinusButton(findViewById(R.id.btn_minus), findViewById(R.id.text_count))
initPlusButton(findViewById(R.id.btn_plus), findViewById(R.id.text_count))
initReserveButton(
findViewById(R.id.btn_reserve),
findViewById(R.id.text_count),
findViewById(R.id.spinner_date),
findViewById(R.id.spinner_time)
)
initDateSpinner(
savedDate,
findViewById(R.id.spinner_date),
findViewById(R.id.spinner_time)
)
initTimeSpinner(
savedTime,
findViewById(R.id.spinner_date),
findViewById(R.id.spinner_time)
)
}

Choose a reason for hiding this comment

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

객체 내부에서 뷰를 찾게 해보는 것은 어떨까요?

}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
finish()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val countText = findViewById<TextView>(R.id.text_count)
val spinnerDate = findViewById<Spinner>(R.id.spinner_date)
val spinnerTime = findViewById<Spinner>(R.id.spinner_time)
outState.putInt(Keys.COUNT_KEY, countText.text.toString().toInt())
outState.putInt(Keys.SPINNER_DATE_KEY, spinnerDate.selectedItemPosition)
outState.putInt(Keys.SPINNER_TIME_KEY, spinnerTime.selectedItemPosition)
}

companion object {
private const val DEFAULT_COUNT = 1
private const val DEFAULT_POSITION = 0
private const val DATA_LOADING_ERROR_MESSAGE = "데이터가 로딩되지 않았습니다. 다시 시도해주세요."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package woowacourse.movie.activity.moviedetail

import android.widget.ImageView
import android.widget.TextView
import woowacourse.movie.R
import woowacourse.movie.model.MovieDTO
import java.time.format.DateTimeFormatter

class MovieDetailViewInitializer(private val movieDTO: MovieDTO) {

Choose a reason for hiding this comment

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

영화 상세에 대한 View를 초기화만 하는 객체를 만들고 싶었던 것으로 보여요.
정말 초기화만 담당한다면, 이후 View에 대한 수정이나 인터렉션등이 추가되면, 어떤 변경점이 생겨날까요?

MovieDetailView
를 만든다고 생각해보고 만들어보세요 :)

fun initDescription(textView: TextView) {
textView.text = movieDTO.description
}

fun initRunningTime(textView: TextView) {
textView.text = textView.context.getString(R.string.running_time, movieDTO.runningTime)
}

fun initPlayingDate(textView: TextView) {
textView.text = textView.context.getString(
R.string.playing_time,
DateTimeFormatter.ofPattern(textView.context.getString(R.string.date_format)).format(movieDTO.playingTimes.startDate),
DateTimeFormatter.ofPattern(textView.context.getString(R.string.date_format)).format(movieDTO.playingTimes.endDate)
)
}

fun initTitle(textView: TextView) {
textView.text = movieDTO.title
}

fun initImageView(imageView: ImageView) {
imageView.setImageResource(movieDTO.image)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package woowacourse.movie.activity.moviedetail

import android.content.Intent
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
import woowacourse.movie.activity.ticketresult.TicketResultActivity
import woowacourse.movie.model.MovieDTO
import woowacourse.movie.model.Price
import woowacourse.movie.model.TicketingInfo
import woowacourse.movie.util.DiscountPolicies
import woowacourse.movie.util.Keys
import java.time.LocalDate
import java.time.LocalTime

class ReservationInfoViewInitializer(private val movieDTO: MovieDTO) {
fun initReserveButton(reserveButton: Button, countView: TextView, dateSpinner: Spinner, timeSpinner: Spinner) {
reserveButton.setOnClickListener {
val intent = Intent(it.context, TicketResultActivity::class.java)
val ticketingInfo = TicketingInfo.of(
DiscountPolicies.policies,
movieDTO.title,
dateSpinner.selectedItem as LocalDate,
timeSpinner.selectedItem as LocalTime,
countView.text.toString().toInt(),
Price(),
"현장"
)
intent.putExtra(Keys.INFO_KEY, ticketingInfo)
it.context.startActivity(intent)
}
}

fun initTimeSpinner(savedTimePosition: Int, dateSpinner: Spinner, timeSpinner: Spinner) {
val times = movieDTO.playingTimes.times[dateSpinner.selectedItem] ?: emptyList()

Choose a reason for hiding this comment

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

playingTimes의 참조가 길어지는 이러한 형태가 많네요.
매번 호출하는 곳에서 emptyList() 할당이 반복되네요. 줄일 수 있는 방법이 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

확장함수를 구현하여 코드의 길이를 줄였습니다!

timeSpinner.adapter =
ArrayAdapter(timeSpinner.context, android.R.layout.simple_spinner_item, times).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}

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.

함수로 분리하였습니다! :)

timeSpinner.setSelection(savedTimePosition)
}

fun initDateSpinner(savedDatePosition: Int, dateSpinner: Spinner, timeSpinner: Spinner) {
val dates = movieDTO.playingTimes.times.keys.sorted()
dateSpinner.adapter =
ArrayAdapter(dateSpinner.context, android.R.layout.simple_spinner_item, dates).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
dateSpinner.setSelection(savedDatePosition, false)
dateSpinner.onItemSelectedListener = DateSpinnerListener(movieDTO.playingTimes, dates, timeSpinner)
}

fun initMinusButton(minusButton: Button, countView: TextView) {
minusButton.setOnClickListener {
val count = countView.text.toString().toInt()
if (count > 1) countView.text = (count - 1).toString()
}
}

fun initPlusButton(plusButton: Button, countView: TextView) {
plusButton.setOnClickListener {
val count = countView.text.toString().toInt()
countView.text = (count + 1).toString()
}
}

fun initCount(savedCount: Int, countView: TextView) {
countView.text = savedCount.toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package woowacourse.movie.activity.movielist

import android.os.Bundle
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import woowacourse.movie.R
import woowacourse.movie.util.DummyData

class MovieListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_list)

val listView = findViewById<ListView>(R.id.list_view)
val adapter = MovieListAdapter(DummyData.movieDTOS)
listView.adapter = adapter
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package woowacourse.movie.activity.movielist

import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import woowacourse.movie.R
import woowacourse.movie.activity.moviedetail.MovieDetailActivity
import woowacourse.movie.model.MovieDTO
import woowacourse.movie.util.Keys
import java.time.format.DateTimeFormatter

class MovieListAdapter(private val movieDTOS: List<MovieDTO>) : BaseAdapter() {
override fun getCount(): Int {
return movieDTOS.size
}

override fun getItem(position: Int): Any {
return movieDTOS[position]
}

override fun getItemId(position: Int): Long {
return position.toLong()
}

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view = convertView ?: LayoutInflater.from(parent?.context).inflate(R.layout.movie_item, null)
if (convertView == null) view.tag = getViewHolder(view)

val holder = view.tag as ViewHolder
val movieDTO = getItem(position) as MovieDTO
setViewHolder(holder, movieDTO, parent?.context) {
val intent = Intent(view.context, MovieDetailActivity::class.java)
intent.putExtra(Keys.MOVIE_KEY, movieDTO)
view.context.startActivity(intent)
}
return view
}

private fun getViewHolder(view: View): ViewHolder = ViewHolder(
view.findViewById(R.id.img_movie),
view.findViewById(R.id.text_title),
view.findViewById(R.id.text_playing_date),
view.findViewById(R.id.text_running_time),
view.findViewById(R.id.btn_reserve)
)

private fun setViewHolder(holder: ViewHolder, movieDTO: MovieDTO, context: Context?, clickListener: OnClickListener) {
holder.image.setImageResource(movieDTO.image)
holder.title.text = movieDTO.title
holder.playingDate.text = context?.getString(
R.string.playing_time,
DateTimeFormatter.ofPattern(context.getString(R.string.date_format)).format(movieDTO.playingTimes.startDate),
DateTimeFormatter.ofPattern(context.getString(R.string.date_format)).format(movieDTO.playingTimes.endDate)
)
holder.runningTime.text = context?.getString(R.string.running_time, movieDTO.runningTime)
holder.reserveButton.setOnClickListener(clickListener)
}

Choose a reason for hiding this comment

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

이 일은 adapter의 일일까요?

Copy link
Author

@hyemdooly hyemdooly Apr 18, 2023

Choose a reason for hiding this comment

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

ViewHolder가 일을 직접하는 것이 좋을 것 같아 ViewHolder 객체 안으로 세팅 함수를 옮겼습니다!


private class ViewHolder constructor(
val image: ImageView,
val title: TextView,
val playingDate: TextView,
val runningTime: TextView,
val reserveButton: Button
)
}
Loading