-
Notifications
You must be signed in to change notification settings - Fork 49
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
Changes from 55 commits
81ab027
545de51
766d546
4c79b1a
4396e2c
282882b
4690eae
2bc5a77
15cf193
c75d5a7
e38faa0
2616c9a
7c5e91f
761850e
013a294
adf25b7
f50a2ce
48a66c5
47f12c8
443e903
e7db664
aa74fa9
c05ff0e
fc093c7
3f5cc8f
b512714
6d2199c
520d74b
3ddf00b
fe47ef3
5525f7b
7dce42c
70e7f6b
6b3843c
c881fba
4d17d10
10f416c
303b877
ed85463
452126c
77afa95
54b5c30
8609c35
2e67294
187af84
6fc6324
a5e2e98
37cf719
b8c06a2
e2c4c55
0fe291d
dc6ad79
e062f6f
9864e56
5cca9a1
a45c442
9f8e452
d04bd6c
5336de6
77cd778
1c36e77
186d6f6
b1bffce
b13a913
f8c3245
ae36175
f2bfce9
1dc76f1
ac7b7e6
39215c6
b89051c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 메소드 |
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() | ||
} | ||
supportActionBar?.setDisplayHomeAsUpEnabled(true) | ||
} | ||
|
||
private fun initMovieDetailView(movieDTO: MovieDTO) { | ||
MovieDetailViewInitializer(movieDTO).run { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. run 스코프 함수를 선택한 이유가 무엇인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. scope function를 살펴보면 두 가지 기준으로 차이점이 나뉩니다.
사실 run을 선택한 이유는 뜻 그대로 실행한다는 의미로 사용했는데요..! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 공부가 되었기를 바라요 :) 조금 추가하면,
저도 공감해요. 저는 이런 경우, 수신 객체를 가지고 또 무언가를 수행하니, 확장 함수에 대한 비교로 말씀 드렸는데, 둘리 의견 처럼 with가 더 적절해 보이네요 :) 제 의견은 정답이 아니니, 이런 의견도 있다 정도로 생각해주시면 좋겠어요 😊 다만, 이러한 일들이 정말 스코프 함수가 필요했는 지 더 고민해보면 좋겠어요. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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를 찾아볼 때 블로그에 반환값이 없다라는 말을 자주 봤는데요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
정확히 짚으셨네요! 저는 둘리가 건강한 검색을 하고 있다고 생각해요. |
||
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) | ||
) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 영화 상세에 대한 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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. playingTimes의 참조가 길어지는 이러한 형태가 많네요. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 동일한 형태의 생성 코드가 많아보여요. 중복을 줄여보는 방법에 대해 고민해보아요 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 일은 adapter의 일일까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
개발에 "사용해선 안된다"는 사실 존재 하지 않아요. 코드가 어떻든 보이기는 동일하게 보일 수 있으니까요.
객체지향 생활체조 연습을 위해 제약을 둔 것 뿐이랍니다.
그렇기에 꼭 연습해보세요 :)
레벨 1때에 else 구문을 어떻게 지웠는지 상기해보며 작성해볼까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이제서야
return
이 생각나서 else문을 삭제했습니다 ㅎㅎ