ListView

来源:互联网 发布:python 字符串数组 编辑:程序博客网 时间:2024/05/22 17:15

在Android应用开发中,ListView可以说是最为常用的一种视图控件,它以垂直列表的方式列出需要列出的列表项,比如说是通讯录里面的联系人、系统设置里面的各个被设置项等。

    而且,ListView可以说是在Android各个视图控件里面,对MVC开发模式体现得最为清晰明了的一个视图控件。我们可以把ListView理解成为一个容器,类似于JavaSE里面的集合,不过,往ListView这个容器里面添加数据的方式并不是传统模式的add(Object data)方法,而是需要一个适配器类(Adapter),该类类似于MVC开发模式里面的控制器(C-Controller),ListView视图控件本身就是一个视图(V-View),而ListView控件里面所要显示的数据项,就是模型(M-Model)。其中,Adapter起了一个控制作用,既然是起了控制作用,为什么并没有将Adapter命名成类似于Cotroller这样的名字呢?原因就是因为,本来我们的ListView视图控件本身与里面所要显示的数据(一般是一个数组或者是集合)之间没有任何的联系,也就是没有任何的耦合,如果硬要将java中的两个对象之间本来没有关系的产生关系,使用适配器模式(Adapter)是一种比较合理的选择,所以这里的Adapter就是起了一个连接ListV视图控件和数据(Data)的作用。

    按照ListView视图控件的开发形式与需要的不同,我们将之分为如下几种:

    1.纯xml配置的方式

    2.ArrayAdapter的方式

    3.单选按钮的ListView

    4.复选按钮的ListView

    5.SimpleAdapter的方式

   

1.纯xml配置的方式

既然ListView是一个视图控件,那么它就可以在布局文件中定义:

list_view_1.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" >
 <ListView
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:entries="@array/weekdays" <!-- 这里是引用了数组资源文件的内容 -->
     />
</LinearLayout>

arrays.xml(数组资源文件)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="weekdays">
        <item>星期日</item>
        <item>星期一</item>
        <item>星期二</item>
        <item>星期三</item>
        <item>星期四</item>
        <item>星期五</item>
        <item>星期六</item>
    </string-array>
</resources>

无需写任何的java代码,直接部署到Android模拟器上运行,如下效果:

[转载]ListView
但是这种方式,仅仅能起到一个展示的效果,对开发真正的应用程序并没有太大的帮助,因为我们要点击里面的具体的列表项去做一定的业务逻辑处理,比如点击“星期五”,去买机票,因为明后天放假了,可以回家看望父母了。

 

2.使用ArrayAdapter

使用ArrayAdapter,可以在java代码中动态地创建列表项和处理具体的点击列表项的事件。

list_view_2.xml

注意:如果使用在代码中定义ListView列表项数组的时候,android:entries="@array/weekdays"该属性就不能再配置了,如果配置,Android不知道到底是以哪边的数据为准了!

<?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" >
 <ListView
     android:id="@+id/listViewId"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     />
</LinearLayout>

 

ListViewActivity2.java源代码

public class ListViewActivity2 extends Activity {
 //定义需要在ListView中显示的数据,这里是数组
 private String[] weekdays;
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.list_view_2);
     //根据资源对象获取资源文件里面定义的数组
     weekdays = getBaseContext().getResources().getStringArray(R.array.weekdays);
     ArrayAdapter<String> aa = new ArrayAdapter<String>(
            ListViewActivity2.this,             //上下文对象
            android.R.layout.simple_list_item_1,//布局文件
            weekdays);                          //数组对象
     //获取布局文件中的ListView
     ListView lv = (ListView)findViewById(R.id.listViewId);
     //将适配器封装的数组适配给ListView
     lv.setAdapter(aa);
        
     //为点击ListView列表项添加事件监听
     lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         Toast.makeText(ListViewActivity2.this,

                        weekdays[position]+"被点击了!",

                        Toast.LENGTH_LONG).show();
     }
     });
    }
}

