zoukankan      html  css  js  c++  java
  • Android开发系列(六) ListView使用教程二——QQ联系人列表

    上一篇讲了ListView的基本使用方式,是通过SimpleAdapter适配器来设置,但是这种方法无法实现在每一行添加按钮,添加图片,动态添加或者删除某一行,或者使滚动条自动滑动到最底端的要求,因此需要自定义一个适配器类继承BaseAdapter进而实现更加丰富的方法。

      另外由于这已经不是我第一次做这个总结了,为了增加一点挑战,这次就做一个 QQ好友列表的ListView

      

      首先还是xml布局文件,在其中添加ListView控件:

    主布局layout_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"
        android:background="#00aaff"
        tools:context=".MainActivity" >
    
        <TextView
            android:id="@+id/myText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="联系人"
            android:textSize="7pt"
            android:layout_centerHorizontal="true"
            android:textColor="#ffffff"
            android:textStyle="bold" />
        <ListView 
            android:id="@+id/qq_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/myText"/>
    
       
    </RelativeLayout>

    然后是每一行ListItem的布局,采用LinerLayout布局,一些注意的点都在里面:

    <?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="wrap_content"
        android:background="#efefef" >
        
        <!-- LinerLayout有比较奇怪的性质:当布局中的控件可以超出布局规定的大小 ,所以这里一行的行宽改成由内部的几个控件
        控制,而LinerLayout的layout_height改成wrap_content ..  -->
        
        
        <ImageButton 
            android:id="@+id/ct_photo"
            android:layout_height="70dip"
            android:layout_width="70dip"
            android:layout_margin="5dip"
            android:background="@drawable/contact_0"/>
        <TextView 
            android:id="@+id/ct_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dip"
            android:layout_toRightOf="@id/ct_photo"
            android:layout_alignTop="@id/ct_photo"
            android:text="为你我受冷风吹"
            android:textSize="8pt"
            android:textStyle="bold"
            android:maxLength="7"/>
        
        <TextView 
            android:id="@+id/ct_sign"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dip"
            android:layout_toRightOf="@id/ct_photo"
            android:layout_alignBottom="@id/ct_photo"
            android:text="为什么受伤的总是我"
            android:textColor="#888888"/>
    
        <!-- 注意不是layout_padding -->
        
        
        
        
    
    </RelativeLayout>

    因为这里使用的是自己定义的MyAdapter类,可以更灵活的实现列表的一些功能,比如和数据库相联系,动态更新数据、添加按钮控件等等,在本例中模仿QQ列表为头像设置成了ImageButton,后面的附图中的一个Toast信息就是点击图像做出的相应,当然点击一行也可以做出相应,这个后续可能会对QQ程序做一些扩展,如增加网络模块,聊天窗口等等。到时候再进一步讨论。

    下面是MyAdapter类,这个类最好和MainActivity类放在同一个包里。

    package com.example.android_qqlist;
    
    import java.util.*;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.*;
    
    public class MyAdapter extends BaseAdapter{
        private Context context=null;
        private int resources;
        private ArrayList<HashMap<String,Object>> list=null;
        private String[] from;
        private int[] to;
        
        
        /**
         * 这里仿照的是SimpleAdapter的形参列表
         * @param context
         * @param Resources
         * @param list
         * @param from
         * @param to
         */
        
        public MyAdapter(Context context, int resources,
                ArrayList<HashMap<String, Object>> list, String[] from, int[] to) {
            super();
            this.context = context;
            this.resources = resources;
            this.list = list;
            this.from = from;
            this.to = to;
        }
    
        /**
         * 剩下的问题就是依次实现BaseAdapter的这几个类方法就可以了
         */
        
        @Override
        public int getCount() {        //这个方法返回的是ListView的行数
            // TODO Auto-generated method stub
            return list.size();
        }
    
        
    
        @Override
        public Object getItem(int arg0) {      //这个方法没必要使用,可以用getItemId代替
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public long getItemId(int itemId) {     //点击某一行时会调用该方法,其形参由安卓系统提供
            // TODO Auto-generated method stub
            return itemId;
        }
    
        /**
         * getView方法为系统在绘制每一行时调用,在此方法中要设置需要显示的文字,图片,
         * 以及为按钮设置监听器。
         * 
         * 形参意义:
         * position:当前绘制的item 的位置(ID);
         * convertView,系统在绘制ListView时,如果是绘制第一个Item(即第一行),convertView为null,当
         * 绘制第二个及以后的Item的convertView不为空,这时可以直接利用这个convertView的getTag()方法,获得各控件
         * 的实例,并进行相应的设置,这样可以加快绘图速度。
         * 
         * 为了为convertView设置附加信息Tag,这里创建一个内部类ViewHolder,用于盛放一行中所有控件的引用,将这些引用
         * 实例化后作为convertView的附加信息。
         */
        class ViewHolder{
            public ImageButton ctPhoto=null;
            public TextView ctName=null,ctSign=null;
            
            /*
             * 从这里可以看出,from和to数组彼此之间的元素应该一一对应,同时from和to各自元素内部的顺序不同,最后ListView
             * 呈现的位置也会不同!
             */
            public ViewHolder(View convertView){
                ctPhoto=(ImageButton)convertView.findViewById(to[0]);    
                /*注意View和Activity都属于容器类,都需要设置布局文件,内部都含有子控件,且都有findViewById()
                 * 他们之间没有明显的继承关系
                 */
                ctName=(TextView)convertView.findViewById(to[1]);
                ctSign=(TextView)convertView.findViewById(to[2]);
                
            }
            
        }
        
        class ImageListener implements OnClickListener{
    
            private int position;
            
            public ImageListener(int position){
                this.position=position;
            }                          //构造函数没有返回值
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String str=list.get(position).get(from[1]).toString();
                Toast.makeText(context,str+" is Clicked" , Toast.LENGTH_LONG).show();
                
            }
            
        }
        
    
        @Override
        public View getView(int position, View convertView, ViewGroup arg2) {
            // TODO Auto-generated method stub
            
            /**
             * 首先判断是不是第一次创建Item,若是,则创建convertView实例和ViewHolder对象,并通过fandViewById()方法
             * 获得每一行中所有空间的实例放在ViewHolder对象中,然后对convertView设置标签
             */
            ViewHolder viewHolder=null;
            
            //注意convertView不是随意创建的,需要有LayoutInflater,根据list_item布局文件创建
            if(convertView==null){
                LayoutInflater inflater=LayoutInflater.from(context);
                convertView=inflater.inflate(resources,null);    //这里的null是一个ViewGroup形参,基本用不上
                viewHolder=new ViewHolder(convertView);
                convertView.setTag(viewHolder);
                
            }
            else{
                viewHolder=(ViewHolder)convertView.getTag();    //通过getTag()方法获得附加信息
            }
            /**
             * 这里对viewHolder中的各个控件进行相应的设置
             */
            /**
             * @author DragonGN
             * 这里出现了一个问题:在绘制当前行的ListItem时,只需要对当前行的控件进行设置,因此这里不能加一个for
             * 循环对每一个list中的每一个元素进行遍历,而应该根据当前创建的ListItem行的position,然后
             * 访问数据库list中相应位置的Map的数据,进行控件的设置!
             */
    
            /**
             * 注意这里必须是setBackgroundDrawable() 而不是setBackground(),后者会报错,尽管前者过期了但一样可用
             */
                viewHolder.ctPhoto.setBackgroundDrawable((Drawable)(list.get(position).get(from[0]))); 
                //Map中要添加一个Drawable对象,这里的from和to中的元素应该一一对应,其顺序也应该对应ViewHolder构造方法中控件的调用的顺序
                
                viewHolder.ctName.setText((String)(list.get(position).get(from[1])));
                viewHolder.ctSign.setText((String)(list.get(position).get(from[2])));    
                viewHolder.ctPhoto.setOnClickListener(new ImageListener(position));
            
            
            return convertView;     //把这个每一行的View对象返回
        }
    
    }


      最后就是MainActivity类了,与因为MyAdapter的封装方式与SimpleAdpter是一样额,因此这里MainActivity的操作基本不变。

    package com.example.android_qqlist;
    
    import java.util.*;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.view.Menu;
    import android.widget.ListView;
    
    public class MainActivity extends Activity {
    
        //每一列的列名/Map的键名  和其对应的View子控件的ID
        String[] from={"userPhoto","userName","userSign"};           //这里的内容对应后面HashMap中的键
        int[] to={R.id.ct_photo,R.id.ct_name,R.id.ct_sign};
        
        //整个ListView所显示的全部信息和资源数组
        int[] photoRes={R.drawable.contact_0,R.drawable.contact_1,R.drawable.contact_2,R.drawable.contact_3};
        String[] strName={"暗夜之殇","街角的幸福","静悄悄","愤怒的小胖"};
        String[] strSign={"Where is my love...","有些事终于想开了","总有一天会寻找到自己的幸福","谁再叫我小胖我跟谁急..."};
        
        //数据链表和Map容器
        ArrayList<HashMap<String,Object>> list=null;
        HashMap<String,Object> map=null;
        
        ListView listView=null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            listView=(ListView)findViewById(R.id.qq_list);
            list=new ArrayList<HashMap<String,Object>>();
            
            for(int i=0; i<4; i++){
                map=new HashMap<String,Object>();    //map调用put方法添加键值对
                map.put("userPhoto",getResources().getDrawable(photoRes[i]));
                map.put("userName", strName[i]);
                map.put("userSign",strSign[i]);
                list.add(map);        
            }
            
            //创建自定义的MyAdapter对象
            MyAdapter adapter=new MyAdapter(this,R.layout.list_item,list,from,to);
            
            //调用ListView的setAdapter()方法设置适配器
            listView.setAdapter(adapter);
            
            
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
    }

    最后附上结果图[感觉比几个月前初学时有点更美观了一些]

    这几个头像是我自己下载的图片,其对应的资源地址在 MainActivity中用一个 photoRes数组表示的~

    关于事件响应的一些问题等进一步扩展项目时再续。

  • 相关阅读:
    springmvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?
    数据库中的锁机制
    在inux中安装redis的时候,会出现下面的这个异常
    使用SecureCRT操作linux系统时候的简单设置
    装饰者设计模式
    java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone.
    事务
    2.6.1 测试架构师
    测试专家讲述通往测试架构师之路
    什么是软件测试架构师
  • 原文地址:https://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_6.html
Copyright © 2011-2022 走看看