AndroidAndroid TVAndroid TV开发

[译]PicassoBackgroundManager - An

2017-04-21  本文已影响1053人  wenju_song

版权声明:本文为博主原创翻译文章,转载请注明出处。

推荐:
欢迎关注我创建的Android TV 简书专题,会定期给大家分享一些AndroidTv相关的内容:
http://www.jianshu.com/c/37efc6e9799b


picassobackgroundmanager

本篇目标
实现背景图像更新功能。 应用程序没有背景的话显得很无聊,如果它有适当的背景,它变得非常好。
设置背景其实很简单,本示例实现了根据选择的内容动态的去加载背景图。

在实现背景更改之前,先开始介绍onItemSelected回调函数,以便在选择项目时拦截事件的通知。 接下来,我将展示简单的背景更改实现,然后使用picasso实现更好的性能。

setOnItemViewSelectedListener 监听和onItemSelected回调

当选择并单击itemview时,BrowseFragment支持设置侦听器。 当用户移动光标并更改项目的选择时,当前目标将被通知。
为此,我们可以使用setOnItemViewSelectedListener(OnItemViewSelectedListener listener)函数。 在参数中,可以放置应该实现OnItemViewSelectedListener接口的监听器类,该接口也由leanback库提供。 然后,您可以实现onItemSelected回调函数,这是在选择项目时调用的函数。
MainFragmet.java

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        Log.i(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);

        setupUIElements();

        loadRows();

        setupEventListeners();
    }
    
    private void setupEventListeners() {
        setOnItemViewSelectedListener(new ItemViewSelectedListener());
    }
    
    private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
        @Override
        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, 
                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
            // each time the item is selected, code inside here will be executed.
        }
    }

我们将在下面继续实现背景改变功能。 在这里我创建SimpleBackgroundManager和PicassoBackgroundManager来处理背景图像(Android TV示例应用程序正在MainFragment.java中进行)。

SimpleBackgroundManager

官方开发者网站上有解释,请参阅Update the Background以供参考。
我在下面写了一些测试代码。

右键单击package name → New → class→ SimpleBackgroundManager

SimpleBackgroundManager保留了BackgroundManager类的成员mBackgroundManager,它处理实际的背景更改。 BackgroundManager实例是一个可以通过BackgroundManager.getInstance()获得单例实例。

package com.corochann.androidtvapptutorial;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.v17.leanback.app.BackgroundManager;
import android.util.DisplayMetrics;

import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;


public class SimpleBackgroundManager {

    private static final String TAG = SimpleBackgroundManager.class.getSimpleName();

    private final int DEFAULT_BACKGROUND_RES_ID = R.drawable.default_background;
    private static Drawable mDefaultBackground;

    private Activity mActivity;
    private BackgroundManager mBackgroundManager;

    public SimpleBackgroundManager(Activity activity) {
        mActivity = activity;
        mDefaultBackground = activity.getDrawable(DEFAULT_BACKGROUND_RES_ID);
        mBackgroundManager = BackgroundManager.getInstance(activity);
        mBackgroundManager.attach(activity.getWindow());
        activity.getWindowManager().getDefaultDisplay().getMetrics(new DisplayMetrics());
    }

    public void updateBackground(Drawable drawable) {
        mBackgroundManager.setDrawable(drawable);
    }

    public void clearBackground() {
        mBackgroundManager.setDrawable(mDefaultBackground);
    }

}

首先,在构造函数中创建BackgroundManager的实例。 在更新背景之前必须附加到Window,这些初始化在构造函数中完成。

updateBackground方法会改变背景,clearBackground方法会将背景更新为默认图像。 (添加了res / drawable / default_background.xml和更新的res / values / colors.xml。)

这里MainFragment的修改很小。

public class MainFragment extends BrowseFragment {

    ...

    private static SimpleBackgroundManager simpleBackgroundManager = null;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {

        ...

        simpleBackgroundManager = new SimpleBackgroundManager(getActivity());
    }

    private void setupEventListeners() {
        setOnItemViewSelectedListener(new ItemViewSelectedListener());
    }

    private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
        @Override
        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
            // each time the item is selected, code inside here will be executed.
            if (item instanceof String) { // GridItemPresenter row
                simpleBackgroundManager.clearBackground();
            } else if (item instanceof Movie) { // CardPresenter row
                simpleBackgroundManager.updateBackground(getActivity().getDrawable(R.drawable.movie));
            }
        }
    }

编译并运行

我们可以根据行的选择来检查背景是否更新。 当您返回到GridItemPresenter行时,“背景”也将恢复为默认值。


background1

Source code is on github.

PicassoBackgroundManager

我们来改进SimpleBackgroundManager的实现。 我们将会以下改进:
1.延迟更新背景

在以前的实现中,当用户移动光标时,主线程将始终尝试更新背景,并更改选择项目。 它很忙,可能会导致性能不佳。 下面我们将实现TimerTask等待一段时间从更新背景图像。

2.使用Picasso library图像处理
Picasso library是“Android的强大的图像下载和缓存库”。 我们将使用它更容易的图像资源处理。
创建如下的PicassoBackgroundManager,实现如下:

package com.corochann.androidtvapptutorial;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.support.v17.leanback.app.BackgroundManager;
import android.util.DisplayMetrics;
import android.util.Log;

import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;


public class PicassoBackgroundManager {

    private static final String TAG = PicassoBackgroundManager.class.getSimpleName();

