在android平台作业至今,bitmap 的out of memory error(OOM)异常是最让人头疼的了,由于我们制造的是设计类应用,所以处处都会与bitmap产生交集。因此,在这个过程中,我们因为性能问题、因为异常处理,因为种种原因,不得不去不断改善bitmap的性能,也因此有了一些小体会、小经验。为了巩固在工作中学得的经验,从这篇日志起,我希望能够将这些经验记录下来,以供将来参考、回顾。
Android的GridView控件应该算是和BItmap打交道相对较多的了(还有一个gallary),而它也是我工作中接触最多、最深入的控件(没有之一),因此,在这个系列中,我的一切功能都是以它为基础呈现在屏幕中的。GridView通常被人称之为九宫图,它是以m*n的矩阵布局呈现在我们面前,随着要呈现的内容的不断增多,这个矩阵也将越来越大,而随着这个矩阵的变大,我们的内存也将不断感受到压力,一旦没有正确地处理好内存管理,应用就会有崩溃的险情。因此,GridView虽说只是Android众多控件中的一个,但是学好它、用好它,可以极大地提高我们的能力。
言归正传。在这篇日志中,我们的主要任务是让GridVIew能够跑起来,让它可以显示出图片来,为了简便起见,我们姑且只用android项目中的小绿人,在日后的进阶过程中会陆续涉及到从本地读取图片、从网络读取图片等。我们首先从界面布局开始,首先我们需要在main.xml(也可以根据需求创建自定义layout)中添加一个GridView元素,并赋予它一个独一无二的ID。
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="3" >
</GridView>
但是但有这个还不够,我们还需要创建一个定义gridView每一个格子的具体布局,这里我们创建一个gridview.xml,其根元素是LInearLayout,在这一版中,我们还需要给它定义一个ID,在后面会需要用到。而子元素则是ImageView(因为我们要解决的是OOM问题嘛)。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="3dp" >
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
于是,界面设计部分就到这里为止了,接下来是代码部分,而这也是最重要的部分,最有趣的部分。要想让gridView显示出我们想让它显示的内容,我们需要一个adapter来管理它,因此,我们需要新建一个继承自Android提供的BaseAdapter的自定义类,并重写getCount、getView等方法,这里我们定义为GridViewAdapter。为了后面的操作更为方便,我们需要定义2个成员变量,一个是Context,它的作用下面会提到,另一个是容纳所有Bitmap的List,然后在构造函数中初始化它们,之前我曾提到过,在这个版本中为了方便,Bitmap选用了项目默认自带的小绿人,所以在构造的时候需要传一个Context进来,这点是需要说明的。
private final Context mContext;
private final ArrayList<Bitmap> bitmapList;
public GridViewAdapter(Context context) {
super();
mContext = context;
bitmapList = new ArrayList<Bitmap>();
}
然后写一个初始化List的方法,为了体现出GridView的特性,可以把List设置得大一点,这里以100为例。
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.ic_launcher);
for (int i = 0; i < 100; i++) {
bitmapList.add(bitmap);
}
至此Adapter的准备工作就结束了,下面来看看Adapter里的几个必须重写的方法。
getCount():这个方法决定了getView(final int position, View convertView, ViewGroup parent) 的循环执行次数,默认为return 0,也就是不执行,我们需要把它改为return List的长度。这里要注意的是,如果List为空,或者size为0,getView也不会执行的,虽然不会报错,但是界面上不会有任何东西。
@Override
public int getCount() {
return bitmapList.size();
}
getItem(int position):这个方面通常是在写OnItemClickListener时用到的,我们暂时用不到它,所以保持return null就可以了。
@Override
public Object getItem(int position) {
return null;
}
getItemId(int position) :这个基本不会用到,保持原样吧。
@Override
public long getItemId(int position) {
return 0;
}
最后是最重头的getView(final int position, View convertView, ViewGroup parent) ,它是一切的核心,也是最奇妙的部分。这里我们先看代码,再来解释:
@Override
public View getView(final int position, View convertView,ViewGroup parent) {
View view = View.inflate(mContext, R.layout.gridview, null);
LinearLayout ll = (LinearLayout) view.findViewById(R.id.ll);
ImageView imageView = (ImageView)ll.findViewById(R.id.image);
imageView.setImageBitmap(bitmapList.get(position));
return ll;
}
在这个方法里,第一句是根据xml名字找到View,以便后面寻找到View上面的组件,进行相应的操作。当找到View之后,我们先根据ID取出gridView的根布局,还记得之前在gridview.xml的LinearLayout根元素里定义的ID吧,就是它。找到它之后再根据ID找到ImageView,取到之后,使用ImageView的setImageBitmap方法将前面创建的BItmap的List中的图片设置进去。完成之后return linearlayout就可以了。
完成这些之后,我们的gridView事实上已经能够显示图片了,但是我们还需要把adapter和gridview绑定起来。由于简单原则,这里我直接在activity里操作了。在activity中我们需要做的是在onCreate(Bundle savedInstanceState)方法中实例化GridView——通过main.xml中GridView元素的ID来获得,并实例化一个GridViewAdapter,然后通过gridView的setAdapter()方法将它们绑定起来,这样就完成了。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridView = (GridView) findViewById(R.id.gridView);
GridViewAdapter adapter = new GridViewAdapter(this);
gridView.setAdapter(adapter);
}
