android 在你的UI中顯示Bitmap - 開發文檔翻譯

由於本人英文能力實在有限,不足之初敬請諒解

本博客只要沒有註明“轉”,那麼均爲原創,轉貼請註明本博客鏈接鏈接



Displaying Bitmaps in Your UI

在你的UI中顯示Bitmap

 

This lesson brings together everything from previous lessons, showing you how to load multiple bitmaps into ViewPager and GridView components using a background thread and bitmap cache, while dealing with concurrency and configuration changes.

這裏彙總了之前課程,展示給你當處理併發和配置改變時,如何使用後臺線程和bitmap緩存加載多圖片到ViewPager和GridView組件中

 

Load Bitmaps into a ViewPager Implementation

加載bitmap到ViewPager的實現

 

The swipe view pattern is an excellent way to navigate the detail view of an image gallery. 

You can implement this pattern using a ViewPager component backed by a PagerAdapter. 

However, a more suitable backing adapter is the subclass FragmentStatePagerAdapter which automatically destroys and saves state of the Fragments in the ViewPager as they disappear off-screen, keeping memory usage down.

swipe view模式是一種極好的方式來導航一個圖片畫廊的詳細視圖

你可以使用ViewPager組件和PagerAdapter實現這個模式

然而,一個更適合的adapter是FragmentStatePagerAdapter,當他們消失在屏幕之外時,它可以在ViewPager中自動銷燬和保存Fragment的狀態,保持內存的低使用率

 

Note: If you have a smaller number of images and are confident they all fit within the application memory limit, then using a regular PagerAdapter or FragmentPagerAdapter might be more appropriate.

注意:如果你有較少的圖片,並且確定他們在應用內存限制之內,那麼使用一個常規的PagerAdapter 或者 FragmentPagerAdapter可能更適合

 

Here’s an implementation of a ViewPager with ImageView children. 

The main activity holds the ViewPager and the adapter:

下面是ViewPager使用ImageView做子view的實現

主activity持有ViewPager和適配器

public class ImageDetailActivity extends FragmentActivity {
    public static final String EXTRA_IMAGE = "extra_image";

    private ImagePagerAdapter mAdapter;
    private ViewPager mPager;

    // A static dataset to back the ViewPager adapter
    public final static Integer[] imageResIds = new Integer[] {
            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_detail_pager); // Contains just a ViewPager

        mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);
    }

    public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
        private final int mSize;

        public ImagePagerAdapter(FragmentManager fm, int size) {
            super(fm);
            mSize = size;
        }

        @Override
        public int getCount() {
            return mSize;
        }

        @Override
        public Fragment getItem(int position) {
            return ImageDetailFragment.newInstance(position);
        }
    }
}

Here is an implementation of the details Fragment which holds the ImageView children. 

This might seem like a perfectly reasonable approach, but can you see the drawbacks of this implementation? How could it be improved?

下面是詳細的Fragment持有ImageView子view的實現

這似乎是完美、合理的手段,但是你能看出這種實現其中的弊端碼?如何改良?

public class ImageDetailFragment extends Fragment {
    private static final String IMAGE_DATA_EXTRA = "resId";
    private int mImageNum;
    private ImageView mImageView;

    static ImageDetailFragment newInstance(int imageNum) {
        final ImageDetailFragment f = new ImageDetailFragment();
        final Bundle args = new Bundle();
        args.putInt(IMAGE_DATA_EXTRA, imageNum);
        f.setArguments(args);
        return f;
    }

    // Empty constructor, required as per Fragment docs
    public ImageDetailFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // image_detail_fragment.xml contains just an ImageView
        final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
        mImageView = (ImageView) v.findViewById(R.id.imageView);
        return v;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final int resId = ImageDetailActivity.imageResIds[mImageNum];
        mImageView.setImageResource(resId); // Load image into ImageView
    }
}

Hopefully you noticed the issue: the images are being read from resources on the UI thread, which can lead to an application hanging and being force closed. 

