在android应用开发中,ListView是使用频率非常高的一个组件,基本上稍微复杂点的布局都会用到它,利用它可以让你的界面美观,有层次
。ListView可以用来作为数据显示的容器,也可以作为界面的布局。学习ListView需要关注的内容大概有三点:显示、数据适配器以及各种
事件的监听器。内容有点多,这里先只讲如何让ListView达到你想要的显示效果。
一、普通的ListView
普通的ListView是指每一个item只显示一条文本数据,程序运行效果图如下:
代码:
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_listview_simple);
//取得ListView实例
ListView lvwSimple = (ListView)findViewById(R.id.lvw_simple);
//要在ListView中显示的数据集合
String items[] = new String[] {"item1", "item2", "item3", "item4", "item5"};
//new一个ArrayAdapter,android.R.layout.simple_list_item_1为ListView显示的布局文件
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
//位ListView设置Adapter
lvwSimple.setAdapter(adapter);
}
二、自定义ListView
上面的那种方法只能显示简单的文字信息,显然在很多场合下都不够用,比如你想要在一个item中显示图片,显示多行文字,这就需要你自定义ListView的布局了。
效果图如下:
步骤:
1、分析你想要实现的布局效果,自定义布局文件lvw_custom.xml,该布局文件针对的是ListView的item,而不是整个ListView:
<?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="fill_parent"
>
<ImageView
android:id="@+id/lvw_custom_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dip"
android:background="@drawable/custom"
/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/lvw_custom_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:textSize="25dip"
android:text="item名称"/>
<TextView
android:id="@+id/lvw_custom_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:textSize="10dip"
android:text="item描述..."/>
</LinearLayout>
</LinearLayout>
2、取得用于ListView的数据集合,类型是ArrayList<Map<String, Object>>,每一个Map对应于ListView的一个item,多个map就构成了ListView的数据集合:
/**
* 取得用于ListView的数据
* @return
*/
private ArrayList<HashMap<String, Object>> getItems() {
ArrayList<HashMap<String, Object>> items = new ArrayList<HashMap<String, Object>>();
for(int i = 0; i < 5; i++) {
//***********************************************
//* 每一个map中的数据对应与ListView中的一个item *
//* 在我自定义的布局文件中,一个item包括: *
//* 1、图片(lvw_custom_img) *
//* 2、名称(lvw_custom_name) *
//* 3、描述(lvw_custom_description) *
//* 所以map中也至少需要包括这三项数据 *
//***********************************************
HashMap<String, Object> map = new HashMap<String, Object>();
//图片,key值可以随便取,映射关系会在实例化Adapter时定义,但我喜欢将key与布局文件中定义的id取同样的值
//value值为图片的资源id
map.put("lvw_custom_img", R.drawable.custom);
//名称
map.put("lvw_custom_name", "item名称");
//描述
map.put("lvw_custom_description", "item描述");
items.add(map);
}
return items;
}
3、为ListView设置SimpleAdapter:
//取得ListView实例
ListView lvwCustom = (ListView)findViewById(R.id.lvw_custom);
//要在ListView中显示的数据集合
ArrayList<HashMap<String, Object>> items = getItems();
//************************************************************************
//* new一个SimpleAdapter *
//* items为数据集合 *
//* R.layout.lvw_custom为自定义的ListView布局文件 *
//* 第四个参数为map中德key集合 *
//* 第五个参数为自定义布局文件中空间的资源id集合,与第四个参数要一一对应 *
//************************************************************************
SimpleAdapter adapter = new SimpleAdapter(this, items, R.layout.lvw_custom,
new String[] {"lvw_custom_img","lvw_custom_name","lvw_custom_description"},
new int[] {R.id.lvw_custom_img, R.id.lvw_custom_name, R.id.lvw_custom_description});
//位ListView设置Adapter
lvwCustom.setAdapter(adapter);
三、显示非资源id类型图片的ListView
通过上面的例子可以看到Map中图片项的value是资源id,这是针对项目中已存在的图片文件,为什么要用资源id而不是其他(比如Bitmap类型)呢,这是因为adapter的bindView()方法是负责解析图片并将其显示到ImageView中,但它只针对资源id类型做了判断。然而有一种情况,比如你的图片是从网络读取的Bitmap类型,你就需要对代码进行改写了。分析SimpleAdapter的源码,发现getView()方法是负责够造界面布局的的,而getView又是调用bindView来往控件里填充值的,所以我这里对bindView()方法进行改写。新建一个类CustomImageAdapter,代码完全copy自SimpleAdapter,找到bindView方法,对其进行改写:
private void bindView(int position, View view) {
final Map dataSet = mData.get(position);
if (dataSet == null) {
return;
}
final ViewBinder binder = mViewBinder;
final View[] holder = mHolders.get(view);
final String[] from = mFrom;
final int[] to = mTo;
final int count = to.length;
for (int i = 0; i < count; i++) {
final View v = holder[i];
if (v != null) {
final Object data = dataSet.get(from[i]);
String text = data == null ? "" : data.toString();
if (text == null) {
text = "";
}
boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, data, text);
}
if (!bound) {
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
} else {
throw new IllegalStateException(v.getClass().getName() +
" should be bound to a Boolean, not a " + data.getClass());
}
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
if (data instanceof Integer) {
setViewImage((ImageView) v, (Integer) data);
}
//这里增加对Bitmap类型的判断
else if(data instanceof Bitmap) {
((ImageView)v).setImageBitmap((Bitmap)data);
}
else {
setViewImage((ImageView) v, text);
}
} else {
throw new IllegalStateException(v.getClass().getName() + " is not a " +
" view that can be bounds by this SimpleAdapter");
}
}
}
}
}
然后像自定义ListView的步骤一样使用就行了,只是把SimpleAdapter替换为CustomImageAdapter,Map中图片项的value变为Bitmap类型了。
四、Item使用不同布局的ListView
通过前面的例子可以看到,ListView的所有item使用的都是相同的布局,如果想使用不同的布局呢?
这个例子是我从以前做的音乐播放器代码里找的,效果图:
MP3的封面图片突然不显示了,不知道咋回事。
步骤:
1、在Map中存放的键值对中多增加一项布局类型:
/**
* 根据playlistId获得歌曲列表,用于ListView显示
*
* @param playlistId
* 0表示所有歌曲
* @return
*/
private List<Map<String, Object>> getSongList(int playlistId) {
// 取得符合条件的所有歌曲
List<Song> songs = getSongs(playlistId);
// 构造SongList的数据
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> map1 = new HashMap<String, Object>();
//这里的Type是用于在getView方法中判断并构造布局的
map1.put("type", TYPE_BTN);
map1.put("id", ID_RANDOM_PLAY);
map1.put("mainlist_btn_name", "随机播放");
map1.put("mainlist_btn_img", R.drawable.list_random_icon);
list.add(map1);
if (songs != null) {
int size = songs.size();
for (int i = 0; i < size; i++) {
Song song = songs.get(i);
Map<String, Object> map = new HashMap<String, Object>();
//这里的Type是用于在getView方法中判断并构造布局的
map.put("type", TYPE_SONG_LIST);
map.put("id", song.getId());
Bitmap bm = MusicHelper.getArtwork(this, song.getId(), song
.getAlbumId(), true);
map.put("songlist_cover_img", bm);
map.put("songlist_song_name", song.getTitle());
map.put("songlist_song_album", song.getAlbum());
map.put("songlist_song_artist", song.getArtist());
list.add(map);
}
}
return list;
}
2、为不同的item布局分别定义不同的布局文件。
list_songs.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/songlist_cover_img"
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="6dip"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/songlist_cover_img"
android:layout_alignParentRight="true"
android:layout_centerVertical="true">
<TextView
android:id="@+id/songlist_song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:layout_marginLeft="6dip"
android:layout_marginBottom="3dip"
android:textSize="24dip"
android:textColor="@drawable/black">
</TextView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dip">
<TextView
android:id="@+id/songlist_song_album"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_marginLeft="6dip"
android:textSize="10dip"
android:textColor="@drawable/black">
</TextView>
<TextView
android:id="@+id/songlist_song_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_marginRight="6dip"
android:layout_alignParentRight="true"
android:textSize="10dip"
android:textColor="@drawable/black">
</TextView>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
list_main_btn.xml:
<?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="fill_parent"
>
<TextView
android:id="@+id/mainlist_btn_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="6dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_gravity="center_vertical"
android:textSize="28dip"
android:textColor="@drawable/black"/>
<ImageView
android:id="@+id/mainlist_btn_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="6dip"
/>
</LinearLayout>
3、自定义Adapter类SongListAdapter,继承自BaseAdapter,重写getView方法,在该方法中使用LayoutInflator,根据map中定义的布局类型,构造对应的布局:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
//产生一个View
View view = null;
//根据type不同的数据类型构造不同的View,0代表歌曲,1代表按钮菜单
//这个list是ListView的数据项集合,是通过构造方法传递进来的
int type = (Integer)list.get(position).get("type");
if(0 == type) {
view = inflater.inflate(R.layout.list_songs, null);
//获取songlist_cover_img
ImageView songlist_cover_img = (ImageView)view.findViewById(R.id.songlist_cover_img);
songlist_cover_img.setImageBitmap((Bitmap)list.get(position).get("songlist_cover_img"));
//获取songlist_song_name
TextView songlist_song_name = (TextView)view.findViewById(R.id.songlist_song_name);
String song_name = list.get(position).get("songlist_song_name").toString();
songlist_song_name.setText(song_name);
//获取songlist_song_album属性
TextView songlist_song_album = (TextView)view.findViewById(R.id.songlist_song_album);
String song_album = list.get(position).get("songlist_song_album").toString();
songlist_song_album.setText(song_album);
//获取songlist_song_artist属性
TextView songlist_song_artist = (TextView)view.findViewById(R.id.songlist_song_artist);
String song_artist = list.get(position).get("songlist_song_artist").toString();
songlist_song_artist.setText(song_artist);
} else if(1 == type) {
view = inflater.inflate(R.layout.list_main_btn, null);
//获取按钮菜单的mainlist_btn_name属性
TextView mainlist_btn_name = (TextView)view.findViewById(R.id.mainlist_btn_name);
String btn_name = list.get(position).get("mainlist_btn_name").toString();
mainlist_btn_name.setText(btn_name);
//获取mainlist_btn_img
ImageView mainlist_btn_img = (ImageView)view.findViewById(R.id.mainlist_btn_img);
int resId = (Integer)list.get(position).get("mainlist_btn_img");
mainlist_btn_img.setImageResource(resId);
} else {
}
return view;
}
3、为ListView设置adapter:
list = getSongList(playlistId);
SongListAdapter adapter = new SongListAdapter(list, this);
songList.setAdapter(adapter);
标题说的不是很清楚,假设有一个activity,activity中有一个Button和一个TextView,点击按钮,弹出Dialog,对话框中有一个ListView,选中ListView中的某一项,关闭对话框,更新activity中EditText的值为你选中项的值。
分析这个问题,假设Dialog为AlertDialog,你可以直接在Button的OnClickListener事件中创建Dialog,并在AlertDialog的setPositiveButton或其他按钮监听器中完成对EditText值的更新;如果Dialog为自定义Dialog,你也完全可以将这个自定义Dialog以Activity内部类的方式实现,这样在Dialog中ListView的选择事件监听器中你仍然可以访问到EditText。
但是我不喜欢以这样的方式来实现,不管是代码复杂度的原因还是耦合的原因,总之我还是喜欢将自定义Dialog使用单独的类来创建。这样的话,在Dialog中你就访问不到Activity的EditText了,除非你将EditText的设为public(这当然不是一个好方法)。 网上查了下,发现可以通过回调函数来实现,仍然使用上一篇文章当中用到的自定义对话框作为例子,具体步骤如下:
1、新建一个接口作为Dialog的监听器,并在接口中声明回调函数:
/**
* 自定义Dialog监听器
* @author Kael.Chen
*
*/
public interface PriorityListener {
/**
* 回调函数,用于在Dialog的监听事件触发后刷新Activity的UI显示
*/
public void refreshPriorityUI();
}
2、为自定义Dialog增加带监听器参数的构造函数:
private PriorityListener listener;
public PriorityDlg(Context context) {
super(context);
this.context = context;
// TODO Auto-generated constructor stub
}
public PriorityDlg(Context context, int theme) {
super(context, theme);
this.context = context;
}
public PriorityDlg(Context context, int theme, PriorityListener listener) {
this(context, theme);
this.listener = listener;
}
3、在Dialog中需要的地方去调用回调函数,比如在ListView的选择事件触发时:
dlg_priority_lvw.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
dismiss();
listener.refreshPriorityUI();
}
});
4、然后你在Activity中使用带监听器参数的构造函数去实例化自定义Dialog,并实现监听器中声明的回调函数就可以了:
//为优先级选择按钮增加监听器
task_simple_form_priority.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
//弹出任务优先级选择对话框
PriorityDlg dlg = new PriorityDlg(SimpleTaskActivity.this, R.style.dlg_priority, new PriorityDlg.PriorityListener() {
@Override
public void refreshPriorityUI() {
//这里就是用来刷新Activity的UI显示的,如果你需要用到从Dialog传回的数据,你可以把该数据存储在全局变量中或者作为回调函数的参数传递进来
Toast.makeText(SimpleTaskActivity.this, "完成选择", Toast.LENGTH_SHORT).show();
}
});
dlg.show();
}
});
Dialog是android开发过程中最常用到的组件之一,它包括以下几种类型:
- 警告对话框:Alertialog
- 进度对话框:ProgressDialog
- 日期选择对话框:DatePickerDialog
- 时间选择对话框:TimePickerDialog
- 自定义对话框:从Dialog继承
Dialog的创建方式有两种:
一是直接new一个Dialog对象,然后调用Dialog对象的show和dismiss方法来控制对话框的显示和隐藏。
二是在Activity的onCreateDialog(int id)方法中创建Dialog对象并返回,然后调用Activty的showDialog(int id)和dismissDialog(int id)来显示和隐藏对话框。
区别在于通过第二种方式创建的对话框会继承Activity的属性,比如获得Activity的menu事件等。
使用AlertDialog可以创建普通对话框、带列表的对话框以及带单选按钮和多选按钮的对话框。
普通对话框
效果如下:

