Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Speedy Development with Kotlin

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Speedy Development with Kotlin

Presetation given at first mobile.lx meetup

Avatar for Bruno Oliveira

Bruno Oliveira

May 19, 2016

More Decks by Bruno Oliveira

Other Decks in Programming

Transcript

  1. A bit of history Architecture — Activities for everything —

    Fragments as a Saviour — Simple Views
  2. A bit of history Language features — Anonymous classes —

    Verbosity — Helpers — Java8 Features — Nullability annotations everywhere — Kotlin + Kotlin android Helpers
  3. A bit of history Architectures — MVC only with activities

    — MVC with fragments — MVP — MVVM with callbacks or ReactiveX
  4. Kotlin Keypoints — Optionals — Default parameters — Data classes

    — Auto generated constructors / Getters and Setters — Lambdas — Extensions
  5. BaseCamp Project details — Activities + Fragments — Retrofit —

    RxJava adapter — GSON converter — AutoParcelable Kotlin
  6. object CrunchBaseService { val builder: CrunchBaseApi private val baseEndpoint =

    "https://api.crunchbase.com/v/3/" init { var logging = HttpLoggingInterceptor() logging.level = HttpLoggingInterceptor.Level.BASIC var client = OkHttpClient() client.interceptors().add(logging) var retrofit = Retrofit.Builder() .baseUrl(baseEndpoint) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(client) .build() builder = retrofit.create(CrunchBaseApi::class.java) } }
  7. Model GSON + Data classes object Model { data class

    ODMOrganizations(var metadata: ODMMetadata, val data: ODMData): AutoParcelable data class ODMMetadata(val version: Int, @SerializedName("www_path_prefix") val wwwPrefix: String, @SerializedName("api_path_prefix") val apiPrefix: String, @SerializedName("image_path_prefix") val imagePrefix: String) : AutoParcelable }
  8. CompanyListFragment LateInitModel class CompanyListFragment(): RxFragment(), LateInitModel<CompanyListFragmentModel> { lateinit var companyPresenter:

    CompanyDetailPresenter lateinit var toolbarManager: ToolbarManager ... private fun initViews() { companyPresenter = activity as CompanyDetailPresenter toolbarManager = activity as ToolbarManager } } ... private fun loadViewModel() { modelReplaceNonNull(getViewModel()) if (!modelIsInited) { model = CompanyListFragmentModel() reloadCompanies() } }
  9. data class CompanyListFragmentModel(private var startingPage: Int = 1): AutoParcelable, PagingViewModel

    { private val _companies = Variable<Array<CompanyItemModel>?>(null) val companies = _companies.asObservable() fun detailModelForIndex(index: Int): CompanyDetailFragmentModel? { _companies.value?.get(index)?.let { return CompanyDetailFragmentModel(it) } return null } ... fun fetchCompanies(): rx.Observable<Unit> { return CrunchBaseService.builder.organizations(page = currentPage) .assignToIO() .map { CompanyItemModel.CompaniesFromResponse(it) } .doOnNext { next -> next?.let { companies -> if (currentPage == 1) { _companies.value = companies } else { _companies.value = _companies.value?.plus(companies) } pagingRequestEnded() } } .toForeground() .map { Unit } } }
  10. class CompanyListFragment(): RxFragment(), LateInitModel<CompanyListFragmentModel> { ... private fun bindViewModel() {

    adapter.subscribe(model.companies) companies_recycler_swipe_to_refresh.setOnRefreshListener { reloadCompanies() } } private fun setupAdapter() { ... companies_recycler.rx_onItemClicked<CompanyViewHolder>() .bindToLifecycle(this) .subscribe { next -> model.detailModelForIndex(next.second)?.let { detailModel -> companyPresenter.showCompany(detailModel, this@CompanyListFragment, next.first.image) } } companies_recycler.rx_onPage(model) .bindToLifecycle(this) .subscribe { page -> companies_recycler_swipe_to_refresh.showBottomRefresh() model.fetchCompanies().subscribe { companies_recycler_swipe_to_refresh.hideRefresh() } } } }
  11. import kotlinx.android.synthetic.main.company_recycler_item.view.* class CompanyViewHolder(private var view: View): RecyclerView.ViewHolder(view) { val

    image = view.company_image val name = view.company_name val twitter = view.company_twitter val cityCountry = view.company_city_country val companyType = view.company_type var viewModel: CompanyItemModel? = null set(value) { value?.let { name.text = it.name it.twitter?.apply { twitter.visibility = View.VISIBLE twitter.text = this } ?: twitter.setVisibility(View.GONE) it.city?.apply { cityCountry.visibility = View.VISIBLE cityCountry.text = this } ?: cityCountry.setVisibility(View.GONE) it.imageUrl?.apply { Glide.with(view.context).load(this).error(R.mipmap.not_found).into(image) } ?: image.setImageResource(R.mipmap.not_found) } ?: resetCell() } }
  12. class MainActivity : RxAppCompatActivity(), CompanyListPresenter, CompanyDetailPresenter { override fun showCompanyList()

    { val fragment = fragmentManager.findFragmentByTag(CompanyListFragment::class.java.name) ?: CompanyListFragment.initNew() fragmentManager .beginTransaction() .setCustomAnimations(android.R.anim.fade_in,android.R.anim.fade_out,android.R.anim.fade_in,android.R.anim.fade_out) .replace(fragmentHolderIdentifier, fragment) .addToBackStack(fragment.javaClass.name) .commit() } override fun showCompany(viewFragmentModelCompany: CompanyDetailFragmentModel, fromFragment: Fragment?, detailImage: ImageView?) { val targetFragment = fragmentManager.findFragmentByTag(CompanyDetailFragmentModel::class.java.name) ?: CompanyDetailFragment.withViewModel(viewFragmentModelCompany) fragmentManager.beginTransaction() .replace(fragmentHolderIdentifier, targetFragment) .addToBackStack(targetFragment.javaClass.name) .commit() } }
  13. Activity Presenter interface interface CompanyDetailPresenter { fun showCompany(viewFragmentModelCompany: CompanyDetailFragmentModel, fromFragment:

    Fragment? = null, detailImage: ImageView? = null) } interface CompanyListPresenter { fun showCompanyList() }