// decorview(최상단 레이아웃)을 전체 화면으로 설정
window.setDecorFitsSystemWindows(false)
// status bar의 배경색을 투명하게 만든다.
window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
// guideline을 statusbar와 navigationbar 높이에 맞춰 이동시킨다.
// 필요에 따라 guideline의 위치를 조정해주면 된다.
guidelineTop.setGuidelineBegin(statusBarHeight(this@MainActivity))
// setDecorFitsSysyemWindows로 status뿐만 아니라 navigation까지도 확장되므로.
guidelineBottom.setGuidelineEnd(navigationBarHeight(this@MainActivity))
statusBarHeight와 navigationBarHeight는 이전 글의 topHeight을 참조하면 될 것이다.
<ImageView
android:id="@+id/imageViewDown"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/holo_orange_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<ImageView
android:id="@+id/imageViewUpper"
android:layout_width="70dp"
android:layout_height="70dp"
android:background="@android:color/holo_green_light"
app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline" />
비슷한 역할을 할 수 있는 Barrier의 경우, Guideline이 설정된 end와 begin에 위치하는 것과 달리 기준이 될 뷰에 의해 동적으로 위치가 조정된다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
when (viewType) {
1 -> ...
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when (list[position].type) {
1 -> ...
}
}
// ViewHolder 생성을 맡기자.
class ChattingViewHolderFactory {
companion object {
fun makeViewHolder(type: MessageType, parent: ViewGroup): ViewHolder {
val resId = when (type) { // 메시지 타입에 따른 레이아웃 선택
MessageType.NOTICE -> R.layout.layout_notice
... (여러 종류)
}
val binding: ViewDataBinding =
DataBindingUtil.inflate(LayoutInflater.from(parent.context), resId, parent, false)
return when (type) {
MessageType.NOTICE -> NoticeViewHolder(binding)
... (여러 종류)
}
}
}
}
// ViewHolder 종류별로 다른 bind를 구현해주자.
abstract class ViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
abstract fun bind(chat: Host.Chat) // onBindViewHolder의 동작
}
// Adapter 클래스의 onCreateViewHolder와 onBindViewHolder는 아래와 같게 될 것이다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ChattingViewHolderFactory.makeViewHolder(MessageType.values()[viewType], parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position])
RxJava를 직접 사용해서 ui 이벤트 처리를 할 수도 있지만, RxBinding이라는 잘 알려진 라이브러리가 있다. 링크
프로젝트를 진행하며 사용했던 몇가지 예제를 살펴보자.
RxTextView.textChanges(edittextChattingInput)
.map { it.isNotEmpty() }
.subscribe {
btnChattingSend.setClickability(it)
}.addTo(compositeDisposable)
RxView.clicks(btnProfileTest)
.throttleFirst(1000L, TimeUnit.MILLISECONDS)
.subscribe {
requestUserInfo(uid)
}.addTo(compositeDisposable)
기회가 되면 RxBinding을 직접 뜯어 보는 것도 좋을 것 같다.
// viewmodel
fun postChatting(chat: Host.Chat) {
broadcastRepository.postChatting(chat, broadcastId) // returns Single
.timeout(TIMEOUT, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...)
.addTo(compositeDisposable)
}
// repository
fun postChatting(chat: Chat, broadcastId: String): Completable =
Completable.create { emitter: CompletableEmitter ->
firestoreInstance
... insert할 경로
.add(chat)
.addOnCompleteListener {
if (!it.isSuccessful)
emitter.onError(...)
else
emitter.onComplete()
}
}
그간 학습 및 적용해봤지만 다음 두가지 주제가 빠졌다.
괭장히 중요한 주제로 다음에 따로 다뤄볼 예정이다.