您的位置:首页 > 移动开发 > Android开发

Pro Android学习笔记(二二):用户界面和控制(10):自定义Adapter

2013-09-05 21:37 471 查看


我们可以同继承抽象类BaseAdapter来实现自己的Adapter,自己设置子View的UI,不同子View可以由不同的布局,并自己进行数据和子view中数据的对应关系。图是例子的呈现结果,我们有很多图标,对这些图标按一定大小进行缩放,然后布局在GridView中。这个例子很简单。

设计Adapter的布局

Activity的layout很简单,就只有一个GridView,我们继续沿用之前GridView例子中的XML文件,见Pro Android学习笔记(二十):用户界面和控制(8):GridView和Spinner,不再重复。
我们设计每个网格,即子view的布局,ui_gridimage.xml如下,也很简单,只含有一个控件ImageView,id为R.id.ui_myimage。

<?xml version="1.0" encoding="utf-8"?>

<ImageView xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/ui_myimage"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="#555"

android:scaleType="centerInside"

android:padding="5dip"

android:maxHeight="50dip"

android:maxWidth="50dip" />

代码部分

Activity的代码

由于这个例子很小,自定义的Adapter以内部类的方式放在Activity中,一般应当独立为一个class。Activity的代码如下:

public class UiGridCustomTest1 extends Activity{

/* 和系统提供的Adapter方式在调用上没有什么不同,只是我们将在MyAdapter中直接获取数据,和直接指定如何呈现,不需要传递数据源等信息。*/

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.ui_gridview);

GridView gv = (GridView)findViewById(R.id.ui_grid);

MyAdapter adapter = new MyAdapter(this);

gv.setAdapter(adapter);


}

/* MyAdatpter是自定义的Adapter。本例以内部类方式出现 */

private class MyAdapter extends BaseAdapter{

......

}

}
接下来,我们重点看看MyAdapter的代码。

MyAdapter的代码:数据源和构造函数

private class MyAdapter extends BaseAdapter{

private static final String TAG="MyAdapter"; //用于Log.d(TAG, ……);

private Context mContext = null;

private LayoutInflater infalter = null;



/* 【数据源(1)】: icons[]是图片资源的ID,是原始数据,通过资源ID生产Bitmap图片 myImages[],这是原始尺寸的图片,实际上,我们对图片做了进行伸缩处理,放置在myThumbs[]中,而myThumbs是需要呈现在UI的图片。 */

private int[] icons = {R.drawable.png01,R.drawable.png02,…(省略)…,R.drawable.png20};

private Bitmap[] myImages = new Bitmap[icons.length];
//原图

private Bitmap[] myThumbs = new Bitmap[icons.length];
//按尺寸伸缩图



private int convertViewCount = 0; //用于Log.d中的跟踪信息。

/* 用于存储子view中的控件,本例只有一个,比较简单。 */

private class ViewHolder{

ImageView image;

}



public MyAdapter(Context context){

/*【初始化(1)】:保存context,创建infalter,并于context关联。infalter可 从xml中创建view对象,非常方便。*/

mContext = context;

infalter = LayoutInflater.from(mContext);

/*【初始化(2)】【数据源(2)】:创建数据源,将资源ID转换为最终呈现的Bitmap信息*/

for(int i = 0 ; i <icons.length; i ++){

myImages[i] = BitmapFactory.decodeResource(context.getResources(), icons[i]);

//还可以使用ThumbnailUtils来处理相关的图片和视频

myThumbs[i] = Bitmap.createScaledBitmap(myImages[i], 100, 100, false);

}

}



......



}

MyAdapter的代码:实现自定义的adapter

MyAdapter集成抽象类BaseAdapter,有4个抽象方法必须实现,实际是来自BaseAdatper实现的Adapter接口。

