由于本人英文能力实在有限,不足之初敬请谅解
本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接
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地址