Using an AsyncTask as described in the Processing Bitmaps Off the UI Thread lesson, it’s straightforward to move image loading and processing to a background thread:

希望你注意到了這件事:圖片是在UI線程中從資源中讀取的,這可導致應用凍屏並且被強制關閉

像Processing Bitmaps Off the UI Thread中那樣使用AsyncTask,直接把圖片加載和處理放到後臺線程中:

public class ImageDetailActivity extends FragmentActivity {
    ...

    public void loadBitmap(int resId, ImageView imageView) {
        mImageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
        task.execute(resId);
    }

    ... // include BitmapWorkerTask class
}

public class ImageDetailFragment extends Fragment {
    ...

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (ImageDetailActivity.class.isInstance(getActivity())) {
            final int resId = ImageDetailActivity.imageResIds[mImageNum];
            // Call out to ImageDetailActivity to load the bitmap in a background thread
            ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
        }
    }
}

Any additional processing (such as resizing or fetching images from the network) can take place in the BitmapWorkerTask without affecting responsiveness of the main UI. 

If the background thread is doing more than just loading an image directly from disk, it can also be beneficial to add a memory and/or disk cache as described in the lesson Caching Bitmaps. 

Here's the additional modifications for a memory cache:

任何額外的處理(比如改變圖片大小、從網絡上獲取圖片)在BitmapWorkerTask發生則不會影響主UI的響應

如果後臺進程不止是直接從磁盤加載圖片,它還可以像Caching Bitmaps中描述的添加一個內存緩存/磁盤緩存也是有益的

public class ImageDetailActivity extends FragmentActivity {
    ...
    private LruCache<String, Bitmap> mMemoryCache;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // initialize LruCache as per Use a Memory Cache section
    }

    public void loadBitmap(int resId, ImageView imageView) {
        final String imageKey = String.valueOf(resId);

        final Bitmap bitmap = mMemoryCache.get(imageKey);
        if (bitmap != null) {
            mImageView.setImageBitmap(bitmap);
        } else {
            mImageView.setImageResource(R.drawable.image_placeholder);
            BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
            task.execute(resId);
        }
    }

    ... // include updated BitmapWorkerTask from Use a Memory Cache section
}

Putting all these pieces together gives you a responsive ViewPager implementation with minimal image loading latency and the ability to do as much or as little background processing on your images as needed.

把所有這些片斷放到一起,你會得到一個響應性良好的,最小圖片加載延遲的ViewPager實現,或多或少的對你的圖片做一些所需的後臺處理



Load Bitmaps into a GridView Implementation

加載bitmap到GridView的實現

 

The grid list building block is useful for showing image data sets and can be implemented using a GridView component in which many images can be on-screen at any one time and many more need to be ready to appear if the user scrolls up or down. 

When implementing this type of control, you must ensure the UI remains fluid, memory usage remains under control and concurrency is handled correctly (due to the way GridView recycles its children views).

grid list building block有助於圖像數據集的顯示,而且可以使用gridview組件實現;用gridview控件,許多圖像可以同時呈現在屏幕上,而且如果用戶上下滾動鼠標,更多圖像可以即時呈現

實現這種控制的時候,你必須保證你的UI保持流暢,內存使用要始終在控制之中並且正確處理併發(取決於GridView回收它的子view的方法)

 

To start with, here is a standard GridView implementation with ImageView children placed inside a Fragment. 

Again, this might seem like a perfectly reasonable approach, but what would make it better?

這是一個標準的在Fragment使用子ImageView的GridView實現

