EXPLORING THE WORLD OF ANDROID :: PART 2
来源:互联网 发布:关于办理电信网络诈骗 编辑:程序博客网 时间:2024/06/01 08:32
http://blog.jteam.nl/2009/09/17/exploring-the-world-of-android-part-2/ And I'm back! Reporting live on the glorious adventures in the exciting world of Android. This blog post is the second one in the Android series. This time with code samples! Yeah! In my first blog post about Android I talked about setting up a project using Android. This time I want to discuss a more "advanced" topic: ListView performance. A ListView is a view component which allows you to display and scroll through a list of items. It can display simple text in each row, but is also able to display a more complicated structure. In the latter case you will need to make sure your ListView still performs well (read: renders fast and scrolls smoothly). I am going to provide solutions to a few different performance problems when using a ListView If you want to use a ListView, you will have to supply it with a ListAdapter to allow it to display any content. A few simple implementations of that adapter are already available in the SDK: These implementations are perfect for displaying very simple lists. But if your list is just a little more complicated than that, you will need to write your own custom ListAdapter implementation. In most cases it's useful to subclass ArrayAdapter which already takes care of managing a list of objects. Now you only have to tell it how to render each object in the list. Do this by overriding thegetView(int, View, ViewGroup) method of the ArrayAdapter class. To give you a simple example of a case in which you need to write your own ListAdapter: displaying a list of images with some text next to it. The images need to be on-the-fly downloaded from the internet. Let's create a class which represents items in the list: Now, let's create an implementation of a ListAdapter that is able to display a list of theseImageAndTexts. The views are inflated from an XML file called "image_and_text_row.xml": This ListAdapter implementation renders the ImageAndTexts in the ListView like you would expect. The only thing is that this only works for a very small list which doesn't require scrolling to see all items. If the list of ImageAndTexts gets bigger you will notice that scrolling isn't as smooth as it should be (in fact, it's far off!). The biggest bottleneck in the above example is the fact that the images have to be downloaded from the internet. Because we execute all our code in the same thread as the UI, the UI will get stuck each time an image is being downloaded. If you run the same application using a 3G internet connection instead of WiFi, the performance will even be worse. To avoid this we want the image to be loaded in a separate thread to not disturb the UI thread too much. To make this happen, we could use an AsyncTask which is designed for cases like this. But in practice, you will notice that the AsyncTask is limited to 10 threads. This number is hardcoded somewhere in the Android SDK so we cannot change this. In this case it's a limitation we cannot live with, because often more than 10 images are loaded at the same time. An alternative is to manually spawn a new Thread for each image. In addition we should useHandlers to deliver the downloaded images to the UI thread. We want to do this because only from the UI thread you are allowed to modify the UI (read: draw an image on the screen). I created a class called AsyncImageLoader which takes care of loading images using Threads and Handlerslike I just described. Also it caches images to avoid a single image to be downloaded multiple times. Notice that I used a SoftReference for caching images, to allow the garbage collector to clean the images from the cache when needed. How it works: Only one instance of AsyncImageLoader should exist in your application, or else the caching won't work. If we take the example ImageAndTextListAdapter class we can now replace: with: Using this approach, the ListView performs a lot better and feels much more smooth because the UI thread is no longer blocked by the loading of images. If you tried the solution described above you will notice that the ListView is still not a 100% smooth. You will still notice some little disruptions that make it a little less smooth than it could be. There are two things remaining that can be improved: The solution is obvious: we should cache/reuse these things! Mark Murphy did a very nice job on writing a few blog entries describing how this can be done. To reuse the views which are inflated from XML read this blog entry: To cache the views returned by findViewById() read this blog entry: If we apply the strategies described in Mark Murphy's blog entries our ImageAndTextListAdaptercould look like the following: There are two things to notice. The first thing is that the drawable is not directly set to theImageView anymore after loading. Instead, the right ImageView is looked up through it's tag. This is done because we're now reusing views and the images might end up on the wrong rows. We need a reference to the ListView to lookup ImageViews by tag. The other thing to notice, is that this implementation uses an object called ViewCache. This is what the class for that object looks like: This ViewCache is the same as what Mark Murphy calls a "ViewWrapper" and takes care of caching individual views which normally would have to be looked up every time using the expensive call to findViewById(). I've shown you how to improve performance of a ListView in three different ways: It took me quite some time to figure this stuff out, especially the image loading part. So I thought it is all worth mentioning to avoid you having to waste too much time on it. Next time I will discuss other interesting challenges in the world of Android! TO BE CONTINUED ...
Example of a ListView containing Youtube search results in the form of images and textpublic
class
ImageAndText {
private
String imageUrl;
private
String text;
public
ImageAndText(String imageUrl, String text) {
this
.imageUrl = imageUrl;
this
.text = text;
}
public
String getImageUrl() {
return
imageUrl;
}
public
String getText() {
return
text;
}
}
public
class
ImageAndTextListAdapter
extends
ArrayAdapter<ImageAndText> {
public
ImageAndTextListAdapter(Activity activity, List<ImageAndText> imageAndTexts) {
super
(activity,
0
, imageAndTexts);
}
@Override
public
View getView(
int
position, View convertView, ViewGroup parent) {
Activity activity = (Activity) getContext();
LayoutInflater inflater = activity.getLayoutInflater();
// Inflate the views from XML
View rowView = inflater.inflate(R.layout.image_and_text_row,
null
);
ImageAndText imageAndText = getItem(position);
// Load the image and set it on the ImageView
ImageView imageView = (ImageView) rowView.findViewById(R.id.image);
imageView.setImageDrawable(loadImageFromUrl(imageAndText.getImageUrl()));
// Set the text on the TextView
TextView textView = (TextView) rowView.findViewById(R.id.text);
textView.setText(imageAndText.getText());
return
rowView;
}
public
static
Drawable loadImageFromUrl(String url) {
InputStream inputStream;
try
{
inputStream =
new
URL(url).openStream();
}
catch
(IOException e) {
throw
new
RuntimeException(e);
}
return
Drawable.createFromStream(inputStream,
"src"
);
}
}
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:orientation
=
"horizontal"
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
>
<
ImageView
android:id
=
"@+id/image"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:src
=
"@drawable/default_image"
/>
<
TextView
android:id
=
"@+id/text"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
/>
</
LinearLayout
>
AsyncImageLoader
public
class
AsyncImageLoader {
private
HashMap<String, SoftReference<Drawable>> imageCache;
public
AsyncImageLoader() {
drawableMap =
new
HashMap<String, SoftReference<Drawable>>();
}
public
Drawable loadDrawable(
final
String imageUrl,
final
ImageCallback imageCallback) {
if
(drawableMap.containsKey(imageUrl)) {
SoftReference<Drawable> softReference = imageCache.get(imageUrl);
Drawable drawable = softReference.get();
if
(drawable !=
null
) {
return
drawable;
}
}
final
Handler handler =
new
Handler() {
@Override
public
void
handleMessage(Message message) {
imageCallback.imageLoaded((Drawable) message.obj, imageUrl);
}
};
new
Thread() {
@Override
public
void
run() {
Drawable drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl,
new
SoftReference<Drawable>(drawable));
Message message = handler.obtainMessage(
0
, drawable);
handler.sendMessage(message);
}
}.start();
return
null
;
}
public
static
Drawable loadImageFromUrl(String url) {
// ...
}
public
interface
ImageCallback {
public
void
imageLoaded(Drawable imageDrawable, String imageUrl);
}
}
ImageView imageView = (ImageView) rowView.findViewById(R.id.image);
imageView.setImageDrawable(loadImageFromUrl(imageAndText.getImageUrl()));
final
ImageView imageView = (ImageView) rowView.findViewById(R.id.image);
Drawable cachedImage = asyncImageLoader.loadDrawable(imageAndText.getImageUrl(),
new
ImageCallback() {
public
void
imageLoaded(Drawable imageDrawable, String imageUrl) {
imageView.setImageDrawable(imageDrawable);
}
});
imageView.setImageDrawable(cachedImage);
http://www.androidguys.com/2008/07/17/fancy-listviews-part-two/
http://www.androidguys.com/2008/07/22/fancy-listviews-part-three/public
class
ImageAndTextListAdapter
extends
ArrayAdapter<ImageAndText> {
private
ListView listView;
private
AsyncImageLoader asyncImageLoader;
public
ImageAndTextListAdapter(Activity activity, List<ImageAndText> imageAndTexts, ListView listView) {
super
(activity,
0
, imageAndTexts);
this
.listView = listView;
asyncImageLoader =
new
AsyncImageLoader();
}
@Override
public
View getView(
int
position, View convertView, ViewGroup parent) {
Activity activity = (Activity) getContext();
// Inflate the views from XML
View rowView = convertView;
ViewCache viewCache;
if
(rowView ==
null
) {
LayoutInflater inflater = activity.getLayoutInflater();
rowView = inflater.inflate(R.layout.image_and_text_row,
null
);
viewCache =
new
ViewCache(rowView);
rowView.setTag(viewCache);
}
else
{
viewCache = (ViewCache) rowView.getTag();
}
ImageAndText imageAndText = getItem(position);
// Load the image and set it on the ImageView
String imageUrl = imageAndText.getImageUrl();
ImageView imageView = viewCache.getImageView();
imageView.setTag(imageUrl);
Drawable cachedImage = asyncImageLoader.loadDrawable(imageUrl,
new
ImageCallback() {
public
void
imageLoaded(Drawable imageDrawable, String imageUrl) {
ImageView imageViewByTag = (ImageView) listView.findViewWithTag(imageUrl);
if
(imageViewByTag !=
null
) {
imageViewByTag.setImageDrawable(imageDrawable);
}
}
});
imageView.setImageDrawable(cachedImage);
// Set the text on the TextView
TextView textView = viewCache.getTextView();
textView.setText(imageAndText.getText());
return
rowView;
}
}
public
class
ViewCache {
private
View baseView;
private
TextView textView;
private
ImageView imageView;
public
ViewCache(View baseView) {
this
.baseView = baseView;
}
public
TextView getTextView() {
if
(textView ==
null
) {
textView = (TextView) baseView.findViewById(R.id.text);
}
return
titleView;
}
public
ImageView getImageView() {
if
(imageView ==
null
) {
imageView = (ImageView) baseView.findViewById(R.id.image);
}
return
imageView;
}
}
- EXPLORING THE WORLD OF ANDROID :: PART 2
- Exploring the world of Android :: Part 2
- Exploring the world of Android :: Part 2
- Exploring the world of Android
- Home Training Clients Blog Tech Corner Contact Exploring the world of Android :: Part 2
- EXPLORING IPHONE GRAPHICS PART 2
- Testing in the Agile World (Part 2)
- Exploring the 'Dao' of Search---Sun Liang
- Models of Computation - Exploring the power of computing
- The Design of Model (part 2)
- The State of Rendering – Part 2
- Sailing Through The World of Linux BASH Scripting – Part III
- Be a part of making the world’s first people powered tablet
- cc End Of The World 2
- Top of The World
- Top of The World
- The World of ECMAScript
- THE WORLD OF SELECT()
- [转载] 有理想的程序员必须知道的15件事 作者:ugmbbc
- 开机时间小工具
- GolddenGate-双向复制
- Android自动化测试框架Robotium学习笔记
- 转载-- 给程序员职业的一些建议
- EXPLORING THE WORLD OF ANDROID :: PART 2
- 调整好心态和策略,从底层做起——致2011毕业生
- 如何去掉某些程序的广告提示信息
- 期待已久的Ext JS 4.0正式版发布了
- 20. android dialog——自定义对话框之一
- proteus元件名称中文名说明
- hdoj 1143-Tri Tiling
- JDBC mysql数据库连接池配置
- Windows函数