Android控件开发——ListView
上篇博客解决了Android客户端通过WebService与服务器端程序进行交互的问题,这篇博客重点关注两个问题,一个是Android应用程序如何与本机文件型数据库SQLite进行交互,另一问题则是如何在ListView中按照我们想要的界面效果进行展示。限于篇幅这篇重点讲ListView,下篇博客重点阐述SQLite。
ListView是一个常用的数据显示控件,假设我们要做一个简单的界面,如图所示。
这张图是我直接从Android平板电脑(Android 4.2.2)上面截图下来的,就是一个普通的列表,能够点击报名按钮获取到对应行的信息。
这里面显示的数据是我从SQLite数据库中查询出来的,封装的类的代码如下:
activity_main.xml布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <LinearLayout android:id="@+id/head" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="岗位名称" android:textSize="24sp" android:width="150dip" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="岗位数量" android:textSize="24sp" android:width="150dip" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发布日期" android:textSize="24sp" android:width="150dip" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="岗位描述" android:textSize="24sp" android:width="550dip" /> </LinearLayout> <ListView android:id="@id/android:list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/head" > </ListView> </RelativeLayout>
可以看到这是一个相对布局,里面有一个线性布局,线性布局里面又放置了4个TextView作为ListView数据的标题。下面直接是一个ListView控件,由于这是相对布局,为了让ListView显示在“表头”下面,我们设置了layout_below属性。此外要注意ListView的id的写法。
接着按照界面的要求,我们准备一下ListView加载布局文件的内容,我们起名为:list_item.xml。
list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:width="150dip" /> <TextView android:id="@+id/num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:width="150dip" /> <TextView android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:width="150dip" /> <TextView android:id="@+id/description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:width="550dip" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:focusable="false" android:focusableInTouchMode="false" android:text="报名" android:width="150dip" android:textSize="24sp" /> </LinearLayout>
这也是一个普通的线性布局,设置orientation为horizontal(水平)。
布局文件准备好,下面我们准备写代码了。
我们让MainActivity这个类继承自ListActivity,完整的代码如下:
public class MainActivity extends ListActivity { List<Map<String, Object>> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); list = new ArrayList<Map<String, Object>>(); //初始化SQLite数据库操作类对象 MyDatabaseHelper dbHelper = new MyDatabaseHelper(MainActivity.this); //查询数据库返回Cursor(游标)对象 Cursor cursor = dbHelper.getReadableDatabase().query("jobInfo", new String[] { "name", "num", "date", "description" }, null, null, null, null, "name"); //将结果集封装到List<Map<String,Object>>数据结构当中 while (cursor.moveToNext()) { Map<String, Object> map = new HashMap<String, Object>(); map.put("name", cursor.getString(0)); map.put("num", cursor.getInt(1)); map.put("date", cursor.getString(2)); map.put("description", cursor.getString(3)); map.put("btn", R.drawable.ic_launcher); list.add(map); } //查询完毕,记得及时关闭数据库链接 cursor.close(); MyButtonAdapter adapter = new MyButtonAdapter(MainActivity.this, list, R.layout.list_item, new String[] { "name", "num", "date", "description", "btn" }, new int[] { R.id.name, R.id.num, R.id.date, R.id.description, R.id.btn }); //给ListView设置数据填充适配器 ListView listView = (ListView) findViewById(android.R.id.list); listView.setAdapter(adapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { //ListView的 @SuppressWarnings("unchecked") Map<String, Object> map = (HashMap<String, Object>) l .getItemAtPosition(position); Toast.makeText(MainActivity.this, "您点击了:" + map.get("name").toString() + "岗位!", Toast.LENGTH_SHORT).show(); } public class MyButtonAdapter extends BaseAdapter { private class ButtonViewHolder { TextView name; TextView num; TextView date; TextView description; Button btn; } private Context mContext; private List<Map<String, Object>> mList; private ButtonViewHolder holder; private LayoutInflater mInflater; private String[] keyString; private int[] valueViewID; // 构造函数初始化变量 public MyButtonAdapter(Context context, List<Map<String, Object>> list, int resource, String[] from, int[] to) { this.mContext = context; this.mList = list; // 获得布局文件对象 mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); keyString = new String[from.length]; valueViewID = new int[to.length]; // 复制数组 System.arraycopy(from, 0, keyString, 0, from.length); System.arraycopy(to, 0, valueViewID, 0, to.length); } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } /** * 从list中移除某一项 * * @param position */ public void removeItem(int position) { list.remove(position); // 通知数据集已改变,请求自刷新 this.notifyDataSetChanged(); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView != null) { holder = (ButtonViewHolder) convertView.getTag(); } else { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ButtonViewHolder(); holder.name = (TextView) convertView .findViewById(valueViewID[0]);// 岗位名称 holder.num = (TextView) convertView .findViewById(valueViewID[1]);// 岗位数量 holder.date = (TextView) convertView .findViewById(valueViewID[2]);// 发布日期 holder.description = (TextView) convertView .findViewById(valueViewID[3]);// 岗位描述 holder.btn = (Button) convertView.findViewById(valueViewID[4]);// 报名按钮 convertView.setTag(holder); } Map<String, Object> appInfo = mList.get(position); if (appInfo != null) { String aname = (String) appInfo.get(keyString[0]); Integer anum = (Integer) appInfo.get(keyString[1]); String adate = (String) appInfo.get(keyString[2]); String adescription = (String) appInfo.get(keyString[3]); holder.name.setText(aname); holder.num.setText(anum + ""); holder.date.setText(adate); holder.description.setText(adescription); // 报名按钮事件 holder.btn.setOnClickListener(new lvButtonListener(position)); } return convertView; } class lvButtonListener implements OnClickListener { private int position; lvButtonListener(int pos) { position = pos; } @Override public void onClick(View v) { int vid = v.getId(); if (vid == holder.btn.getId()) { String result = "" + "岗位名称:" + list.get(position).get("name") + "\r\n" + "岗位人数:" + list.get(position).get("num") + "\r\n" + "发布日期:" + list.get(position).get("date") + "\r\n" + "岗位描述:" + list.get(position).get("description") + "\r\n"; new AlertDialog.Builder(MainActivity.this) .setTitle("提示") .setIcon(R.drawable.ic_launcher) .setMessage(result + "\r\n" + "您确定要申请该岗位吗?") .setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { Toast toast = Toast .makeText( MainActivity.this, "您点击了" + getResources() .getString( R.string.positive) + "按钮,申请了" + list.get( position) .get("name") + "的岗位!", Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } }) .setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int which) { Toast toast = Toast .makeText( MainActivity.this, "您点击了" + getResources() .getString( R.string.negative) + "按钮", Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } }).create().show(); // 如果要删除行,可以调用此方法 // removeItem(position); } } } } }
上面的代码有几个知识点需要注意:
1、SQLite数据库的查询操作
我们通过getReadableDatabase().query方法执行了查询操作,返回Cursor(游标,与JDBC中的ResultSet类似)对象。
2、ListView控件使用(重点)
我们参考了SimpleAdapter默认的构造函数的方法,创建了自定义的MyButtonAdapter类,在显示数据的同时,能够给每一行的按钮绑定点击事件。
3、弹出提示框
弹出提示框的代码很长,完全可以封装到一个方法中,简化代码。这里完整的列出来,目的就是体验一下设计思路。经过观察我们发现,这就是所谓的“链式编程”,可以通过连续的".",设置参数(控制显示效果)。
strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="positive">确定</string> <string name="negative">取消</string> </resources>
最终在pad上面的执行效果如下: