Android开发笔记(三十八)列表类视图

AdapterView

AdapterView顾名思义是适配器视图,Spinner、ListView和GridView都间接继承自AdapterView,这三个视图都存在多个元素并排展示的情况,所以需要引入适配器模式。
适配器视图的特点有:
1、定义了适配器的设置方法setAdapter,以及获取方法getAdapter。适配器用于传入视图展示需要的相关数据。
2、定义了一个数据观察者AdapterDataSetObserver,用于在列表数据发生变化时,可以通过notifyDataSetChanged方法来更新视图。
3、定义了单个元素的点击、长按、选中事件。其中点击方法为setOnItemClickListener,点击监听器为OnItemClickListener;长按方法为setOnItemLongClickListener,长按监听器为OnItemLongClickListener;选中方法为setOnItemSelectedListener,选中监听器为OnItemSelectedListener。


Adapter

适配器Adapter与适配视图是配合使用的,每个适配类视图都要搭配相应的适配器,才能够正常工作。Adapter派生出两个接口SpinnerAdapter和ListAdapter,然后BaseAdapter又同时实现了SpinnerAdapter和ListAdapter,所以实际开发中用的是BaseAdapter及其派生出的子类。


一般情况下自定义适配器继承自BaseAdapter就够用了,当然Android为了方便懒人,专门扩展了两种简单易用的适配器,如ArrayAdapter用于每行只显示文本的情况,而SimpleAdapter用于每行显示左图标右文本的情况。实际开发中,ArrayAdapter多用于Spinner,但是SimpleAdapter却很少使用。像ListView和GridView一般都是直接使用BaseAdapter,并不使用布局过于简单的SimpleAdapter。


Spinner

Spinner是下拉框,用于从一串列表中选择某项。下面是Spinner常用的属性和方法:
xml布局上的属性设置:
prompt : 指定弹窗的标题视图,在spinnerMode=dialog时有效。该属性值不可直接填字符串,但可通过"@string/..."方式来指定标题文字。
spinnerMode : 下拉列表的显示样式,有dialog弹窗和dropdown下拉两种。不过考虑到用户体验,实际开发中一般用dialog。spinnerMode只能在xml中设置,不能在代码中设置。
代码中的方法:
setPrompt : 设置标题文字。
setPromptId : 设置标题视图的资源ID。
setSelection : 设置当前选中哪项。
setAdapter : 设置适配器。源码中的适配器类型是SpinnerAdapter,但该类用起来很麻烦,所以实际中用的一般是ArrayAdapter<String>,ArrayAdapter可以直接传入一个字符串数组。
setOnItemSelectedListener : 设置下拉列表的选中监听器。


下面是Spinner调用的代码例子:
		ArrayAdapter<String> starAdapter = new ArrayAdapter<String>(this,
				R.layout.spinner_item, starArray);
		starAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
		Spinner sp = (Spinner) findViewById(R.id.sp_hello);
		sp.setPrompt("请选择行星");
		sp.setAdapter(starAdapter);
		sp.setOnItemSelectedListener(new MySelectedListener());


	private String[] starArray = {"水星", "金星", "地球", "火星", "木星", "土星"};
	class MySelectedListener implements OnItemSelectedListener {
		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
			Toast.makeText(MainActivity.this, "您选择的是"+starArray[arg2], Toast.LENGTH_LONG).show();
		}


		public void onNothingSelected(AdapterView<?> arg0) {
		}
	}


代码中用到的spinner_item元素布局的示例如下:
<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textAlignment="inherit"
    android:textSize="17sp"
    android:textColor="#0000ff" />
上面这个xml布局用到了TextView的三个新属性,说明如下:
singleLine : 指定是否单行显示,取值true表示单行,false表示多行。代码中对应的方法是setSingleLine。
ellipsize : 指定字符超出TextView区域时的显示方式,取值说明如下:start表示在字符串开头显示省略号,end表示在字符串末尾显示省略号,middle表示在字符串中间显示省略号,marquee表示以跑马灯方式显示字符串(即从左向右循环滚动,跑马灯方式需同时指定singleLine为true)。代码中对应的方法是setEllipsize。
textAlignment : 指定文本的对齐方式,常见的取值说明如下:inherit表示继承上级视图的对齐方式,center表示文本居中对齐,textStart表示文本开头对齐,textEnd表示文本末尾对齐,viewStart表示视图开头对齐,viewEnd表示视图末尾对齐。该属性在API17后增加,即Android4.2.2以上版本才支持。代码中对应的方法是setTextAlignment。


