“Leanback” 就是靠着看的意思。是指以放松的姿势倒在沙发上.谷歌推出 android.support.v17.leanback 软件包提供的 API 支持在电视设备上构建用户界面。它为电视应用提供了一些重要的小部件。这个库只支持到api 17以上的版本,也就是andorid 4.2,而一些效果也只是在api-21以上支持。
这是两个关于比较官方的Demo地址。有需要的可以下载
Leanback 提供了model-view-presenter mvp的方式来构建应用。
List<Movie> list = MovieList.setupMovies();
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
CardPresenter cardPresenter = new CardPresenter();
for (int i = 0; i < NUM_ROWS; i++) {
if (i != 0) {
Collections.shuffle(list);
}
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
for (int j = 0; j < NUM_COLS; j++) {
listRowAdapter.add(list.get(j % 5));
}
HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i]);
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
setAdapter(mRowsAdapter);
一版来说,谷歌的leanback 是(如上图)左边的菜单对应后面的一行。但是其实实际在开发中应用中,是左边的一个菜单对应右边一整个页面。第一种情况基本上上面已经说了。下面来说说第二种情况:
public class MainFragment extends BaseBrowseFragment {
private static final String TAG = "MainFragment";
private static final long HEADER_ID_DISCOVERY = 0;
private static final long HEADER_ID_TV_SHOW = 1;
private static final long HEADER_ID_FAV = 2;
private static final long HEADER_ID_MEDIA = 3;
private static final long HEADER_ID_SETTINGS = 4; // 这些都是对应的左边菜单的id
@Override
protected int getHeaderTitleArrayRes() {
return R.array.main_header_title_array;
}
@Override
protected int getHeaderIconArrayRes() {
return R.array.main_header_icon;
}
@Override
protected FragmentFactory getBrowseFragmentFactory() {
return new PageRowFragmentFactory(mBackgroundManager);
}
protected void onEntranceTransitionEnd() {
Log.d(TAG, "onEntranceTransitionEnd: ");
}
private static class PageRowFragmentFactory extends BrowseFragment.FragmentFactory {
private final BackgroundManager mBackgroundManager;
PageRowFragmentFactory(BackgroundManager backgroundManager) {
this.mBackgroundManager = backgroundManager;
}
@Override
public Fragment createFragment(Object rowObj) {
Row row = (Row) rowObj;
long id = row.getHeaderItem().getId(); //每当点击后就会显示出对应的fragment
mBackgroundManager.setDrawable(null);
if (id == HEADER_ID_TV_SHOW) {
return new TvShowFragment();
} else if (id == HEADER_ID_DISCOVERY) {
return new DiscoveryFragment();
} else if (id == HEADER_ID_MEDIA) {
return new MediaFragment();
}else if (id == HEADER_ID_FAV) {
return new FavoriteFragment();
}
else if (id == HEADER_ID_SETTINGS) {
return new SettingsFragment();
}
throw new IllegalArgumentException(String.format("Invalid row %s", rowObj));
}
}
}
在leanback 中,右边页面显示的种类往往不同,例如,有视频列表,图片列表,音乐列表。那么这些在leanback中都是怎么处理的呢?其实在那些列表中的每一个item都是一个Card。然后其实就是在recycleview 中设置不同类型的item。
通过如下的selector 去选择不同的card
public class CardPresenterSelector extends PresenterSelector {
private final Context mContext;
private final HashMap<Card.Type, Presenter> presenters = new HashMap<Card.Type, Presenter>();
public CardPresenterSelector(Context context) {
mContext = context;
}
@Override
public Presenter getPresenter(Object item) {
if (!(item instanceof Card)) throw new RuntimeException(
String.format("The PresenterSelector only supports data items of type '%s'",
Card.class.getName()));
Card card = (Card) item;
Presenter presenter = presenters.get(card.getCardType());
if (presenter == null) {
switch (card.getCardType()) {
case SINGLE_LINE:
presenter = new SingleLineCardPresenter(mContext);
break;
case VIDEO_GRID:
presenter = new VideoCardViewPresenter(mContext, R.style.VideoGridCardTheme);
break;
case MOVIE:
presenter = new CardPresenter();
break;
case MOVIE_COMPLETE:
/**
* {@link com.smartdevice.multimediaplayer.utils.Constants.ITEM_TYPE_MUSIC}
* add for music which use in search and discovery model ,ordinal = 0
*/
presenter = new MusicCardPresenter(mContext, false);
break;
case MUSIC_SMALL:
presenter = new MusicCardPresenter(mContext, true);
break;
case MOVIE_BASE:
case SQUARE_BIG:
case ICON:
presenter = new GridItemPresenter(mContext);
break;
case GRID_SQUARE:
presenter = new GridItemPresenter(mContext);
break;
case MUSIC_ARTIST:
presenter = new ArtistsCirclePresenter(mContext);
break;
case MUSIC_ALBUM:
presenter = new AlbumCardPresenter(mContext);
break;
case CIRCLE_ICON:
presenter = new CircleIconPresenter(mContext);
break;
case TV_SHOW_CURRENT:
case TV_SHOW_UPCOMING:
case TV_SHOW_RECOMMENDATION:
case TV_SHOW_APP:
case SPOTIFY_SEARCH:
presenter = new TVShowPresenter(mContext);
break;
case POPULAR_MUSIC:
presenter = new PopularMusicPresenter(mContext);
break;
case RECOMMEND_VIDEO:
case CHILD_CHANNEL:
presenter = new RecommendVideoPresenter(mContext);
break;
case FAVOITE_TVSHOW:
presenter = new FavoriteTVShowPresenter(mContext);
break;
case DEVICE:
presenter = new DeviceItemPresenter(mContext);
break;
case SETTINGS:
presenter = new SettingsItemPresenter(mContext);
break;
case LOCALDEVICE:
presenter = new LocalDeviceItemPresenter(mContext);
break;
case DLNA_DEVICE:
presenter = new DlnaDeviceItemPresenter(mContext);
break;
case LOADING_ICON:
presenter = new LoadingCardPresenter(false);
break;
case LOADING_ICON_ERROR:
presenter = new LoadingCardPresenter(true);
break;
case YOUTUBE_VIDEO_ICON:
presenter = new DiscoveryCardViewPresent(mContext);
break;
default:
presenter = new ImageCardViewPresenter(mContext);
break;
}
}
presenters.put(card.getCardType(), presenter);
return presenter;
}
选择好card后,然后对应的card再去往里面填充数据,下面是一种card 类型。
public class MusicCardPresenter extends MusicAbstractCardPresenter<ImageCardView> {
public static final String TAG = MusicCardPresenter.class.getSimpleName();
public MusicCardPresenter(Context context, int cardThemeResId) {
super(new ContextThemeWrapper(context, cardThemeResId));
}
public MusicCardPresenter(Context context, boolean isSmall) {
this(context, isSmall ? R.style.MusicSmallCardStyle : R.style.MusicCardStyle);
}
@Override
protected ImageCardView onCreateView() {
ImageCardView imageCardView = new ImageCardView(getContext());
imageCardView.setFocusable(true);
imageCardView.setFocusableInTouchMode(true);
imageCardView.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP);
return imageCardView;
}
@Override
public void onBindViewHolder(Card card, ImageCardView cardView) {
cardView.setTitleText(card.getTitle());
if (TextUtils.isEmpty(card.getFilePath())) {//spotify music
Glide.with(getContext())
.load(card.getImageUrl())
.into(cardView.getMainImageView()); 这里使用Glide 框架来填充图片
} else {//local music files
mImageFetcher.loadImage(card.getFilePath(), cardView.getMainImageView());
}
}
@Override
public void onUnbindViewHolder(ImageCardView cardView) {
super.onUnbindViewHolder(cardView);
ImageWorker.cancelWork(cardView.getMainImageView());
cardView.setBadgeImage(null);
cardView.setMainImage(null);
}
build.gradle中导入
implementation 'com.android.support:recyclerview-v7:27.1.0'
implementation 'com.android.support:leanback-v17:27.1.0' //主要是这个
implementation 'com.android.support:recommendation:27.1.0'
implementation 'com.android.support:preference-leanback-v17:27.1.0'
然后就可以正常调用了。
感谢:Android TV Leanback 简介