版权声明:本文为博主原创翻译文章,转载请注明出处。
推荐:
欢迎关注我创建的Android TV 简书专题,会定期给大家分享一些AndroidTv相关的内容:
http://www.jianshu.com/c/37efc6e9799b
一.本章学习目标
在上一章中,我们查看了GridItemPresenter。 其关系如下。
- Presenter:GridItemPresenter
- ViewHolder的视图:TextView
- CardInfo / Item:String
这是很简单的例子。 在本章中,我们继续介绍另一种类型的Presenter,
- Presenter:CardPresenter
- ViewHolderr的视图:ImageCardView
- CardInfo / Item:Movie class
二.ImageCardView
ImageCardView类是Android SDK提供的,它提供了具有主图像,标题文本和内容文本的卡片设计布局。
ImageCardView是BaseCardView的子类,所以开源看BaseCardView这个类。这是对BaseCardView的解释:
android.support.v17.leanback.widget
public class BaseCardView extends android.widget.FrameLayout
响应某些状态更改的卡片样式布局。它的孩子们排列在垂直的列中,不同的地区在不同的时间可见。BaseCardView将根据其类型,子类型的区域可见性以及小部件的状态来绘制其子项。一个孩子可能被标记为属于三个地区之一:主,信息或额外。主区域始终可见,而信息和额外区域可以根据View的激活或选择状态设置为显示。卡状态通过调用setActivated和setSelected来设置。
BaseCardView本身不提供特定的设计布局。所以当你想利用这个,你可以制作具有特定设计的BaseCardView子类。 ImageCardView是其中之一,目前我只能找到ImageCardView类作为SDK提供的BaseCardView子类。
在本章中,我们将把ImageCardView添加到我们的代码中。
三.实现CardPresenter,Movie class
我将首先放置必要的文件。 右键单击包装,
1.New → class → CardPresenter
2.New → class → Movie
3.对于主image,我们使用movie.png。
从Android TV示例应用程序res / drawable / movie.png中复制。
4.我们将使用Android TV示例应用程序提供的实用程序功能。
将[package name] / Utils类从Android TV示例应用程序复制到你的源代码。
首先,Utils.java只是从AOSP复制,这将在下面。
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.corochann.androidtvapptutorial;
import android.content.Context;
import android.graphics.Point;
import android.view.Display;
import android.view.WindowManager;
import android.widget.Toast;
/**
* A collection of utility methods, all static.
*/
public class Utils {
/*
* Making sure public utility methods remain static
*/
private Utils() {
}
/**
* Returns the screen/display size
*/
public static Point getDisplaySize(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size;
}
/**
* Shows a (long) toast
*/
public static void showToast(Context context, String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
/**
* Shows a (long) toast.
*/
public static void showToast(Context context, int resourceId) {
Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_LONG).show();
}
public static int convertDpToPixel(Context ctx, int dp) {
float density = ctx.getResources().getDisplayMetrics().density;
return Math.round((float) dp * density);
}
/**
* Formats time in milliseconds to hh:mm:ss string format.
*/
public static String formatMillis(int millis) {
String result = "";
int hr = millis / 3600000;
millis %= 3600000;
int min = millis / 60000;
millis %= 60000;
int sec = millis / 1000;
if (hr > 0) {
result += hr + ":";
}
if (min >= 0) {
if (min > 9) {
result += min + ":";
} else {
result += "0" + min + ":";
}
}
if (sec > 9) {
result += sec;
} else {
result += "0" + sec;
}
return result;
}
}
接着,Movie类定义CardPresenter将使用ImageCardView显示的CardInfo / Item。 它应该有信息
- main image
- title text
- content text (studio)
但是在第一阶段,我只用“标题”和“内容”信息。
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.corochann.androidtvapptutorial;
import android.util.Log;
import java.net.URI;
import java.net.URISyntaxException;
/**
* Modified from AOSP sample source code, by corochann on 2/7/2015.
* Movie class represents video entity with title, description, image thumbs and video url.
*/
public class Movie {
private static final String TAG = Movie.class.getSimpleName();
static final long serialVersionUID = 727566175075960653L;
private long id;
private String title;
private String studio;
public Movie() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStudio() {
return studio;
}
public void setStudio(String studio) {
this.studio = studio;
}
@Override
public String toString() {
return "Movie{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
最后的实现是CardPresenter,它是Presenter的一个子类。 CardPresenter拥有从Parent的Presenter.ViewHolder扩展的ViewHolder。 该ViewHolder保存ImageCardView,用于显示Movie项目的UI。
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.corochann.androidtvapptutorial;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.Presenter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
/**
* Modified from AOSP sample source code, by corochann on 2/7/2015.
*/
public class CardPresenter extends Presenter {
private static final String TAG = CardPresenter.class.getSimpleName();
private static Context mContext;
private static int CARD_WIDTH = 313;
private static int CARD_HEIGHT = 176;
static class ViewHolder extends Presenter.ViewHolder {
private Movie mMovie;
private ImageCardView mCardView;
private Drawable mDefaultCardImage;
public ViewHolder(View view) {
super(view);
mCardView = (ImageCardView) view;
mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
}
public void setMovie(Movie m) {
mMovie = m;
}
public Movie getMovie() {
return mMovie;
}
public ImageCardView getCardView() {
return mCardView;
}
public Drawable getDefaultCardImage() {
return mDefaultCardImage;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
Log.d(TAG, "onCreateViewHolder");
mContext = parent.getContext();
ImageCardView cardView = new ImageCardView(mContext);
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
cardView.setBackgroundColor(mContext.getResources().getColor(R.color.fastlane_background));
return new ViewHolder(cardView);
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
Movie movie = (Movie) item;
((ViewHolder) viewHolder).setMovie(movie);
Log.d(TAG, "onBindViewHolder");
((ViewHolder) viewHolder).mCardView.setTitleText(movie.getTitle());
((ViewHolder) viewHolder).mCardView.setContentText(movie.getStudio());
((ViewHolder) viewHolder).mCardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
((ViewHolder) viewHolder).mCardView.setMainImage(((ViewHolder) viewHolder).getDefaultCardImage());
}
@Override
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
Log.d(TAG, "onUnbindViewHolder");
}
@Override
public void onViewAttachedToWindow(Presenter.ViewHolder viewHolder) {
// TO DO
}
}
数据模型=Movie和presente= CardPresenter已经完成。 我们可以通过将项目放到适配器上来显示Movie项目。
private void loadRows() {
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
...
/* CardPresenter */
HeaderItem cardPresenterHeader = new HeaderItem(1, "CardPresenter");
CardPresenter cardPresenter = new CardPresenter();
ArrayObjectAdapter cardRowAdapter = new ArrayObjectAdapter(cardPresenter);
for(int i=0; i<10; i++) {
Movie movie = new Movie();
movie.setTitle("title" + i);
movie.setStudio("studio" + i);
cardRowAdapter.add(movie);
}
mRowsAdapter.add(new ListRow(cardPresenterHeader, cardRowAdapter));
...
}
四.第一次运行
CardPresenter标题将显示在第二行,ImageCardView显示默认的卡片图像。 当您从标题移动到内容(项目为“onActivated”)时,标题和内容文本将会出现。
源代码在github上。
使用Picasso从网上下载图片后更新主图
示例显示ImageCardView中必须包含在应用程序中的默认图像(图像是静态的)。 但是,有时希望使用从网页下载的图像,以便你的应用程序可以显示更新的信息。
Picasso图像加载库将帮助我们轻松实现。 以下是参考。
- Picasso
- Android SDK: Working with Picasso
-
How to Use Picasso Image Loader Library in Android
在CardPresenter类中,我们要使用picasso库,可以通过在app / build.gradle文件中添加以下行来包含。
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:recyclerview-v7:22.2.0'
compile 'com.android.support:leanback-v17:22.2.0'
compile 'com.squareup.picasso:picasso:2.3.2'
}
我们将添加cardImageUrl成员到Movie类,它指向主图像的URL。
private String cardImageUrl;
public String getCardImageUrl() {
return cardImageUrl;
}
public void setCardImageUrl(String cardImageUrl) {
this.cardImageUrl = cardImageUrl;
}
getter和setter可以由Android studio自动生成。 在上述修改中,您只需要声明cardImageUrl成员,然后再按[Alt] + [Insert]并生成getter和setter。 我们还实现了一个getImageURI函数,将URL字符串转换为URI格式。
Movie.java
public URI getCardImageURI() {
try {
return new URI(getCardImageUrl());
} catch (URISyntaxException e) {
return null;
}
}
CardPresenter负责使用Picasso更新图像。 这通过实现updateCardViewImage函数来完成。 Picasso使源代码看起来更能直观了解加载和转换图像。
public ViewHolder(View view) {
super(view);
mCardView = (ImageCardView) view;
mImageCardViewTarget = new PicassoImageCardViewTarget(mCardView);
mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
}
...
protected void updateCardViewImage(URI uri) {
Picasso.with(mContext)
.load(uri.toString())
.resize(Utils.convertDpToPixel(mContext, CARD_WIDTH),
Utils.convertDpToPixel(mContext, CARD_HEIGHT))
.error(mDefaultCardImage)
.into(mImageCardViewTarget);
}
}
...
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
Movie movie = (Movie) item;
((ViewHolder) viewHolder).setMovie(movie);
Log.d(TAG, "onBindViewHolder");
if (movie.getCardImageUrl() != null) {
((ViewHolder) viewHolder).mCardView.setTitleText(movie.getTitle());
((ViewHolder) viewHolder).mCardView.setContentText(movie.getStudio());
((ViewHolder) viewHolder).mCardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
((ViewHolder) viewHolder).updateCardViewImage(movie.getCardImageURI());
//((ViewHolder) viewHolder).mCardView.setMainImage(((ViewHolder) viewHolder).getDefaultCardImage());
}
}
在updateCardViewImage的最后一行,它调用(mImageCardViewTarget)方法将图像加载到imageview。 该目标的实现如下。
public static class PicassoImageCardViewTarget implements Target {
private ImageCardView mImageCardView;
public PicassoImageCardViewTarget(ImageCardView imageCardView) {
mImageCardView = imageCardView;
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
Drawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
mImageCardView.setMainImage(bitmapDrawable);
}
@Override
public void onBitmapFailed(Drawable drawable) {
mImageCardView.setMainImage(drawable);
}
@Override
public void onPrepareLoad(Drawable drawable) {
// Do nothing, default_background manager has its own transitions
}
}
界面目标是在picasso库中定义的
represents an arbitrary listener for image loading.
目标界面允许我们实现3个监听器功能。
- onBitmapLoade - 图像成功加载时回调。
- onBitmapFailed - 图像成功加载时回调。 链接错误()
- onPrepareLoad - 在您的请求提交之前调用回调。 与占位符()连接
剩下的任务是从MainFragment指定cardImageUrl,这是在
private void loadRows() {
...
for(int i=0; i<10; i++) {
Movie movie = new Movie();
movie.setCardImageUrl("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/08/DSC02580.jpg");
movie.setTitle("title" + i);
movie.setStudio("studio" + i);
cardRowAdapter.add(movie);
}
...
}
最后,您需要在构建之前添加在AndroidManifest.xml中使用Internet的权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.corochann.androidtvapptutorial" >
<uses-permission android:name="android.permission.INTERNET" />
...
再次运行
现在主图像是从互联网上下载的。
Source code is on github.
自定义ImageCardView,BaseCardView
我们可以改变设计风格和卡的动画行为。 首先,我建议在Android SDK提供的源代码中引用BaseCardView说明,
BaseCardView将根据其类型,子类型的区域可见性以及小部件的状态来绘制其子项。 一个孩子可能被标记为属于三个地区之一: main, info, extra。 main区域始终可见,而info和extra可以根据View的激活或选择状态设置为显示。 Card状态通过调用setActivated和setSelected来设置。
在BaseCardView类中,您可以检查可用于更改设计的选项。
- public void setCardType(int type)
- public void setInfoVisibility(int visibility)
- public void setExtraVisibility(int visibility)
setCardType(int type)
你可以为card设置下面的参数:
- CARD_TYPE_MAIN_ONLY
- CARD_TYPE_INFO_OVER
- CARD_TYPE_INFO_UNDER
-
CARD_TYPE_INFO_UNDER_WITH_EXTRA
ImageCardView的例子:
CARD_TYPE_MAIN_ONLY
CARD_TYPE_INFO_OVER
您可以在SDK文件夹\ sdk \ extras \ android \ support \ v17 \ leanback \ res \ layout \ lb_image_card_view.xml中查看ImageCardView的布局。
ImageCardView具有imageView作为主要区域,标题和内容文本位于信息区域。 没有设置额外的区域,因此CARD_TYPE_INFO_UNDER和CARD_TYPE_INFO_UNDER_WITH_EXTRA之间的行为是相同的。
setInfoVisibility(int visibility), setExtraVisibility(int visibility)
你可以设置以下的参数:
- CARD_REGION_VISIBLE_ALWAYS- 区域(标题和内容文本区域)将始终显示.
- CARD_REGION_VISIBLE_ACTIVATED - 当用户选择页眉时,该区域不会出现。当用户移动到RowsFragment时,该区域将显示。
- CARD_REGION_VISIBLE_SELECTED - 未选择此卡/项目时,该区域不会出现。仅当选择卡/项目时,该区域才会出现。
这些选项的更详细的解释可以参考SDK源代码。
这里我通过修改CardPresenter类中的onCreateViewHolder来改变设置,
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
Log.d(TAG, "onCreateViewHolder");
mContext = parent.getContext();
ImageCardView cardView = new ImageCardView(mContext);
cardView.setCardType(BaseCardView.CARD_TYPE_INFO_UNDER);
cardView.setInfoVisibility(BaseCardView.CARD_REGION_VISIBLE_ALWAYS);
cardView.setFocusable(true);
cardView.setFocusableInTouchMode(true);
cardView.setBackgroundColor(mContext.getResources().getColor(R.color.fastlane_background));
return new ViewHolder(cardView);
}
源码在 github.
下一章,Picasso背景管理 - Android TV应用手册教程四,实现背景图像更新功能。
关注微信公众号,定期为你推荐移动开发相关文章。
网友评论