再一次,這似乎是一個完美的合理的方法,但是如何做會更好呢?

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    private ImageAdapter mAdapter;

    // A static dataset to back the GridView adapter
    public final static Integer[] imageResIds = new Integer[] {
            R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
            R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
            R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};

    // Empty constructor as per Fragment docs
    public ImageGridFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAdapter = new ImageAdapter(getActivity());
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
        final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
        mGridView.setAdapter(mAdapter);
        mGridView.setOnItemClickListener(this);
        return v;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
        i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
        startActivity(i);
    }

    private class ImageAdapter extends BaseAdapter {
        private final Context mContext;

        public ImageAdapter(Context context) {
            super();
            mContext = context;
        }

        @Override
        public int getCount() {
            return imageResIds.length;
        }

        @Override
        public Object getItem(int position) {
            return imageResIds[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ImageView imageView;
            if (convertView == null) { // if it's not recycled, initialize some attributes
                imageView = new ImageView(mContext);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setLayoutParams(new GridView.LayoutParams(
                        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            } else {
                imageView = (ImageView) convertView;
            }
            imageView.setImageResource(imageResIds[position]); // Load image into ImageView
            return imageView;
        }
    }
}

Once again, the problem with this implementation is that the image is being set in the UI thread. 

While this may work for small, simple images (due to system resource loading and caching), if any additional processing needs to be done, your UI grinds to a halt.

再一次,這個實現的問題是,圖片是在UI線程中設置的

也許在小且簡單的圖片上面可以正常工作(取決於系統資源加載和緩存),如果有任何其他過程需要完成,你的UI就會慢慢凍住

 

The same asynchronous processing and caching methods from the previous section can be implemented here. 

However, you also need to wary of concurrency issues as the GridView recycles its children views. 

To handle this, use the techniques discussed in the Processing Bitmaps Off the UI Thread lesson. 

Here is the updated solution:

上一章節的相同的異步過程和緩存方法可以在這裏實現

然後,你還是需要小心GridView回收其子view這種併發問題

爲了處理這些問題,可以使用Processing Bitmaps Off the UI Thread章節中討論的技術

下面更新一下解決方案

public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
    ...

    private class ImageAdapter extends BaseAdapter {
        ...

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            ...
            loadBitmap(imageResIds[position], imageView)
            return imageView;
        }
    }

    public void loadBitmap(int resId, ImageView imageView) {
        if (cancelPotentialWork(resId, imageView)) {
            final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
            imageView.setImageDrawable(asyncDrawable);
            task.execute(resId);
        }
    }

    static class AsyncDrawable extends BitmapDrawable {
        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

        public AsyncDrawable(Resources res, Bitmap bitmap,
                BitmapWorkerTask bitmapWorkerTask) {
            super(res, bitmap);
            bitmapWorkerTaskReference =
                new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
        }

        public BitmapWorkerTask getBitmapWorkerTask() {
            return bitmapWorkerTaskReference.get();
        }
    }

    public static boolean cancelPotentialWork(int data, ImageView imageView) {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (bitmapWorkerTask != null) {
            final int bitmapData = bitmapWorkerTask.data;
            if (bitmapData != data) {
                // Cancel previous task
                bitmapWorkerTask.cancel(true);
            } else {
                // The same work is already in progress
                return false;
            }
        }
        // No task associated with the ImageView, or an existing task was cancelled
        return true;
    }

    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
       if (imageView != null) {
           final Drawable drawable = imageView.getDrawable();
           if (drawable instanceof AsyncDrawable) {
               final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
               return asyncDrawable.getBitmapWorkerTask();
           }
        }
        return null;
    }

    ... // include updated BitmapWorkerTask class

Note: The same code can easily be adapted to work with ListView as well.

注意:相同的代碼也可以輕易的適配與ListView工作。

 

This implementation allows for flexibility in how the images are processed and loaded without impeding the smoothness of the UI. 

In the background task you can load images from the network or resize large digital camera photos and the images appear as the tasks finish processing.

這個實現考慮到了圖片如何被處理被加載而不妨礙UI流暢性的靈活性

在後臺任務中,你可以從網絡或者修改大的數碼相機照片大小來加載圖片,當任務結束處理時,圖片就顯示出來。

 

For a full example of this and other concepts discussed in this lesson, please see the included sample application.

在這節中的完整的例子以及其他討論過的概念,請參看樣本應用

 

 

原文地址如下,英文水平實在有限,希望拍磚同時能給予指正。

http://developer.android.com/training/displaying-bitmaps/display-bitmap.html

 

 

轉貼請保留以下鏈接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章