整个代码并不难理解,weekdays数组,除了可以以硬编码的方式直接写死在代码里之外,还可以通过资源对象(Resources)来从数组资源文件中获得,这种可配的代码方式还是值得推荐的。主要是ArrayAdapter对象的创建稍微有一点的麻烦,它需要三个参数:上下文对象(Context),而我们这里的Activity是继承自Context类的,所以可以以当前对象作为上下文对象(ListViewActivity2.this);布局文件,这里我们使用了系统自带的一个布局文件simple_list_item_1.xml,该文件可以在sdkplatformsandroid-API版本号datareslayout目录下找到,可以稍微看一下该文件:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:paddingLeft="6dip"
    android:minHeight="?android:attr/listPreferredItemHeight"
/>

该文件是一个布局文件,仅仅只有一个TextView文本控件,而正是因为它,所有我们在我们的ListView选项列表中只看到一行数据;数组对象,对应的就是从资源文件拿出来的数组。

具体效果如下:

[转载]ListView

3.单选按钮的ListView

单选按钮的ListView很简单,跟之前第二种的方式几乎一样,就是需要将外观更改一下,需要更改两个地方:

3.1:在创建ArrayAdapter的时候,它的第二个布局文件要换成系统的另外一个布局文件:simple_list_item_single_choice.xml,对应的配置是android.R.layout.simple_list_item_single_choice

3.2:需要为ListView设置一个选择的模式:

listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

其他配置与代码不变,如下图:

[转载]ListView

4.复选按钮的ListView

跟单选按钮的ListView与普通的ListView之间的区别一样,复选按钮的ListView也只需更改外观即可,修改如下:

4.1:在创建ArrayAdapter的时候,它的第二个布局文件要换成系统的另外一个布局文件:simple_list_item_multiple_choice.xml,对应的配置是android.R.layout.simple_list_item_multiple_choice

4.2:需要为ListView设置一个选择的模式:

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

其他配置与代码不变,如下图:

[转载]ListView

5.SimpleAdapter的方式

上述的一些ListView,只能显示一行的数据,而且这一行数据还是由一个控件来提供的(查看创建Adapter时的Android布局文件可以看出来),如果像一个电话通讯录的那个列表框,如下:(这是真实手机的电话通讯录)

[转载]ListView

这样的列表该如何实现呢?至少我们之前的列表方式是实现不了了,因为Android系统提供的布局根本不可能适应千变万化的用户需求,所以这个时候,我们就需要根据自己的需求定义自己的布局文件,让这个布局文件填充到ListView中的一行,因为布局文件改了,之前的ArrayAdapter所提供的数据功能就不能满足要求了,这个时候,我们可以使用SimpleAdapter适配器来满足我们的需求。

首选,定义一个我们自己的布局文件,如下:

list_view_oneline.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp" >

    <ImageView
        android:id="@+id/imageViewId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/rabbit"
        />
   
    <TextView
        android:id="@+id/textViewId1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/imageViewId"
        android:paddingLeft="3dp"
        />
   
    <TextView
        android:id="@+id/textViewId2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/imageViewId"
        android:layout_below="@id/textViewId1"
        android:layout_alignBottom="@id/imageViewId"
        android:paddingLeft="3dp"
        />
   
</RelativeLayout>

主要的Activity类文件如下:

ListViewActivity5.java

