How to use Presenter and ViewHolder? – Android TV application hands on tutorial 3

[Update 2015.11.18]: Revise.

Aim of this chapter

In previous chapter, we looked GridItemPresenter. Its relationship was following.

  • Presenter: GridItemPresenter
  • ViewHolder’s view: TextView
  • CardInfo/Item: String

This was easy example. In this chapter, we proceed to introduce another type of Presenter,

  • Presenter: CardPresenter
  • ViewHolder’s view: ImageCardView
  • CardInfo/Item: Movie class

ImageCardView

ImageCardView class is provided from Android SDK, and it provides a card design layout with main image, title text and content text.

ImageCardView is a subclass of BaseCardView, so it is nice to look BaseCardView class. This is the explanation of BaseCardView,

android.support.v17.leanback.widget
public class BaseCardView
extends android.widget.FrameLayout
A card style layout that responds to certain state changes. It arranges its children in a vertical column, with different regions becoming visible at different times.
A BaseCardView will draw its children based on its type, the region visibilities of the child types, and the state of the widget. A child may be marked as belonging to one of three regions: main, info, or extra. The main region is always visible, while the info and extra regions can be set to display based on the activated or selected state of the View. The card states are set by calling setActivated and setSelected.

BaseCardView itself does not provide specific design layout. So when you want to utilize this, you can make subclass of BaseCardView which have specific design. ImageCardView is one of the class, and currently I could find only ImageCardView class as the subclass of BaseCardView provided by SDK.

In this chapter, we will add this ImageCardView to our code.

Implement CardPresenter, Movie class

I will start by placing necessary files at first. Rightclick on package,

  1. New → class → CardPresenter
  2. New → class → Movie 
  3. For the main image, we use movie.png.
    Copy res/drawable/movie.png from Android TV sample application.
  4. We will use Utility functions provided by Android TV sample application.
    Copy [package name]/Utils class from Android TV sample application to your source code.

First, Utils.java is just copying from AOSP, which will be below.

 

Second, Movie class defines the CardInfo/Item which CardPresenter will present using ImageCardView. It should have the imformation of

  • main image
  • title text
  • content text (studio)

But for the first stage, I only put the information of “title” and “content (studio)”.

Last implementaiton is CardPresenter, it is a subclass of Presenter. CardPresenter owns ViewHolder extended from parent’s Presenter.ViewHolder. This ViewHolder holds ImageCardView which is used to present UI for the Movie item. 

Preparation of data model = Movie and presenter = CardPresenter are done. We can show Movie item by putting the item to adapter.

Build and Run 1

cardpresenter2-2015-07-02-114356 cardpresenter3-2015-07-02-114356

CardPresenter header will appear in the second line, and ImageCardView shows the default card image. The title and content text will appear when you move from header to contents (when items are “onActivated”).

Source code is on github.

 

Updating main image after downloading picture from web using Picasso

Above example shows the default image in the ImageCardView which must be included together with your app (image is static). Sometimes, however, you want to use the image downloading from web so that your application can show updated information.

Picasso image loader library will help us to achieve this easily. Here are the references.

In the CardPresenter class, we want to use picasso library, which can be included by adding a following line in app/build.gradle file.

We will add cardImageUrl member to Movie class, which points a URL for the main image. 

As your tips, getter and setter can be automatically generated by Android studio. In the above modification, you just need to declare cardImageUrl member followed by [Alt]+[Insert] and generate getter and setter. See How to automatically generate getters and setters in Android Studio. We also implement a getImageURI function, to convert URL string to URI format.

CardPresenter takes care of updating image using picasso. This is done by implementing updateCardViewImage function. Picasso makes a source code intuitive to understand for loading, transforming image.

At the last line of updateCardViewImage it calls into(mImageCardViewTarget)  method to load the image to imageview. This target is implemented as follows.

Interface Target is defined in picasso library, it 

represents an arbitrary listener for image loading.

Target interface allows us to implement 3 listener functions.

  • onBitmapLoaded  
    –  Callback when an image has been successfully loaded.
  • onBitmapFailed    
    – Callback when an image has been successfully loaded. linked with error()
  • onPrepareLoad    
    – Callback invoked right before your request is submitted. linked with placeholder()