ListView

ListView是列表视图,用于分行显示列表信息。下面是ListView常用的属性和方法:

ListView的属性和方法

xml布局上的属性设置:
divider : 指定分隔线的图形。如需取消分隔线,可设置该属性值为@null
dividerHeight : 指定分隔线的高度。注意如果divider设置为@null时,就不可将dividerHeight设置为大于0dp的数值,因为这样可能导致末尾的元素显示不全。
headerDividersEnabled : 指定是否显示列表开头的分隔线。但实际开发中发现这个设置不起作用,即使该属性设置为true,开头也不会显示分隔线。查看ListView的源码,发现分隔线是画在子视图的下方,所以列表上方的分隔线就画不出来了。有种情况是例外,就是如果stackFromBottom设置为true,表示列表从下往上显示,那么此时会显示列表上方的分隔线,而不会显示列表下方的分隔线了。
footerDividersEnabled : 指定是否显示列表末尾的分隔线。
stackFromBottom : 指定列表项是否从下往上显示。


代码中的方法:
setDivider : 设置分隔线的图形。
setDividerHeight : 设置分隔线的高度。
setHeaderDividersEnabled : 设置是否显示列表开头的分隔线。该方法实际上不起作用。
setFooterDividersEnabled : 设置是否显示列表末尾的分隔线。
setStackFromBottom : 设置列表项是否从下往上显示。
setAdapter : 设置适配器。ListView使用的适配器一般继承自BaseAdapter。
setOnItemClickListener : 设置点击事件的监听器。
setOnItemLongClickListener : 设置长按事件的监听器。


总结ListView的属性设置有两个注意点(不知算不算Android的bug,呵呵):
1、divider设置为@null时,就不能再设置dividerHeight为非0值,不然列表末尾元素显示有问题;
2、不管是否指定headerDividersEnabled,列表上方的分隔线都不会显示;


ListView的使用方式

Android提供了两种使用ListView的方式:
1、ListActivity方式。首先xml布局中将ListView的id设置为系统id,即“@android:id/list”,然后页面的代码类继承ListActivity。该方式无需在代码中获取ListView的对象,直接调用setListAdapter方法设置适配器,同时实现ListActivity的点击方法onListItemClick来响应点击事件。
2、普通Activity方式。xml布局中ListView的id可自定义,页面的代码类继承自Activity。该方式要从布局文件中获取ListView的对象,然后调用该对象的setAdapter方法设置适配器,并调用ListView对象的setOnItemClickListener方法来设置点击事件的监听器。


两种使用方式的区别如下:
1、ListActivity方式的视图id被设置为系统id,不方便在代码中修改该列表视图的属性;
2、ListActivity方式只实现点击方法、未实现长按方法,不方便响应列表项的长按事件。
3、实际开发中经常自己写个Activity的基类,具体页面都从该Activity基类派生出来。如果有个页面采用ListActivity方式,就无法继承使用这个Activity基类了。
从上面可以看出,ListActivity方式的限制较多,所以实际开发中我们还是使用普通Activity方式来开发ListView。


ListView的示例代码

下面是适配器的代码例子:
import java.util.ArrayList;

import com.example.exmsimplewidgte.R;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;