public class ListViewActivity5 extends Activity {
    private String[] persons = {"令狐冲","杨过","张无忌","胡斐","韦小宝"};
 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_view_5);
       
        //定义一个集合,用于存放列表项(map),而里面的Map集合存放一项里面的数据
        List<Map<String,Object>> listItems = new ArrayList<Map<String,Object>>();
        //往list中添加数据,模拟一些数据,测试使用
        for(int i=0;i<persons.length;i++){
         Map<String,Object> item = new HashMap<String,Object>();
         item.put("name", persons[i]);
         item.put("phone", "1505188227"+i);
         listItems.add(item);
        }
       
        ListView lv = (ListView)findViewById(R.id.listViewId);
        //创建SimpleAdapter
        SimpleAdapter sa = new SimpleAdapter(

                  ListViewActivity5.this,                       //上下文对象
                  listItems,                                    //数据集合
                  R.layout.list_view_oneline,                   //自定义的布局文件
                  new String[]{"name","phone"},                 //规定了数据集合Map的全部的key
                  new int[]{R.id.textViewId1,R.id.textViewId2});//规定了数据集合Mapkey对应的值应该跟自定义布局的哪一个组件对应
       
        lv.setAdapter(sa);
    }
}

 请注意红色部分的代码,在往List集合里面添加Map对象的时候,创建这个map对象,再往map集合里面添加数据时的key,要与创建SimpleAdapter时的第四个参数里面的数组的元素一致,map的value值正好与创建SimpleAdapter时的第五个参数相匹配。运行之后如下效果:

[转载]ListView
当然了,如果想将数据写的更真实一点,可以再模拟数据的时候,将图片以动态获取的方式生成。

 

问题:如何处理列表项里面的具体控件的点击事件?

我们知道,真正的电话通讯录的列表,点击前面的头像,会直接打电话,如果点击后面的名字,会进入查看,或者编辑当前联系人的联系信息。而我们这里的列表内容,不管是点击图片还是后面的文字,它都只会当做是点击了整个列表项,并不能区分具体点击了哪一个,那我们应该怎么办呢?

SimpleAdapter类有一个方法,叫做getView,该方法默认实现就相当于点击了整个列表项,我们可以定义一个我们自己的SimpleAdapter,覆盖该方法,改成适合我们自己业务的实现即可,具体做法如下:

定义一个继承自SimpleAdapter类的子类:MySimpleAdapter,并覆盖getView方法:

class MySimpleAdapter extends SimpleAdapter{

   //必须要写的构造器

   public MySimpleAdapter(Context context,
     List<? extends Map<String, ?>> data, int resource,
     String[] from, int[] to) {
     super(context, data, resource, from, to);
   }

   //覆盖该方法,以自己的方式处理用户的点击动作   

   public View getView(int position, View convertView, ViewGroup parent) {
    View view = super.getView(position, convertView, parent);
    ImageView iv = (ImageView)view.findViewById(R.id.imageViewId);
    TextView tv1 = (TextView)view.findViewById(R.id.textViewId1);
    TextView tv2 = (TextView)view.findViewById(R.id.textViewId2);
    iv.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
      Toast.makeText(ListViewActivity5.this, "点击了图片,准备去打电话了", 1000);
     }
    });
    tv1.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
      Toast.makeText(ListViewActivity5.this, "点击了用户名,准备去编辑用户名了", 1000);
     }
    });
    tv2.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
      Toast.makeText(ListViewActivity5.this, "点击了图片,准备去编辑电话号码了", 1000);
     }
    });
    return view;
   }
 }

 

在主类的Activity中,就需要使用自己的SimpleAdapter来作为适配器了,这样连处理ListView的列表选项的事件监听都无需处理了,如下图效果:

[转载]ListView[转载]ListView

 

问题:如何动态地刷新ListView里面的数据呢?

这样的场景我们经常见到:删除我们联系人列表里面的某一个联系人,然后再回到列表页面的时候,刚刚删除的联系人就不会出现在列表项中,这个该如何处理?

这个其实很简单,只需要调用SimpleAdapter的notifyDataSetChanged()方法即可,也就是说需要在往创建SimpleAdapter时,在其构造器的第二个参数的List添加或者是删除一个map的时候需要调用该方法,核心代码如下:

String name = ((EditText)addView.findViewById(R.id.editTextId1)).getText().toString();
String phone = ((EditText)addView.findViewById(R.id.editTextId2)).getText().toString();
Map<String,Object> item = new HashMap<String,Object>();
item.put("name", name);
item.put("phone", phone);
listItems.add(item);
adapter.notifyDataSetChanged();
最终效果如下:

[转载]ListView[转载]ListView

未完待续...

0 0
原创粉丝点击