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

Kotlin Android Extensions and Anko

Kotlin Android Extensions and Anko

Presentation done at meetup group KotlinTLV

Alexander Gherschon

September 25, 2017
Tweet

More Decks by Alexander Gherschon

Other Decks in Programming

Transcript

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

    injecting libraries • No more findViewsById() • Be careful
  2. 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) }
  3. 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;
  4. 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(); }
  5. 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) } } }
  6. 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(…); }
  7. 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? }
  8. 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;
  9. 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!
  10. 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!”)
  11. Anko Commons - Dialogs context.alert("Hi, I'm Roy", "Have you tried

    turning it off and on again?") { yesButton { context.toast("Oh…") } noButton {} }.show()
  12. Anko Commons - Logging class EpisodesPresenter(private val callback: WeakReference<EpisodesPresenterCallback>): AnkoLogger

    { fun someFunction(item: Item) { info("clicked on $item") warn("This is a warning") } }
  13. 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
  14. 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>
  15. 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 }
  16. 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) } }
  17. 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) } }
  18. 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 }
  19. 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() } }
  20. 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 }
  21. 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 }
  22. 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?
  23. 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!