代码:
//创建builder
AlertDialog.Builder builder = new AlertDialog.Builder(DialogSampleActivity.this);
builder.setTitle("普通对话框") //标题
.setIcon(R.drawable.ic_launcher) //icon
.setCancelable(false) //不响应back按钮
.setMessage("这是一个普通对话框") //对话框显示内容
//设置按钮
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogSampleActivity.this, "点击了确定按钮", Toast.LENGTH_SHORT).show();
}
})
.setNeutralButton("中立", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogSampleActivity.this, "点击了中立按钮", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogSampleActivity.this, "点击了取消按钮", Toast.LENGTH_SHORT).show();
}
});
//创建Dialog对象
AlertDialog dlg = builder.create();
return dlg;
带列表的对话框
效果图:

代码:
final CharSequence[] items = { "Item1", "Item2", "Item3" };
// 创建builder
AlertDialog.Builder builder = new AlertDialog.Builder(
DialogSampleActivity.this);
builder.setTitle("列表对话框") // 标题
.setIcon(R.drawable.ic_launcher) // icon
.setCancelable(false) // 不响应back按钮
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogSampleActivity.this,
"选择了" + items[which], Toast.LENGTH_SHORT)
.show();
}
});
// 创建Dialog对象
AlertDialog dlg = builder.create();
return dlg;
带单选按钮的列表对话框
只需将setItems替换为:
.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogSampleActivity.this,
"选择了" + items[which], Toast.LENGTH_SHORT)
.show();
}
});
这里多了一个参数-1,代表默认选中第几项,-1表示默认不选中
带复选框的列表对话框
只需将setItems替换为:
.setMultiChoiceItems(items, checked, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Toast.makeText(DialogSampleActivity.this,
"选择了" + items[which], Toast.LENGTH_SHORT)
.show();
}
});
参数checked伟boolean数组,表示默认哪些复选框是被选中的。
另外,如果你想要获取list中哪些项是被选中的,你需要:
//获得ListView
ListView list = dlg.getListView();
//判断第i项是否被选中,为真表示被选中,为假表示没有选中
list.getCheckedItemPositions().get(i)
日期选择对话框
效果图:

