Displaying Bitmaps in Your UI

来源:互联网 发布:编程猫的网址 编辑:程序博客网 时间:2024/05/19 02:45

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

Load Bitmaps into a ViewPager Implementation


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 aPagerAdapter. However, a more suitable backing adapter is the subclass FragmentStatePagerAdapter which automatically destroys and saves state of the Fragmentsin the ViewPager as they disappear off-screen, keeping memory usage down.

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 orFragmentPagerAdapter might be more appropriate.

Here’s an implementation of a ViewPager with ImageView children. The main activity holds theViewPager and the adapter:

译:

这一课会演示如何运用前面几节课的内容,使用后台线程与Cache机制来加载图片到 ViewPager 与 GridView 组件,并且学习处理并发与配置改变问题。

实现加载图片到ViewPager(Load Bitmaps into a ViewPager Implementation)

swipe view pattern是一个用来切换显示不同详情界面的很好的方法。(关于这种效果请先参看Android Design: Swipe Views).

你可以通过 PagerAdapter 与 ViewPager 组件来实现这个效果。 然而,一个更加合适的Adapter是PagerAdapter 的子类 FragmentStatePagerAdapter:它可以在某个ViewPager中的子视图切换出屏幕时自动销毁与保存 Fragments 的状态。这样能够保持消耗更少的内存。

Note: 如果你只有为数不多的图片并且确保不会超出程序内存限制,那么使用 PagerAdapter 或 FragmentPagerAdapter 会更加合适。

下面是一个使用ViewPager与ImageView作为子视图的示例。主Activity包含有ViewPager 和adapter。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
publicclass ImageDetailActivity extends FragmentActivity {
publicstatic final String EXTRA_IMAGE = "extra_image";
 
privateImagePagerAdapter mAdapter;
privateViewPager mPager;
 
// A static dataset to back the ViewPager adapter
publicfinal static Integer[] imageResIds = newInteger[] {
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
publicvoid 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);
}
 
publicstatic class ImagePagerAdapter extendsFragmentStatePagerAdapter {
privatefinal int mSize;
 
publicImagePagerAdapter(FragmentManager fm, intsize) {
super(fm);
mSize = size;
}
 
@Override
publicint getCount() {
returnmSize;
}
 
@Override
publicFragment getItem(intposition) {
returnImageDetailFragment.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?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
publicclass ImageDetailFragment extends Fragment {
privatestatic final String IMAGE_DATA_EXTRA = "resId";
privateint mImageNum;
privateImageView mImageView;
 
staticImageDetailFragment newInstance(intimageNum) {
finalImageDetailFragment f = newImageDetailFragment();
finalBundle args = newBundle();
args.putInt(IMAGE_DATA_EXTRA, imageNum);
f.setArguments(args);
returnf;
}
 
// Empty constructor, required as per Fragment docs
publicImageDetailFragment() {}
 
@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageNum = getArguments() !=null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
}
 
@Override
publicView onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// image_detail_fragment.xml contains just an ImageView
finalView v = inflater.inflate(R.layout.image_detail_fragment, container,false);
mImageView = (ImageView) v.findViewById(R.id.imageView);
returnv;
}
 
@Override
publicvoid onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
finalint resId = ImageDetailActivity.imageResIds[mImageNum];
<strong>mImageView.setImageResource(resId);</strong>// 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 theProcessing Bitmaps Off the UI Thread lesson, it’s straightforward to move image loading and processing to a background thread:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
publicclass ImageDetailActivity extends FragmentActivity {
...
 
publicvoid loadBitmap(int resId, ImageView imageView) {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task =new BitmapWorkerTask(mImageView);
task.execute(resId);
}
 
...// include <a href="file:///D:/Users/Administrator/AppData/Local/Android/sdk/docs/training/displaying-bitmaps/process-bitmap.html#BitmapWorkerTask"><code>BitmapWorkerTask</code></a> class
}
 
publicclass ImageDetailFragment extends Fragment {
...
 
@Override
publicvoid onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(ImageDetailActivity.class.isInstance(getActivity())) {
finalint 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
publicclass ImageDetailActivity extends FragmentActivity {
...
privateLruCache<String, Bitmap> mMemoryCache;
 
@Override
publicvoid onCreate(Bundle savedInstanceState) {
...
// initialize LruCache as per <a href="file:///D:/Users/Administrator/AppData/Local/Android/sdk/docs/training/displaying-bitmaps/cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
}
 
publicvoid loadBitmap(int resId, ImageView imageView) {
finalString imageKey = String.valueOf(resId);
 
finalBitmap 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 <a href="file:///D:/Users/Administrator/AppData/Local/Android/sdk/docs/training/displaying-bitmaps/cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
}

Load Bitmaps into a GridView Implementation


The grid list building block is useful for showing image data sets and can be implemented using aGridView 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).

To start with, here is a standard GridView implementation with ImageView children placed inside aFragment. Again, this might seem like a perfectly reasonable approach, but what would make it better?

译:

Grid list building block 是一种有效显示大量图片的方式。这样能够一次显示许多图片,而且那些即将被显示的图片也处于准备显示状态。如果你想要实现这种效果,你必须确保UI是流畅的,能够控制内存使用,并且正确的处理并发问题(因为 GridView 会循环使用子视图)。

下面是一个在Fragment里面内置了ImageView作为GridView子视图的示例:

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.

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 Threadlesson. Here is the updated solution:

译:

又一次,这一个实现的问题是图片是在UI线程中被设置。当处理小的图片时可以,但其他需要额外操作的处理,都会使你的UI慢下来。

与前面加载到图片到ViewPager一样,如果setImageResource的操作会比较耗时,有可能会卡到UI Thread。可以使用类似前面异步处理图片与增加缓存的方法来解决那个问题。然而,我们还需要考虑GridView的循环机制所带来的并发问题。为了处理这个问题,请参考前面的课程 。下面是一个更新的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
publicclass ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
...
 
privateclass ImageAdapter extends BaseAdapter {
...
 
@Override
publicView getView(intposition, View convertView, ViewGroup container) {
...
<strong>loadBitmap(imageResIds[position], imageView)</strong>
returnimageView;
}
}
 
publicvoid loadBitmap(int resId, ImageView imageView) {
if(cancelPotentialWork(resId, imageView)) {
finalBitmapWorkerTask task = newBitmapWorkerTask(imageView);
finalAsyncDrawable asyncDrawable =
newAsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
 
staticclass AsyncDrawable extends BitmapDrawable {
privatefinal WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 
publicAsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
newWeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
 
publicBitmapWorkerTask getBitmapWorkerTask() {
returnbitmapWorkerTaskReference.get();
}
}
 
publicstatic boolean cancelPotentialWork(intdata, ImageView imageView) {
finalBitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 
if(bitmapWorkerTask != null) {
finalint bitmapData = bitmapWorkerTask.data;
if(bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
}else {
// The same work is already in progress
returnfalse;
}
}
// No task associated with the ImageView, or an existing task was cancelled
returntrue;
}
 
privatestatic BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if(imageView != null) {
finalDrawable drawable = imageView.getDrawable();
if(drawable instanceofAsyncDrawable) {
finalAsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
returnasyncDrawable.getBitmapWorkerTask();
}
}
returnnull;
}
 
...// include updated <a href="file:///D:/Users/Administrator/AppData/Local/Android/sdk/docs/training/displaying-bitmaps/process-bitmap.html#BitmapWorkerTaskUpdated"><code>BitmapWorkerTask</code></a> class

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

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.

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

0 0
原创粉丝点击