private class MyAdapter extends BaseAdapter{

... ...

//How many items are in the data set represented by this Adapter.

//需要显示多少个item,本例即图片的数目。

public int getCount()
{

Log.d(TAG,"getCount() return " + icons.length);

return icons.length;

}

// Get the data item associated with the specified position in the data set.

// 根据position返回该子view对应的data,用户无需考虑具体的UI布局就可获取data,拘役data如何获取和adapter的具体实现相关,例如CursorAdapter中读取联系人姓名的例子,我们是通过_ID从content provider中读取,在此可以直接返回null。

public Object getItem(int position)
{

Log.d(TAG,"getItem(" + position + ")" );

return icons[position];

}

// Get the row id associated with the specified position in the list.

// 根据position获得rowId,在CursorAdapter的例子中,rowId为_ID,不一定和position一致,又例如在ListView中一行数据一行分隔符(分隔符也是一个子view,占一个position)的显示方法, rowId和position也不一致。

public long getItemId(int position)
{

Log.d(TAG,"getItemId(position) is " + position);

return position;

}



// Get a View that displays the data at the specified position in the data set.

// 这是子View布局显示的实现,是最关键的代码部分。Android只会询问当前显示的子View的呈现, 也就是不显示的子view,系统不会调用getView(),这就很好地提高了性能。系统要显示某个子view的使用,就会根据position来调用该方法。

//参数2:convertView就是子View,如果第一次要求给出position的子view的呈现,convertView为null,我们上下滚动屏幕,系统会要求获得显示部分的各个子view的具体呈现,如果该子view之前已经创建,则convertView就是之前子view对象,我们可以利用系统保存的之前已经实现的子view对象,简化代码,提升效率。参数3 parent,从本例的层次上看就是GridView

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder; //存放该子view的控件,这样我们不用每次都通过Id来查找,直接引用对象。

Log.d(TAG,"getView() : position = " + position

+ " convertView = " + (convertView == null ? "null" : convertView.toString())

/*+ " parent : " + (parent == null ? "null" : parent.toString())*/);



//实现布局

if(convertView == null){ //子view第一次出现,需要构造,将重要内容(本例为控件对象)放置在viewHolder,并通过setTag()存放。

convertView = infalter.inflate(R.layout.ui_gridimage, null ); //通过xml来创建view

convertViewCount ++;

Log.d(TAG,"convertViewCount is " + convertViewCount);

holder = new ViewHolder();

holder.image = (ImageView) convertView.findViewById(R.id.ui_myimage);

// holder.image.setImageBitmap(myThumbs[position]);

convertView.setTag(holder);

}else{ //子view已经出现过,利用原来已经创建的对象,获得控件信息

holder = (ViewHolder)convertView.getTag();

Log.d(TAG,"image is " + holder.image.toString());

}



//将图片在view中呈现,这里有个地方不是很明白,我们如果放在convertView == null中,即只在第一次出现的时候对设置控件的图片,如果我们上下滚动屏幕,发现图片显示出现混乱现象,为何?

holder.image.setImageBitmap(myThumbs[position]);

return convertView; //返回子view的对象

}

}

MyAdapter的代码:继续探讨BaseAdapter

实现上面四个抽象函数,以及可以完成我们的例子,我们继续对BaseAdapter的其他一些有趣函数进行分析,我们还可以在例子中增加一些重写的方法。

private class MyAdapter extends BaseAdapter{

... ...

//返回在AdapterView中需要显示多少种子view,本例只有ImageView一种,故返回1。例如ListView,如果在数据之间有separator,即一行textView,一行separator,则返回2。

public int getViewTypeCount() {


Log.d(TAG, "in getViewTypeCount() :only one Type");

return 1;

}

//在创建每个子view,也就是在getView()之前,都调用该函数,询问这是哪种子view,如果有2种子view的,在返回值为0或1,如果有3种,返回0,1,2。本例只有一种子view,所以无论是哪个position,都返回0。在使用separator的例子中中,根据奇偶返回0,1,此外还要使用isEanble(int position),对于separator要返回false。

public int getItemViewType(int position) {

Log.d(TAG, "in getItemViewType() for position " + position);

return 0;

}

}
有更多的自定义Adapter的例子,可以参考Android学习笔记(十五):Activity-GalleryViewAndroid学习笔记(十九):建立自己的ListView

相关链接:
我的Android开发相关文章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