Remaining task is to specify cardImageUrl from MainFragment, which is done in 

 

At last, you need to add permission to use Internet in AndroidManifest.xml before building.

 

Build and Run 2

picasso-image-download-2015-07-02-162724

 

Now main image is downloaded from the internet. 

Source code is on github.

Customizing ImageCardView, BaseCardView

We can change the design type, and the animation behavior of the card. To begin with, I recommend to refer BaseCardView explanation in source code provided by Android SDK,

A BaseCardView will draw its children based on its type, the region visibilities of the child types, and the state of the widget. A child may be marked as belonging to one of three regions: main, info, or extra. The main region is always visible, while the info and extra regions can be set to display based on the activated or selected state of the View. The card states are set by calling setActivated and setSelected.

In BaseCardView class, you can check the options available to change the design.

  1. public void setCardType(int type)
  2. public void setInfoVisibility(int visibility)
  3. public void setExtraVisibility(int visibility)

setCardType(int type)

You can use following card type as argument

  • CARD_TYPE_MAIN_ONLY
  • CARD_TYPE_INFO_OVER
  • CARD_TYPE_INFO_UNDER
  • CARD_TYPE_INFO_UNDER_WITH_EXTRA

The example with ImageCardView

CARD_TYPE_MAIN_ONLY-2015-07-02

CARD_TYPE_MAIN_ONLY

CARD_TYPE_INFO_OVER-2015-07-02

CARD_TYPE_INFO_OVER

CARD_TYPE_INFO_UNDER-2015-07-02

CARD_TYPE_INFO_UNDER, CARD_TYPE_INFO_UNDER_WITH_EXTRA

You can check the layout of ImageCardView in SDK folder, \sdk\extras\android\support\v17\leanback\res\layout\lb_image_card_view.xml.

ImageCardView has imageView as main region, and title and content text are in info region. The extra region is not set, therefore the behavior is same between CARD_TYPE_INFO_UNDER and CARD_TYPE_INFO_UNDER_WITH_EXTRA.

 

setInfoVisibility(int visibility), setExtraVisibility(int visibility)

You can use following card type as argument

  • CARD_REGION_VISIBLE_ALWAYS
    – the region (title & content text area) will always appear.
  • CARD_REGION_VISIBLE_ACTIVATED
    – the region will not appear when user is selecting header.
       the region will appear when user move to RowsFragment.
  • CARD_REGION_VISIBLE_SELECTED
    – the region will not appear when this card/item is not selected.
       the region will appear only when the card/item is selected.

The more detail explanation of these options can be referred from SDK source code.

Here I changed the setting by modifying onCreateViewHolder in CardPresenter class, 

Source code is on github.

Next chapter, PicassoBackgroundManager – Android TV application hands on tutorial 4, is implementing background image update feature.

Sponsored Links

15 responses

  1. Having a static Context ? Thats seem to be a source of leak to me.

    public ViewHolder(View view) {
    super(view);
    mCardView = (ImageCardView) view;
    mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
    }

    Like the Series.

    – Jens

  2. I have a question:
    is it possible to put 2 different presenter in 1 row? I mean I want to show the “ImagePresenter” for first 10 and “GridItemPresenter” at last presenter for “View All”. if not, what is another better approach for this? can you give me a suggestion ? the “View All” shouldn’t have image on it. thank you in advance

  3. Hi,
    First of all its a really nice tutorial series. Thanks for the same.

    I am kind of stuck on this point when trying to set the image for the card. By default its shows me the default image from the apk rather than a functon which I have written which feteches String to the image on my disk.

    • Hi Nick,
      Thank you for your comment.
      You mean, do you want to load images locally instead of using URL?
      Can you show me a code what you want to do?

  4. Hello,

    First of all thanks for the tutorial. I have a problem in this step. I am trying to updating main image after downloading picture from web using Picasso but it not woks. I see the default image that use in the firs step (movie.png). Can you help me?

Leave a Reply

Your email address will not be published.