@SuppressLint({ "DefaultLocale", "InflateParams" })
public class TitleListAdapter extends BaseAdapter 
	implements OnItemClickListener, OnItemLongClickListener {

	private ArrayList<String> mTitleList;
	private LayoutInflater mInflater;
	private Context mContext;

	public TitleListAdapter(Context context, String[] title_list) {
		this.mInflater = LayoutInflater.from(context);
		mContext = context;
		mTitleList = new ArrayList<String>();
		for (int i=0; i<title_list.length; i++) {
			mTitleList.add(title_list[i]);
		}
	}

	@Override
	public int getCount() {
		return mTitleList.size();
	}

	@Override
	public Object getItem(int arg0) {
		return mTitleList.get(arg0);
	}

	@Override
	public long getItemId(int arg0) {
		return arg0;
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (convertView == null) {
			holder = new ViewHolder();
			convertView = mInflater.inflate(R.layout.list_title, null);
			holder.tv_seq = (TextView) convertView.findViewById(R.id.tv_seq);
			holder.tv_title = (TextView) convertView.findViewById(R.id.tv_title);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		holder.tv_seq.setText(""+(position+1));
		holder.tv_title.setText(mTitleList.get(position));
		return convertView;
	}

	public final class ViewHolder {
		public TextView tv_seq;
		public TextView tv_title;
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		String desc = String.format("您点击了第%d项,该项的标题是%s", 
				position+1, mTitleList.get(position));
		Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onItemLongClick(AdapterView<?> parent, View view,
			int position, long id) {
		String desc = String.format("您长按了第%d项,该项的标题是%s", 
				position+1, mTitleList.get(position));
		Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show();
		return true;
	}

}


下面是ListView的调用代码例子:
		String[] weekArray = {"星期一", "星期二", "星期三", "星期四", "星期五"};
		TitleListAdapter titleAdapter = new TitleListAdapter(this, weekArray);
		ListView lv_hello = (ListView) findViewById(R.id.lv_hello);
		lv_hello.setFooterDividersEnabled(true);
		lv_hello.setAdapter(titleAdapter);
		lv_hello.setOnItemClickListener(titleAdapter);
		lv_hello.setOnItemLongClickListener(titleAdapter);



GridView

GridView是网格视图,用于分行分列显示表格信息。下面是GridView常用的属性和方法:
xml布局上的属性设置:
horizontalSpacing : 指定子视图在水平方向的间距。
verticalSpacing : 指定子视图在垂直方向的间距。
columnWidth : 指定每列的宽度。
numColumns : 指定列的数目。
stretchMode : 指定拉伸的模式。取值说明如下:none表示不做拉伸;columnWidth表示若有空余空间,则拉伸与列宽大小一致;spacingWidth表示若有空余空间,则列宽不变,把空余分配到每列间的空隙;spacingWidthUniform与spacingWidth的区别在于,Uniform方式在每列左边和右边都补上空隙(即每行开头和末尾都补空隙),而spacingWidth在每行开头和末尾不补空隙,只有列与列之间才补空隙。实际开发中一般把模式设置为columnWidth。
listSelector : 指定点击网格时的显示背景。


代码中的方法:
setHorizontalSpacing : 设置子视图在水平方向的间距。
setVerticalSpacing : 设置子视图在垂直方向的间距。
setColumnWidth : 设置每列的宽度。
setNumColumns : 设置列的数目。
setStretchMode : 设置拉伸的模式。
setAdapter : 设置适配器。GridView使用的适配器一般继承自BaseAdapter。
setOnItemClickListener : 设置点击事件的监听器。
setOnItemLongClickListener : 设置长按事件的监听器。


实际开发中有时需要设置网格之间表格线的颜色,可惜GridView并未直接给出相应的属性和方法,那得变通处理一下。具体的说,就是给GridView设置整个网格的背景色(例如黑色),以及网格之间的水平间距和垂直间距;然后给每项网格的根布局设置背景色(例如白色),这样只有网格间距是黑色,从而间接画上了黑色表格线。


GridView偶尔会出现5dp的外边框,原因尚不明,要想去除这个该死的抽风边框,可将listSelector属性设置为@null,估计此问题与点击背景有关。


GridView的适配器模板与ListView是一样的,只要换掉代码里的布局文件名以及相关控件名称就好了,所以不再重复贴出GridView的适配器代码。下面是GridView的调用代码例子:
		String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
				"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};
		ContentGridAdapter contentAdapter = new ContentGridAdapter(this, yearArray);
		GridView gv_hello = (GridView) findViewById(R.id.gv_hello);
		gv_hello.setNumColumns(5);
		gv_hello.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
		gv_hello.setHorizontalSpacing(1);
		gv_hello.setVerticalSpacing(1);
		gv_hello.setAdapter(contentAdapter);
		gv_hello.setOnItemClickListener(contentAdapter);
		gv_hello.setOnItemLongClickListener(contentAdapter);




点此查看Android开发笔记的完整目录




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值