Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

AndroidあるあるLT_20190122.pdf

RyoWATANABE
January 22, 2019
220

 AndroidあるあるLT_20190122.pdf

RyoWATANABE

January 22, 2019
Tweet

Transcript

  1. class LoadingAndErrorView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { private var binding: LoadingAndErrorBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.loading_and_error, this, true) fun setState(uiState: UIState) { binding.uiState = uiState } fun setOnClickRetryListener(onClick: () -> Unit) { binding.onClick = View.OnClickListener { onClick.invoke() } } } sealed class UIState { object Loading : UIState() object Loaded : UIState() object Retry : UIState() }
  2. class LoadingAndErrorView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { private var binding: LoadingAndErrorBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.loading_and_error, this, true) fun setState(uiState: UIState) { binding.uiState = uiState } fun setOnClickRetryListener(onClick: () -> Unit) { binding.onClick = View.OnClickListener { onClick.invoke() } } } sealed class UIState { object Loading : UIState() object Loaded : UIState() object Retry : UIState() }
  3. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState == UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  4. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState == UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState == UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  5. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState instanceof UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  6. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState instanceof UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  7. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState instanceof UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  8. <?xml version="1.0" encoding="utf-8"?> <layout> <data> <variable name="onClick" type=“android.view.View.OnClickListener” /> <variable

    name="uiState" type=“${packageName}.common.UIState” /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <ImageView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <TextView android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <Button android:visibility="@{uiState instanceof UIState.Retry ? View.VISIBLE : View.GONE}" … /> <ProgressBar android:visibility="@{uiState instanceof UIState.Loading ? View.VISIBLE : View.GONE}" … /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  9. <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http: //schemas.android.com/apk/res/android" xmlns:app="http: //schemas.android.com/apk/res-auto"> <data> <variable

    name="viewModel" type=“${packageName}.presentation.demo.DemoViewModel” /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <${packageName}.view.LoadingAndErrorView android:layout_width="match_parent" android:layout_height="match_parent" app:onRetry="@{viewModel ::retry}" app:uiState="@{viewModel.uiState}" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  10. <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http: //schemas.android.com/apk/res/android" xmlns:app="http: //schemas.android.com/apk/res-auto"> <data> <variable

    name="viewModel" type=“${packageName}.presentation.demo.DemoViewModel” /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <${packageName}.view.LoadingAndErrorView android:layout_width="match_parent" android:layout_height="match_parent" app:onRetry="@{viewModel ::retry}" app:uiState="@{viewModel.uiState}" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  11. object LoadingStateViewBindings { @BindingAdapter("app:uiState") @JvmStatic fun setState(loadingAndErrorView: LoadingAndErrorView, uiState: UIState?)

    { loadingState ?.let { loadingAndErrorView.setState(it) } } @BindingAdapter("app:onRetry") @JvmStatic fun setRetry(loadingAndErrorView: LoadingAndErrorView, onClick: () -> Unit) { loadingAndErrorView.setOnClickRetryListener { onClick.invoke() } } }
  12. object LoadingStateViewBindings { @BindingAdapter("app:uiState") @JvmStatic fun setState(loadingAndErrorView: LoadingAndErrorView, uiState: UIState?)

    { loadingState ?.let { loadingAndErrorView.setState(it) } } @BindingAdapter("app:onRetry") @JvmStatic fun setRetry(loadingAndErrorView: LoadingAndErrorView, onClick: () -> Unit) { loadingAndErrorView.setOnClickRetryListener { onClick.invoke() } } }
  13. <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http: //schemas.android.com/apk/res/android" xmlns:app="http: //schemas.android.com/apk/res-auto"> <data> <variable

    name="viewModel" type=“${packageName}.presentation.demo.DemoViewModel” /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <${packageName}.view.LoadingAndErrorView android:layout_width="match_parent" android:layout_height="match_parent" app:onRetry="@{viewModel ::retry}" app:uiState="@{viewModel.uiState}" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
  14. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  15. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  16. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  17. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  18. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: MutableLiveData<String> = MutableLiveData() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  19. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: SingleLiveEvent<String> = SingleLiveEvent() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  20. class DemoViewModel @Inject constructor() : ViewModel(), LifecycleObserver { val uiState:

    MutableLiveData<UIState> = MutableLiveData() val message: SingleLiveEvent<String> = SingleLiveEvent() private val disposables = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { Completable.timer(3, TimeUnit.SECONDS) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { uiState.value = UIState.Loading } .doFinally { uiState.value = UIState.Retry } .subscribeBy( onComplete = { Timber.d("onComplete") } ) .addTo(disposables) } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { disposables.clear() } fun retry() { message.value = "onClick Retry!" } }
  21. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(this, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  22. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(this, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  23. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(this, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  24. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(viewLifecycleOwner, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  25. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(viewLifecycleOwner, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } } onActivityCreatedͳͲͰobserve͍ͯ͠Δͱ FragmentΛdetachͯ͠࠶attach͢ΔͱɺόάΔ͜ͱ͕͋Δ
  26. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(viewLifecycleOwner, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  27. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(this, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }
  28. class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } binding.setLifecycleOwner(viewLifecycleOwner) return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(this, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } } class DemoFragment : BaseFragment() { private lateinit var binding: FragmentDemoBinding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_demo, container, false) binding = FragmentDemoBinding.bind(view).apply { viewModel = (activity as DemoActivity).obtainViewModel().also { subscribe(it) } } binding.setLifecycleOwner(viewLifecycleOwner) return view } private fun subscribe(viewModel: DemoViewModel) { lifecycle.addObserver(viewModel) viewModel.message.observe(viewLifecyclerOwner, Observer { showToast(it) }) } companion object { fun newInstance(): DemoFragment = DemoFragment() } }