代码:
Calendar calendar = Calendar.getInstance();
DatePickerDialog.OnDateSetListener dateListener =
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker datePicker,
int year, int month, int dayOfMonth) {
Toast.makeText(DialogSampleActivity.this,
year + "年" + (month+1) + "月" + dayOfMonth + "日", Toast.LENGTH_SHORT)
.show();
}
};
DatePickerDialog dlg = new DatePickerDialog(
DialogSampleActivity.this,
dateListener,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
return dlg;
时间选择对话框
效果图:

代码:
Calendar calendar = Calendar.getInstance();
TimePickerDialog.OnTimeSetListener timeListener =
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker timePicker, int hourOfDay, int minute) {
Toast.makeText(DialogSampleActivity.this,
hourOfDay + ":" + minute, Toast.LENGTH_SHORT).show();
}
};
TimePickerDialog dlg = new TimePickerDialog(
DialogSampleActivity.this,
timeListener,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
true);
return dlg;
自定义对话框
效果图:

步骤:
1、创建对话框的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 标题栏 -->
<LinearLayout
android:id="@+id/dlg_priority_titlebar"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<ImageView
android:src="@drawable/star_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dip"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择任务优先级"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<!-- 任务优先级 -->
<ListView
android:id="@+id/dlg_priority_lvw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/dlg_priority_titlebar"
android:background="@drawable/layout_home_bg">
</ListView>
</RelativeLayout>
2、因为该布局中使用了自定义的ListView,所以再为ListView创建布局文件
<?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="fill_parent"
>
<ImageView
android:id="@+id/list_priority_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dip"
/>
<TextView
android:id="@+id/list_priority_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="28dip"
android:textColor="@drawable/black"/>
</LinearLayout>
3、创建自定义Dialog类PriorityDlg继承自Dialog
public class PriorityDlg extends Dialog {
private Context context;
private ListView dlg_priority_lvw = null;
public PriorityDlg(Context context) {
super(context);
this.context = context;
// TODO Auto-generated constructor stub
}
public PriorityDlg(Context context, int theme) {
super(context, theme);
this.context = context;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//设置对话框使用的布局文件
this.setContentView(R.layout.dlg_priority);
dlg_priority_lvw = (ListView) findViewById(R.id.dlg_priority_lvw);
// 设置ListView的数据源
SimpleAdapter adapter = new SimpleAdapter(context, getPriorityList(),
R.layout.lvw_priority, new String[] { "list_priority_img",
"list_priority_value" }, new int[] {
R.id.list_priority_img, R.id.list_priority_value });
dlg_priority_lvw.setAdapter(adapter);
// 为ListView设置监听器
dlg_priority_lvw
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
}
});
}
/**
* 得到ListView数据源
*
* @return
*/
private List<HashMap<String, Object>> getPriorityList() {
List<HashMap<String, Object>> priorityList = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map1 = new HashMap<String, Object>();
map1.put("list_priority_img", R.drawable.priority_not_important);
map1.put("list_priority_value", context.getResources().getString(
R.string.dlg_priority_not_important));
priorityList.add(map1);
HashMap<String, Object> map2 = new HashMap<String, Object>();
map2.put("list_priority_img", R.drawable.priority_general);
map2.put("list_priority_value", context.getResources().getString(
R.string.dlg_priority_general));
priorityList.add(map2);
HashMap<String, Object> map3 = new HashMap<String, Object>();
map3.put("list_priority_img", R.drawable.priority_important);
map3.put("list_priority_value", context.getResources().getString(
R.string.dlg_priority_important));
priorityList.add(map3);
HashMap<String, Object> map4 = new HashMap<String, Object>();
map4.put("list_priority_img", R.drawable.priority_very_important);
map4.put("list_priority_value", context.getResources().getString(
R.string.dlg_priority_very_important));
priorityList.add(map4);
return priorityList;
}
}
4、创建自定义对话框
PriorityDlg dlg = new PriorityDlg(SimpleTaskActivity.this, R.style.dlg_priority);
return dlg;
这里的R.style.dlg_priority设置了对话框使用的样式文件,只是让对话框去掉标题栏,当然你也可以通过代码来完成这种效果:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 对话框样式 -->
<style name="dlg_priority" parent="@android:Theme.Dialog">
<item name="android:windowNoTitle">true</item>
</style>
</resources>
到这里自定义对话框的创建就结束了,想要什么样子的对话框完全凭你自己的想像。
Android提供的SharedPreferences类似于Windows程序中常用的ini文件,以Key-Value的形式来保存应用程序的属性设置信息(区分数据类型)。比如,可以用来保存用户上一次的登录信息;可以保存媒体播放程序的音量设置以及上次播放位置等。
在使用SharedPreferences时常用的方法包括(方法具体含义参考官方文档):
-
Context.getSharedPreferences (String name, int mode)
-
Activity.getPreferences (int mode)
-
SharedPreferences.edit();
-
SharedPreferences.getXXX(name, defaultValue);
- SharedPreferences.Editor.putXXX(name, value);
-
SharedPreferences.Editor.commit();
步骤:
保存数据
Log.d("SharedPreferences", "保存数据...");
//获得SharedPreferences对象
SharedPreferences settings = this.getSharedPreferences("shared_file", 0);
//获得可编辑对象
SharedPreferences.Editor editor = settings.edit();
editor.putString("name", "Kael Chen");
editor.putInt("age", 22);
editor.commit();
Log.d("SharedPreferences", "保存数据成功");
读取数据
Log.d("SharedPreferences", "获取数据...");
SharedPreferences settings = this.getSharedPreferences("shared_file", 0);
String name = settings.getString("name", "no name");
int age = settings.getInt("age", 0);
Log.d("SharedPreferences", "name:" + name + ", age:" + age);
Log.d("SharedPreferences", "获取数据成功");
执行代码后,可以看到在data/data/pacakge name/shared_prefs/目录下多了一个shared_file.xml文件,文件名是由getSharedPreferences方法中传入的参数决定的。
文件内容如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">Kael Chen</string>
<int name="age" value="22" />
</map>
可以看到,不同于ini文件的是,这里是严格区分了数据的类型的,所以在getXXX和putXXX相同数据时使用的类型一定要匹配。
另外值得注意的是getSharedPreferences 方法中的mode参数以及getPreferences和getSharedPreferences 的区别。
getPreferences和getSharedPreferences的区别:
1、getPreferences只能由Activity调用,只在调用它的Activity范围内有效。
2、getSharedPreferences是由Context调用,在包范围内有效。可以这样理解,AndroidManifest.xml文件中定义了包的位置,比如我的测试程序中定义的包为cn.kael.sample,那么android会建立data/data/cn.kael.sample/shared_prefs/的目录,并在其下产生SharePreferences文件。因此如果已经在一个地方使用了SharedPrefernces,并创建了相应的SharedPrefernces文件,然后你想在另一个地方使用该SharedPrefernces文件,你只需要根据文件的路径规则判断在新的地方使用SharedPrefernces生成的路径是不是和之前SharedPrefernces文件的路径一致,就可以知道两者之间是否能够共用SharedPrefernces,即使它们不属于同一个应用程序(因为android是根据包名来决定SharedPrefernces文件的创建位置的,和应用程序名无关)。
关于mode参数:
要想在程序B想访问程序A的SharedPrefernces文件:
首先需要程序A在创建SharedPrefernces时使用MODE_WORLD_WRITEABLE或者MODE_WORLD_READABLE作为mode参数:
SharedPreferences settings = this.getSharedPreferences("shared_file", Context.MODE_WORLD_WRITEABLE);
然后在程序B中需要先创建程序A的context对象,然后再通过这个context对象来调用getSharedPreferences方法:
Context contextA = createPackageContext("cn.kael.sample",
Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences settings = contextA.getSharedPreferences(
"shared_file", Context.MODE_WORLD_WRITEABLE);
但是然我不明白的是无论这个mode是设置为MODE_WORLD_WRITEABLE还是MODE_WORLD_READABLE,在程序B中都可以对程序A的SharedPrefernces文件进行读写,并且读写后SharedPrefernces文件发生的改变也只在程序B中有效。而且Logcat中会出现下面一个error信息:
11-30 16:52:00.965: ERROR/ApplicationContext(25124): Couldn't rename file /data/data/cn.kael.sample/shared_prefs/shared_file.xml to backup file /data/data/cn.kael.sample/shared_prefs/shared_file.xml.bak
知道原因的请告诉我,谢谢!!!
复合消费主要面向于交通领域的应用,既可以满足高速公路不停车收费系统的需求,也可以满足城市公共交通的应用需求。
复合消费相对于普通消费来说多了一个对复合记录文件的更新。比如在高速公路入口或者乘客上车时先做一次金额为0的复合消费,从而正确记录入口或者上车站点的相关信息,之后在高速公路出口或者乘客下车时,读取之前记录的信息,计算出实际费用,做一次完整的复合消费交易,这样就可以实现高速公路或者公共交通中的分段收费功能。
具体消费流程:
1、 选择要充值的应用
2、 得到PSAM卡或加密机中得到消费子密钥
3、 进行复合交易初始化
4、 计算过程密钥
5、 利用过程密钥计算MAC1,这里交易类型为09
6、 更新复合应用数据缓存(P1为卡中复合记录文件原记录的复合消费标志)
7、 发送MAC1进行消费
ps:复合记录文件的命令报文数据域的byte7为EF(使用线路保护读,读写都使用标识为00的密钥)
充值
脱机充值
1、 选择要充值的应用目录。
2、 验证口令密钥。
3、 取得充值密钥(这里使用加密机提供的分散指令,用ATS作为分散因子对00B0密钥进行分散得到充值密钥)。
4、 圈存交易初始化。
5、 生成过程密钥。
6、 用过程密钥计算MAC1与初始化交易返回的MAC1进行比对,若一致,则继续执行。
7、 用过程密钥计算MAC2。
8、 使用圈存指令将MAC2发送给CPU卡,完成充值交易。
联机充值
1、2、3、4步同脱机充值。
将圈存交易初始化返回的数据发送给后台,后台算出过程密钥,并得到MAC1。由后台对MAC1作比较,如果一致,则后台再算出MAC2,返回给前台,前台使用圈存指令将MAC2发送给CPU卡,完成充值交易。
消费
1、 选择要消费的应用目录。
2、 取得消费密钥(这里使用加密机提供的分散指令,用城市代码和ATS作为分散因子对00B1密钥进行分散得到消费密钥)。
3、 消费交易初始化
4、 生成过程密钥
5、 用过程密钥计算MAC1
6、 用消费指令将MAC1发送给CPU卡进行消费交易
7、 得到TAC和MAC2,验证…