    private static int BACKGROUND_UPDATE_DELAY = 500;
    private final int DEFAULT_BACKGROUND_RES_ID = R.drawable.default_background;
    private static Drawable mDefaultBackground;
    // Handler attached with main thread
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    private Activity mActivity;
    private BackgroundManager mBackgroundManager = null;
    private DisplayMetrics mMetrics;
    private URI mBackgroundURI;
    private PicassoBackgroundManagerTarget mBackgroundTarget;

    Timer mBackgroundTimer; // null when no UpdateBackgroundTask is running.

    public PicassoBackgroundManager (Activity activity) {
        mActivity = activity;
        mDefaultBackground = activity.getDrawable(DEFAULT_BACKGROUND_RES_ID);
        mBackgroundManager = BackgroundManager.getInstance(activity);
        mBackgroundManager.attach(activity.getWindow());
        mBackgroundTarget = new PicassoBackgroundManagerTarget(mBackgroundManager);
        mMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(mMetrics);

    }

    /**
     * if UpdateBackgroundTask is already running, cancel this task and start new task.
     */
    private void startBackgroundTimer() {
        if (mBackgroundTimer != null) {
            mBackgroundTimer.cancel();
        }
        mBackgroundTimer = new Timer();
        /* set delay time to reduce too much background image loading process */
        mBackgroundTimer.schedule(new UpdateBackgroundTask(), BACKGROUND_UPDATE_DELAY);
    }


    private class UpdateBackgroundTask extends TimerTask {
        @Override
        public void run() {
            /* Here is TimerTask thread, not UI thread */
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                     /* Here is main (UI) thread */
                    if (mBackgroundURI != null) {
                        updateBackground(mBackgroundURI);
                    }
                }
            });
        }
    }

    public void updateBackgroundWithDelay(String url) {
        try {
            URI uri = new URI(url);
            updateBackgroundWithDelay(uri);
        } catch (URISyntaxException e) {
            /* skip updating background */
            Log.e(TAG, e.toString());
        }
    }

    /**
     * updateBackground with delay
     * delay time is measured in other Timer task thread.
     * @param uri
     */
    public void updateBackgroundWithDelay(URI uri) {
        mBackgroundURI = uri;
        startBackgroundTimer();
    }

    private void updateBackground(URI uri) {
        try {
            Picasso.with(mActivity)
                    .load(uri.toString())
                    .resize(mMetrics.widthPixels, mMetrics.heightPixels)
                    .centerCrop()
                    .error(mDefaultBackground)
                    .into(mBackgroundTarget);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }
    }

    /**
     * Copied from AOSP sample code.
     * Inner class
     * Picasso target for updating default_background images
     */
    public class PicassoBackgroundManagerTarget implements Target {
        BackgroundManager mBackgroundManager;

        public PicassoBackgroundManagerTarget(BackgroundManager backgroundManager) {
            this.mBackgroundManager = backgroundManager;
        }

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
            this.mBackgroundManager.setBitmap(bitmap);
        }

        @Override
        public void onBitmapFailed(Drawable drawable) {
            this.mBackgroundManager.setDrawable(drawable);
        }

        @Override
        public void onPrepareLoad(Drawable drawable) {
            // Do nothing, default_background manager has its own transitions
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            PicassoBackgroundManagerTarget that = (PicassoBackgroundManagerTarget) o;

            if (!mBackgroundManager.equals(that.mBackgroundManager))
                return false;

            return true;
        }

        @Override
        public int hashCode() {
            return mBackgroundManager.hashCode();
        }
    }

    
}

接下来,我们将把MainFragment.java中的SimpleBackgroundManager替换为PicassoBackgroundManager

public class MainFragment extends BrowseFragment {

    ...

    private static PicassoBackgroundManager picassoBackgroundManager = null;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {

        ...

        picassoBackgroundManager = new PicassoBackgroundManager(getActivity());
    }

    private void setupEventListeners() {
        setOnItemViewSelectedListener(new ItemViewSelectedListener());
    }

    private final class ItemViewSelectedListener implements OnItemViewSelectedListener {
        @Override
        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                                   RowPresenter.ViewHolder rowViewHolder, Row row) {
            if (item instanceof String) {                    // GridItemPresenter
                picassoBackgroundManager.updateBackgroundWithDelay("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/10/RIMG0656.jpg");
            } else if (item instanceof Movie) {              // CardPresenter
                picassoBackgroundManager.updateBackgroundWithDelay(((Movie) item).getCardImageUrl());
            }
        }
    }

    ...

    private void loadRows() {
        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());

        ...

        for(int i=0; i<10; i++) {
            Movie movie = new Movie();
            if(i%3 == 0) {
                movie.setCardImageUrl("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/08/DSC02580.jpg");
            } else if (i%3 == 1) {
                movie.setCardImageUrl("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/08/DSC02630.jpg");
            } else {
                movie.setCardImageUrl("http://heimkehrend.raindrop.jp/kl-hacker/wp-content/uploads/2014/08/DSC02529.jpg");
            }
            movie.setTitle("title" + i);
            movie.setStudio("studio" + i);
            cardRowAdapter.add(movie);
        }

编译后运行

由于定时器任务,我们可以在500 ms后检查后台是否更新。 而我们通过使用Picasso从网站获取背景图像。

picassobackgroundmanager

源代码在github上。

到现在为止,我们无法单击item/card。 下一章,DetailsOverviewRowPresenter和FullWidthDetailsOverviewRowPresenter - Android TV应用程序的教程5,是实现一个onClickListener并通过DetailFragment显示内容详细信息。
关注微信公众号,定期为你推荐移动开发相关文章。

songwenju
上一篇下一篇

猜你喜欢

热点阅读