Kotlin Android Extensions and Anko

Kotlin Android Extensions and Anko

Presentation done at meetup group KotlinTLV

B11fe994ffca4aaa3da014530981ece3?s=128

Alexander Gherschon

September 25, 2017
Tweet

Transcript

  1. 1.
  2. 5.

    Kotlin Android Extensions (KAE) • Gradle Plugin • Replaces view

    injecting libraries • No more findViewsById() • Be careful
  3. 7.

    KAE Use import kotlinx.android.synthetic.main.fragment_episodes.recyclerView <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> override

    fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(context) }
  4. 8.

    KAE Magic? public View _$_findCachedViewById(int var1) { } private HashMap

    _$_findViewCache; if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { View var10000 = this.getView(); if(var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2;
  5. 9.

    KAE Decompilation public void onViewCreated(@Nullable View view, @Nullable Bundle savedInstanceState)

    { super.onViewCreated(view, savedInstanceState); ((RecyclerView)this._$_findCachedViewById(id.recyclerView)).setHasFixedSize(true); ((RecyclerView)this._$_findCachedViewById(id.recyclerView)) .setLayoutManager((LayoutManager)(new LinearLayoutManager(this.getContext()))); } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(context) } // $FF: synthetic method public void onDestroyView() { super.onDestroyView(); this._$_clearFindViewByIdCache(); }
  6. 10.

    KAE ViewHolder class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item:

    Item, listener: (Item) -> Unit) = with(itemView) { date.text = simpleDateFormatter.format(item.pubDate) name.text = item.title Picasso.with(context) .load(item.image!!.href) .placeholder(ContextCompat.getDrawable(context, R.drawable.placeholder)) .into(image) description.text = item.description duration.text = item.duration?.value ?: "" card.setOnClickListener { listener(item) } } }
  7. 11.

    KAE ViewHolder Decompilation public final void bind(@NotNull Item item, @NotNull

    Function1 listener) { Intrinsics.checkParameterIsNotNull(item, "item"); Intrinsics.checkParameterIsNotNull(listener, "listener"); View var3 = this.itemView; ((TextView)var3.findViewById(id.date)).setText(…); ((TextView)var3.findViewById(id.name)).setText((CharSequence)item.getTitle()); (….) ((CardView)var3.findViewById(id.card)).setOnClickListener(…); }
  8. 12.

    KAE ViewHolder The Right Way™ class ViewHolder(override val containerView: View?)

    : RecyclerView.ViewHolder(containerView), LayoutContainer { fun bind(item: Item, listener: (Item) -> Unit) = with(itemView) { date.text = simpleDateFormatter.format(item.pubDate) name.text = item.title Picasso.with(context) .load(item.image!!.href) .placeholder(ContextCompat.getDrawable(context, R.drawable.placeholder)) .into(image) description.text = item.description duration.text = item.duration?.value ?: "" card.setOnClickListener { listener(item) } } } public interface LayoutContainer { public val containerView: View? }
  9. 13.

    KAE ViewHolder The Right Way™ public View _$_findCachedViewById(int var1) {

    if(this._$_findViewCache == null) { this._$_findViewCache = new HashMap(); } View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1)); if(var2 == null) { View var10000 = this.getContainerView(); if(var10000 == null) { return null; } var2 = var10000.findViewById(var1); this._$_findViewCache.put(Integer.valueOf(var1), var2); } return var2; } private HashMap _$_findViewCache;
  10. 14.

    KAE ViewHolder The Right Way™ public final void bind(@NotNull Item

    item, @NotNull Function1 listener) { Intrinsics.checkParameterIsNotNull(item, "item"); Intrinsics.checkParameterIsNotNull(listener, "listener"); ((TextView)this._$_findCachedViewById(id.date)).setText(…); ((TextView)this._$_findCachedViewById(id.name)).setText((CharSequence)item.getTitle()); (..) ((CardView)this._$_findCachedViewById(id.card)).setOnClickListener(…); } // Same for Custom Views!
  11. 15.
  12. 17.

    Anko Commons - Intents fun startActivity(context: Context) { val intent

    = Intent(context, MainActivity::class.java) intent.putExtra(EXTRA_STATE, "3") context.startActivity(intent) } fun startActivity(context: Context) { context.startActivity<MainActivity>(EXTRA_STATE to 3) } fun startActivity(context: Context) = context.startActivity<MainActivity>(EXTRA_STATE to 3) context.share("url to a new podcast!”)
  13. 18.

    Anko Commons - Dialogs context.alert("Hi, I'm Roy", "Have you tried

    turning it off and on again?") { yesButton { context.toast("Oh…") } noButton {} }.show()
  14. 19.

    Anko Commons - Logging class EpisodesPresenter(private val callback: WeakReference<EpisodesPresenterCallback>): AnkoLogger

    { fun someFunction(item: Item) { info("clicked on $item") warn("This is a warning") } }
  15. 20.

    Anko Commons - Misc // colors 0xff0000.opaque // non-transparent red

    0x99.gray.opaque // non-transparent #999999 gray // Dimensions val dip = dip(20) // 60 val sp = sp(20) // 60 val px2dip = px2dip(100) // 33.333332
  16. 21.

    Anko Layouts - before override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,

    savedInstanceState: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_episodes, container, false) } <ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/viewSwitcher" android:layout_width="match_parent" android:layout_height="match_parent" android:inAnimation="@android:anim/fade_in" android:outAnimation="@android:anim/fade_out" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".fragment.EpisodesFragment"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </ViewSwitcher>
  17. 22.

    Anko Layouts - after override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,

    savedInstanceState: Bundle?): View? { return UI { verticalLayout { padding = dip(30) editText { hint = "Name" textSize = 24f } editText { hint = "Password" textSize = 24f } button("Login") { textSize = 26f } } }.view }
  18. 23.

    Anko SQLite class DatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1)

    { companion object { private var instance: DatabaseOpenHelper? = null @Synchronized fun getInstance(ctx: Context): DatabaseOpenHelper { if (instance == null) { instance = DatabaseOpenHelper(ctx.applicationContext) } return instance!! } } override fun onCreate(db: SQLiteDatabase) { // Here you create tables db.createTable("Customer", false, "id" to INTEGER, "name" to TEXT, "photo" to BLOB) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // Here you can upgrade tables, as usual db.dropTable("Customer", true) } }
  19. 25.

    Anko Coroutines - before object GetRssFeedRequest { fun getFeed(callback: Callback<Rss>)

    { val retrofit = RetrofitUtils.getRetrofit() val service = retrofit.create(RssFeedService::class.java) val feed = service.getFeed() feed.enqueue(callback) } }
  20. 26.

    Anko Coroutines - before fun getFeed(): LiveData<Resource<Rss>> { val data

    = MutableLiveData<Resource<Rss>>() GetRssFeedRequest.getFeed(object : Callback<Rss> { // retrofit override fun onResponse(call: Call<Rss>?, response: Response<Rss>?) { val rss = response?.body() if (rss != null) { data.value = Resource.success(rss) } } override fun onFailure(call: Call<Rss>?, t: Throwable?) { Log.d(TAG, t?.localizedMessage) data.value = Resource.error() } }) return data }
  21. 27.

    Anko Coroutines - after object GetRssFeedRequest { fun getFeed(): Response<Rss>?

    { val retrofit = RetrofitUtils.getRetrofit() val service = retrofit.create(RssFeedService::class.java) val feed = service.getFeed() return feed.execute() } }
  22. 28.

    Anko Coroutines - after fun getFeed(): LiveData<Resource<Rss>> { val data

    = MutableLiveData<Resource<Rss>>() async(UI) { val deferred = bg { GetRssFeedRequest.getFeed() } val response = deferred.await() if (response != null) { if(response.isSuccessful) { data.value = Resource.success(response.body()!!) } else { data.value = Resource.error() } } } return data }
  23. 29.

    Anko Coroutines - after & shorter fun getFeed(): LiveData<Resource<Rss>> {

    val data = MutableLiveData<Resource<Rss>>() async(UI) { val response: Response<Rss>? = bg { GetRssFeedRequest.getFeed() }.await() data.value = if(response?.isSuccessful == true) Resource.success(response.body()!!) else Resource.error() } return data }
  24. 30.

    Anko Coroutines - Activity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main) async(UI) { val deferred: Deferred<Response<Rss>?> = bg { GetRssFeedRequest.getFeed() } val response: Response<Rss>? = deferred.await() if(response != null && response.isSuccessful) { manageRss(response.body()) } } } Any Leak here?
  25. 31.

    Anko Coroutines - Activity TRW override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val ref = asReference() async(UI) { val deferred: Deferred<Response<Rss>?> = bg { GetRssFeedRequest.getFeed() } val response: Response<Rss>? = deferred.await() if(response != null && response.isSuccessful) { ref().manageRss(response.body()) } } } No leaks!