1. ListView介绍
解决大量的相似的数据显示问题
采用了MVC模式:
M: model (数据模型)
V: view (显示的视图)
C: controller 控制器
入门案例:
acitivity_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" tools:context=".MainActivity" > <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/lv" /> </RelativeLayout>
MainActivity.java:
package com.itheima.listviewdemo; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends Activity { //listview的视图 V view private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.lv); //设置数据适配器 数据控制器 adapter 相当于C controller lv.setAdapter(new MyAdapter()); } private class MyAdapter extends BaseAdapter{ //控制器 /** * 数据集合里面一共有多少条记录 */ @Override public int getCount() { return 5000000; } /** * 返回某个指定位置的view对象 */ @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv = new TextView(MainActivity.this); tv.setTextSize(25); tv.setText("我是第"+position+"个被生出来的"); System.out.println("getview --positon:"+position); //M model 数据模型, textview return tv; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } } }
这里本来我们的MyAdapter是应该实现接口ListAdapter,但是实现接口ListAdapter要实现的方法太多,不太方便使用,于是必然衍生出更加方便的API接口,那这里就是BaseAdapter(还有其他的),我们追踪至源码,知道:BaseAdapter 是实现ListAdapter接口的。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { ………………}
通常默认实现类命名:
(1)BaseXXX
(2)BasicXXX
(3)SimpleXXX
(4)DefaultXXX
2. 由于像GridView、ListView等这样的列表组件是采用MVC模式的,因此不能直接通过列表组件对象对数据进行增、删、改操作。但是我们可以换个思路解决这个问题:
(1)首先我们直接对数据源中数据进行增、删、改操作
(2)然后调用BaseAdapter.notifyDataSetChanged方法通知列表组件进行数据更新。在调用notifyDataSetChanged方法的时候,系统会立刻调用这个BaseAdapter.getView()方法来获取当前显示的列表项的View对象,这样就更新的列表组件中显示的数据。
3. 下面通过一个案例来演示这个类似ListView列表组件数据修改的过程:
案例的运行效果图如下:
(1)首先还是布局文件,这里有主布局文件activity_main.xml,以及其他辅助的布局文件:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/add_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:layout_marginTop="5dp" android:text="添加数据" android:textColor="#99ff0000"> </Button> <Button android:id="@+id/delete_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:layout_marginTop="5dp" android:text="删除数据" android:textColor="#99ff0000"> </Button> <Button android:id="@+id/edit_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:layout_marginTop="5dp" android:text="修改数据" android:textColor="#99ff0000"> </Button> <Button android:id="@+id/query_id" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:layout_marginRight="25dp" android:layout_marginTop="5dp" android:text="查询数据" android:textColor="#99ff0000"> </Button> <ListView android:id="@+id/show_result" android:layout_width="fill_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
主布局文件效果如下:
input_add.xml:添加数据
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/input_add_string" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" > </EditText> </LinearLayout>
input_delete.xml:删除数据
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/input_delete_number" android:layout_width="fill_parent" android:layout_height="wrap_content" android:digits="0123456789" android:singleLine="true" > </EditText> </LinearLayout>
input_edit.xml:修改数据
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="输入要修改的索引" > </TextView> <EditText android:id="@+id/input_edit_number" android:layout_width="fill_parent" android:layout_height="wrap_content" android:digits="0123456789" android:singleLine="true" > </EditText> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="内容修改为" > </TextView> <EditText android:id="@+id/input_edit_string" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" > </EditText> </LinearLayout>
items.xml:列表子项目样式布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/viewspot" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" android:padding="6dip" android:textColor="#88000000" android:textSize="25dp" > </TextView> <ImageView android:id="@+id/add" android:layout_width="100dip" android:layout_height="wrap_content" android:padding="10dip" android:src="@drawable/right" > </ImageView> </LinearLayout>
(2)主要的class文件,UpdateListViewActivity.java,如下:
package com.himi.updatelistview; import java.util.ArrayList; import java.util.HashMap; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.Toast; public class UpdateListViewActivity extends Activity { private Button addBtn; private Button deleteBtn; private Button editBtn; private Button queryBtn; private ListView listview; // 数组 private SimpleAdapter listItemAdapter; private ArrayList<HashMap<String, Object>> listItem = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获取控件 addBtn = (Button) findViewById(R.id.add_id); deleteBtn = (Button) findViewById(R.id.delete_id); editBtn = (Button) findViewById(R.id.edit_id); queryBtn = (Button) findViewById(R.id.query_id); listview = (ListView) findViewById(R.id.show_result); // 初始化数据 init(); // 设置控件事件监听 addBtn.setOnClickListener(addClick); deleteBtn.setOnClickListener(deleteClick); editBtn.setOnClickListener(editClick); queryBtn.setOnClickListener(queryClick); } // 添加事件响应 OnClickListener addClick = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub // 加载输入框的布局文件 LayoutInflater inflater = (LayoutInflater) UpdateListViewActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); final LinearLayout layout = (LinearLayout) inflater.inflate( R.layout.input_add, null); // 弹出的对话框 new AlertDialog.Builder(UpdateListViewActivity.this) /* 弹出窗口的最上头文字 */ .setTitle("添加一条数据") /* 设置弹出窗口的图式 */ .setIcon(android.R.drawable.ic_input_add) /* 设置弹出窗口的信息 */ .setMessage("请输入添加的内容") .setView(layout) .setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialoginterface, int i) { EditText inputStringr = (EditText) layout .findViewById(R.id.input_add_string); String str = inputStringr.getText() .toString(); if (str == null || str.equals("")) { Toast.makeText(getApplicationContext(), "添加的内容不能为空", Toast.LENGTH_SHORT) .show(); } else { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("viewspot", str); map.put("add", R.drawable.right); listItem.add(0, map); // 如果在前面添加一条数据添加 // listItem.add(map); listItemAdapter.notifyDataSetChanged(); Toast.makeText( UpdateListViewActivity.this, "添加的一条数据为:" + str + "", Toast.LENGTH_SHORT).show(); } } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { /* 设置跳出窗口的返回事件 */ public void onClick( DialogInterface dialoginterface, int i) { Toast.makeText(UpdateListViewActivity.this, "取消了删除数据", Toast.LENGTH_SHORT) .show(); } }).show(); } }; // 删除事件响应 OnClickListener deleteClick = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub /** * listItem.clear();清空所有数据 * * */ /* * listItem.clear(); * listItemAdapter.notifyDataSetChanged(); */ // 加载输入框的布局文件 LayoutInflater inflater = (LayoutInflater) UpdateListViewActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); final LinearLayout layout = (LinearLayout) inflater.inflate( R.layout.input_delete, null); // 弹出的对话框 new AlertDialog.Builder(UpdateListViewActivity.this) /* 弹出窗口的最上头文字 */ .setTitle("删除一条数据") /* 设置弹出窗口的图式 */ .setIcon(android.R.drawable.ic_input_delete) /* 设置弹出窗口的信息 */ .setMessage("请输入删除的索引") .setView(layout) .setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialoginterface, int i) { EditText inputNumber = (EditText) layout .findViewById(R.id.input_delete_number); String str = inputNumber.getText() .toString(); if (str == null || str.equals("")) { Toast.makeText(getApplicationContext(), "请输入一个数字", Toast.LENGTH_SHORT) .show(); } else { int number = Integer.valueOf(str); int size = listItem.size(); // 判断数字是否超出数组索引范围 if (number >= size) { Toast.makeText( getApplicationContext(), "没有找到删除的数据索引", Toast.LENGTH_SHORT).show(); } else { String value = listItem.get(number) .toString(); listItem.remove(number); listItemAdapter .notifyDataSetChanged(); Toast.makeText( UpdateListViewActivity.this, "删除的数据为:" + value + "", Toast.LENGTH_SHORT).show(); } } } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { /* 设置跳出窗口的返回事件 */ public void onClick( DialogInterface dialoginterface, int i) { Toast.makeText(UpdateListViewActivity.this, "取消了删除数据", Toast.LENGTH_SHORT) .show(); } }).show(); } }; // 修改事件响应 OnClickListener editClick = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub // 加载输入框的布局文件 LayoutInflater inflater = (LayoutInflater) UpdateListViewActivity.this .getSystemService(Context.LAYOUT_INFLATER_SERVICE); final LinearLayout layout = (LinearLayout) inflater.inflate( R.layout.input_edit, null); // 弹出的对话框 new AlertDialog.Builder(UpdateListViewActivity.this) /* 弹出窗口的最上头文字 */ .setTitle("修改一条数据") /* 设置弹出窗口的图式 */ .setIcon(android.R.drawable.ic_dialog_alert) /* 设置弹出窗口的信息 */ .setMessage("请输入修改的索引及内容") .setView(layout) .setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick( DialogInterface dialoginterface, int i) { EditText inputEditNumber = (EditText) layout .findViewById(R.id.input_edit_number); String numberStr = inputEditNumber .getText().toString(); EditText inputEditString = (EditText) layout .findViewById(R.id.input_edit_string); String editStr = inputEditString.getText() .toString(); if (numberStr == null || numberStr.equals("")) { Toast.makeText(getApplicationContext(), "请输入要修改的索引", Toast.LENGTH_SHORT) .show(); } else { int number = Integer.valueOf(numberStr); int size = listItem.size(); // 判断数字是否超出数组索引范围 if (number >= size) { Toast.makeText( getApplicationContext(), "没有找到修改的数据索引", Toast.LENGTH_SHORT).show(); } else { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("viewspot", editStr); map.put("add", R.drawable.right); listItem.set(number, map); listItemAdapter .notifyDataSetChanged(); Toast.makeText( UpdateListViewActivity.this, "数据修改为:" + editStr + "", Toast.LENGTH_SHORT).show(); } } } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { /* 设置跳出窗口的返回事件 */ public void onClick( DialogInterface dialoginterface, int i) { Toast.makeText(UpdateListViewActivity.this, "取消了修改数据", Toast.LENGTH_SHORT) .show(); } }).show(); } }; // 查询事件响应 OnClickListener queryClick = new OnClickListener() { public void onClick(View v) { // TODO Auto-generated method stub // 查询数据 listItemAdapter.notifyDataSetChanged(); } }; // 初始化数据 private void init() { listItem = new ArrayList<HashMap<String, Object>>(); for (int i = 0; i < 12; i++) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("viewspot", "雪饮狂刀:" + i); map.put("add", R.drawable.right); listItem.add(map); } listItemAdapter = new SimpleAdapter(getApplicationContext(), listItem,// 数据源 R.layout.items, new String[] { "viewspot", "add" }, new int[] { R.id.viewspot, R.id.add }); listview.setAdapter(listItemAdapter); } }
注意事项:
->1:Inflate()和findViewById()区分使用,以及两种之间的逻辑关系?
findViewById()只能找出当前布局中的组件,即setConentView()的那个layout里的组件.
如果你的Activity里用到别的layout,比如对话框layout,你还要设置这个layout上的其他组件的内容,你就必须用inflate()方法先将对话框的layout找出来,然后再用findViewById()找到它上面的其它组件。
->2:对数据源的增删改查,这只是对数据的操作而已,手机上面的ListView列表视图组件必须刷新显示,需要调用到notifyDataSetChanged()方法,就可以实现对listview数据的更新。如果涉及到线程,则需要在UI的线程更新。
->3:DialogInterface.OnClickListener接口实现方法Onclick()
public abstract void onClick (DialogInterface dialog, int which) :对话框中的按钮按下时执行的方法.
参数: dialog 发生按钮按下事件的对话框(当一个Activity有多个对话框时,利用这个参数确定特定的对话框)
which 按下的按钮(例如:BUTTON1
) 或按下的条目位置
对于which参数,如下:利用which参数按下的按钮(例如:BUTTON1
) 或按下的条目位置
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Are you sure you want to exit?") .setCancelable(false) .setPositiveButton("Yes",this) .setNegativeButton("No", this); AlertDialog alert = builder.create(); alert.show(); @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub Log.e("---------------", which + ""); switch (which) { case -1: Log.i("info", "你点击的YES~"); AndProgressActivity.this.finish(); break; case -2: Log.i("info", "你点击的NO~"); dialog.cancel(); break; default: break; } }