• Android开发系列(十二) QQ联系人列表升级版——ListView和ScrollView高阶使用方法


      今天继续进行QQ界面的开发工作。前一段时间讲过ExpandableListView的使用并且设置了一个比较简单的具有子菜单效果的联系人列表,本节添加进ScrollView控件,对QQ2013版的联系人界面进行仿真。当然本质上是做一些美化工作,但是对于掌握Android界面的开发是很重要的,一个好看的界面对于手机应用至关重要。

      首先上图:

    这是官方联系人效果图:

    这是我自己做的效果图:

       

      官方版的最外面是一个TabView控件,本节先不考虑,可见和原版的效果还是比较相似的。(图标是我通过画图软件截过来的图,鉴于这只是交流学习,算不上侵权~)

      谈一下本节所涉及的一些知识点。

    一、layer-list的使用方法

      layer-list的作用是叠放多个图层,这从名字上就可以看出,因为本节想做出一个一边没有边框,其它边界上没有边框的背景效果,使用<shape > 中的<stroke/>标签就没有办法满足了,解决的办法就是在drawable 文件夹中创建一个layer-list文件,然后创建两个图层<item>  第一个item放的是边框的颜色,第二个图层放的是内部的颜色,并且通过设置第二个item的根标签属性,设置第二个item比第一个item窄的程度,这样第一个item的颜色就会在边框处显示出来,从而做出边框的效果。

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
        <item >
            <shape >
                <solid android:color="#cccccc"/>
            </shape>   <!--上面是第一个图层-->
        </item>
                           
        <item android:left="1dip" android:top="1dip" android:right="1dip" android:bottom="0dip">
    <!-- 上面一行是设置哪个边框的关键步骤,即第二个图层比第一个图层闪出来的边框! -->
    <shape > <solid android:color="#f0f0f0"/> </shape> </item> </layer-list>

      做这个有什么用呢?系列(十一)中两个Edittext重合的部分颜色很深,影响美观,因此可以通过创建layer-list文件,使得第一个EditText的下边框宽度为0,从而避免颜色加深的情况:

         这里仅给出修改后的效果图:

    二、如何理解ExpandableListView的生命周期?

      对于ExpandableListView,当刚刚创建该对象并且设置好适配器之后,EListView的每一个group都是没有展开的,当点击某一个group时,系统会把所有的group 和展开的childItem 重新绘制一遍,其中每一个的group 的最后一个childItem放的isLastChild 为true.

         当有group展开或者关闭时会调用onGroupExpanded()和onGroupCollapsed() 方法。

    三、如何动态获得ExpandableListView的高度?

         创建一个一个int 变量 height,  在ExpandableListView刚刚创建时,设置其高度为所有group的宽度之和,然后当打开或者关闭group时,在onGroupExpanded()和onGroupCollapsed() 方法中对height 的值进行增加或者减少,并且通过LayoutParams对象即使更新空间的高度,从而实现动态更新ExpandableListView的高度。

    具体代码如下:

      1、获得ListItem的高度,这里比如获得group的高度,需要调用适配器的getGroupView的方法

          View listItem=  listView.getExpandableAdapter().getGroupView(参数);

          listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));

       //这一个必须有,因为listItem 在通过inflate()方法获得时并未指定父控件,因此导致onMeasure()方法中无法对宽度和高度进行解析,所以这里将布局类型强制设定成wrapContent 类型,然后再调用measure()方法

          listItem.measure(0,0);     

    //0,0是两个参数,用于比较,measure()方法,用于计算控件的尺寸,关于该方法的具体细节请参考:http://blog.csdn.net/lilybaobei/article/details/8021868

          int HEIGHT=listItem.getMeasuredHeight();     

          2、设置ExpandableListView的高度,通过LayoutParams对象

      ViewGroup.LayoutParams params=listView.getLayoutParams();

    //获得尺寸参数

          params.height=HEIGHT*3;

          listView.setLayoutParams(params);

    以上两点问题的提出主要是因把ListView、ExpandableListView放在一个 ScrollView中时往往会出现显示不全的问题,因此解决的办法就是动态更新ListView/ExListView的高度,以使列表能够显示完整。

    四、ListView的边界比较模糊,颜色变淡,解决这个问题的方法是在ListView的属性设置中添加上  android:fadingEdge="none",这样边界就不会模糊了

    基本上就是以上问题,下面上代码:

    这是MainActivity的布局文件:

    <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:background="#f1f1f1"
        tools:context=".MainActivity"
        android:scrollbars="vertical"
        android:scrollbarAlwaysDrawVerticalTrack="true" >
    
        <ScrollView 
            android:id="@+id/qqlist_scroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <LinearLayout 
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                
                <ListView 
                    android:id="@+id/qqlist_classify"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:divider="#000000"
                    android:dividerHeight="0px"
                    android:fadingEdge="none"/>
                <TextView 
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@drawable/qqlist_classify_text"
                    android:text="好友分组"
                    android:textSize="6pt"
                    android:textColor="#666666"
                    android:padding="4dip"/>
                
                <ExpandableListView
                     android:id="@+id/qq_list"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:divider="#888888"
                    android:dividerHeight="0px"
                    android:fadingEdge="none"/>
                
                <TextView 
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@drawable/qqlist_classify_text"
                    android:text="生活服务"
                    android:textSize="6pt"
                    android:textColor="#666666"
                    android:padding="4dip"/>
                
                <ListView 
                    android:id="@+id/qqlist_classify_down"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:divider="#000000"
                    android:dividerHeight="0px"/>
                
                
            </LinearLayout>
            
            
        </ScrollView>
     
       
    </RelativeLayout>


    然后就是第二个MainActivity.java  ,就是在系列(九)的基础上做了一定修改,添加了一个ListView的适配器和ExpandableListView的适配器:

    因为控件数量比较多,所以代码比较多:

    package com.dragon.android_qq;
    
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.BaseAdapter;
    import android.widget.BaseExpandableListAdapter;
    import android.widget.ExpandableListAdapter;
    import android.widget.ExpandableListView;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import java.util.*;
    
    import com.dragon.persondata.ContactsInfo;
    
    
      
    
    public class ContactsListExpandable extends Activity {
    
        
        private int height=0;
        private  int GROUP_HEIGHT=0;
        private  int CHILD_HEIGHT=52;
        int[] photoRes={R.drawable.contact_0,R.drawable.contact_1,R.drawable.contact_2,R.drawable.contact_3};
         
        String[] groupFrom={"groupImage","groupName","childCount"};
        int[] groupTo={R.id.groupImage,R.id.groupName,R.id.childCount};
        String[] childFrom={"userImage","userName","userSign"};
        int[] childTo={R.id.ct_photo,R.id.ct_name,R.id.ct_sign};
        ArrayList<HashMap<String,Object>> groupData=null;
        ArrayList<ArrayList<HashMap<String,Object>>> childData=null; 
        int[] groupIndicator={R.drawable.toright,R.drawable.todown};
        ExpandableListView exListView=null;
        ListView listViewTop=null,listViewDown=null;
        
        
        
        
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.layout_qqlist_expandable);
            groupData=new ArrayList<HashMap<String,Object>>();
            childData=new ArrayList<ArrayList<HashMap<String,Object>>> ();
            
            ContactsInfo user1=new ContactsInfo("暗夜之殇","总有一天会寻找到自己的幸福",R.drawable.contact_0,"我的好友");
            ContactsInfo user2=new ContactsInfo("街角的幸福","有些事终于想开了",R.drawable.contact_1,"我的好友");
            ContactsInfo user3=new ContactsInfo("愤怒的小胖","谁再叫我小胖我跟谁急!",R.drawable.contact_3,"朋友");
            ContactsInfo user4=new ContactsInfo("放羊的星星","What ever",R.drawable.contact_2,"陌生人");
            ContactsInfo user5=new ContactsInfo("都市丽人","我要我的完美",R.drawable.contact_4,"我的好友");
            
            addUser(user1);
            addUser(user2);
            addUser(user3);
            addUser(user4);
            addUser(user5);
            
            
              
            //不能用HashMap的实参赋给Map形参,只能new一个HashMap对象赋给Map的引用!
            
            
            exListView=(ExpandableListView)findViewById(R.id.qq_list);
            MyExpandableListViewAdapter adapter=new MyExpandableListViewAdapter(this,groupData,R.layout.layout_qqlist_group,groupFrom,groupTo,childData,R.layout.layout_qqlist_child,childFrom,childTo );
            exListView.setAdapter(adapter);
            exListView.setGroupIndicator(null);
            
            
            View exGroupListItem= exListView.getExpandableListAdapter().getGroupView(0,false, null, exListView );
            exGroupListItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            exGroupListItem.measure(0, 0);
            GROUP_HEIGHT=exGroupListItem.getMeasuredHeight();
            
            View exChildListItem=exListView.getExpandableListAdapter().getChildView(0, 0, false, null, exListView);
            exChildListItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
            exChildListItem.measure(0, 0);
            CHILD_HEIGHT=exChildListItem.getMeasuredHeight();
            
            ViewGroup.LayoutParams  params= exListView.getLayoutParams();
            height=groupData.size()*GROUP_HEIGHT-2;
            params.height=height;
            exListView.setLayoutParams(params);
            
            
            listViewTop=(ListView)findViewById(R.id.qqlist_classify);
            ArrayList<HashMap<String,Object>> listTop=new ArrayList<HashMap<String,Object>>();
            HashMap<String,Object> mapTop1=new HashMap<String,Object>();
            HashMap<String,Object> mapTop2=new HashMap<String,Object>();
            HashMap<String,Object> mapTop3=new HashMap<String,Object>();
            mapTop1.put("listTopImage",R.drawable.address_list);
            mapTop1.put("listTopText", "通讯录");
            mapTop2.put("listTopImage",R.drawable.chat_flock);
            mapTop2.put("listTopText", "群");
            mapTop3.put("listTopImage",R.drawable.forum);
            mapTop3.put("listTopText", "讨论组");
            listTop.add(mapTop1);
            listTop.add(mapTop2);
            listTop.add(mapTop3);
            MyListViewAdapter adapterTop=new MyListViewAdapter(this, listTop,
                    R.layout.qqlist_classify_item, new String[]{"listTopImage","listTopText"}, new int[]{R.id.qqlist_classify_image,R.id.qqlist_classify_name});
            
            listViewTop.setAdapter(adapterTop);
            ViewGroup.LayoutParams paramsTop=listViewTop.getLayoutParams();
            View listItem=listViewTop.getAdapter().getView(0, null, listViewTop);
            listItem.measure(0, 0);
            paramsTop.height=3*listItem.getMeasuredHeight()-2;
            
            //ListView属于一种ViewGroup类型
            listViewTop.setLayoutParams(paramsTop);
            
            /**
             * 创建一个String数组对象不能直接写String[]{}  必须在前面加上new,即写成 new String[]{"listTopImage","listTopText"}
             * 
             */
            
            listViewDown=(ListView)findViewById(R.id.qqlist_classify_down);
            ArrayList<HashMap<String,Object>> listDown=new ArrayList<HashMap<String,Object>>();
            HashMap<String,Object> mapDown1=new HashMap<String,Object>();
            mapDown1.put("listTopImage",R.drawable.life_service);
            mapDown1.put("listTopText", "生活服务");
            listDown.add(mapDown1);
            listTop.add(mapTop3);
            MyListViewAdapter adapterDown=new MyListViewAdapter(this, listDown,
                    R.layout.qqlist_classify_item, new String[]{"listTopImage","listTopText"}, new int[]{R.id.qqlist_classify_image,R.id.qqlist_classify_name});
            
            listViewDown.setAdapter(adapterDown);
            
        }
    
        protected void addUser(ContactsInfo user)
        {
            int i;
            for(i=0; i< groupData.size(); i++){
                if(groupData.get(i).get("groupName").toString().equals(user.groupInfo)){
                    break;
                }
            }
            if(i>=groupData.size()){
                HashMap<String,Object> map=new HashMap<String,Object>();
                map.put("groupImage", groupIndicator);
                map.put("groupName",user.groupInfo );
                map.put("childCount", 0);
                groupData.add(map);
                
                ArrayList<HashMap<String,Object>> list=new ArrayList<HashMap<String,Object>>();
                childData.add(list);
            }
            
            HashMap<String,Object> userData=new HashMap<String,Object>();
            userData.put("userImage",user.userImage );
            userData.put("userName", user.userName);
            userData.put("userSign", user.userSign);
            childData.get(i).add(userData);
            Integer count=(Integer)groupData.get(i).get("childCount")+1;
            groupData.get(i).put("childCount", count);    
            
        }
        
        
        
        
        
        
        
        
        /**
         * ExpandableListView对应的适配器
         * @author DragonGN
         *
         */
        public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
            
            private Context context=null;
            private ArrayList<HashMap<String,Object>> groupData=null;
            int groupLayout=0;
            private String[] groupFrom=null;
            private int[] groupTo=null;
            private ArrayList<ArrayList<HashMap<String,Object>>> childData=null;
            int childLayout=0;
            private String[] childFrom=null;
            private int[] childTo=null;
            
            
            
    
            public MyExpandableListViewAdapter(Context context, ArrayList<HashMap<String, Object>> groupData,
                    int groupLayout, String[] groupFrom, int[] groupTo,
                    ArrayList<ArrayList<HashMap<String, Object>>> childData, int childLayout,
                    String[] childFrom, int[] childTo) {
                super();
                this.context = context;
                this.groupData = groupData;
                this.groupLayout = groupLayout;
                this.groupFrom = groupFrom;
                this.groupTo = groupTo;
                this.childData = childData;
                this.childLayout = childLayout;
                this.childFrom = childFrom;
                this.childTo = childTo;
                
            }
    
            @Override
            public Object getChild(int arg0, int arg1) {
                // TODO Auto-generated method stub
                return null;
            }
    
            /**
             * position与id一样,都是从0开始计数的,
             * 这里返回的id也是从0开始计数的
             */
            @Override
            public long getChildId(int groupPosition, int childPosition) {
                // TODO Auto-generated method stub
                long id=0;
                for(int i=0;i<groupPosition; i++){
                    id+=childData.size();
                }
                id+=childPosition;  
                return id;           
            }
            
            /**ChildViewHolder内部类**/
            class ChildViewHolder{
                ImageButton userImage=null;
                TextView userName=null;
                TextView userSign=null;
            }
            
            /**头像点击事件监听类**/
            class ImageClickListener implements OnClickListener{
    
                ChildViewHolder holder=null;
                public ImageClickListener(ChildViewHolder holder){
                    this.holder=holder;
                }
                
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Toast.makeText(context, holder.userName.getText()+" is clicked", Toast.LENGTH_SHORT).show();
                    
                }
                
            }
    
            
            
            @Override
            public View getChildView(int groupPosition, int childPosition,
                    boolean isLastChild, View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                /**
                 * 这里isLastChild目前没用到,如果出现异常再说
                 */
                ChildViewHolder holder=null;
                if(convertView==null){
                    convertView= LayoutInflater.from(context).inflate(childLayout,null);
                                                 //感觉这里需要把root设置成ViewGroup 对象
                    /**
                     * ERROR!!这里不能把null换成parent,否则会出现异常退出,原因不太确定,可能是inflate方法获得的这个item的View
                     * 并不属于某个控件组,所以使用默认值null即可
                     */
                    holder=new ChildViewHolder();
                    holder.userImage=(ImageButton)convertView.findViewById(childTo[0]);
                    holder.userName=(TextView)convertView.findViewById(childTo[1]);
                    holder.userSign=(TextView)convertView.findViewById(childTo[2]);
                    convertView.setTag(holder);
                }
                else{
                    holder=(ChildViewHolder)convertView.getTag();
                }
                
                holder.userImage.setBackgroundResource((Integer)(childData.get(groupPosition).get(childPosition).get(childFrom[0])));
                holder.userName.setText(childData.get(groupPosition).get(childPosition).get(childFrom[1]).toString());
                holder.userSign.setText(childData.get(groupPosition).get(childPosition).get(childFrom[2]).toString());
                holder.userImage.setOnClickListener(new ImageClickListener(holder));
    
                
                return convertView;
            }
    
            
            @Override
            public int getChildrenCount(int groupPosition) {
                // TODO Auto-generated method stub
                return childData.get(groupPosition).size();
            }
    
            @Override
            public Object getGroup(int groupPosition) {
                // TODO Auto-generated method stub
                return null;
            }
    
            @Override
            public int getGroupCount() {
                // TODO Auto-generated method stub
                return groupData.size();
            }
    
            @Override
            public long getGroupId(int groupPosition) {
                // TODO Auto-generated method stub
                return groupPosition;
            }
    
            
            class GroupViewHolder{
                ImageView image=null;
                TextView groupName=null;
                TextView childCount=null;
            }
            
            @Override
            public View getGroupView(int groupPosition, boolean isExpanded,
                    View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                GroupViewHolder holder=null;
                if(convertView==null){
                    convertView=LayoutInflater.from(context).inflate(groupLayout, null);
                    holder=new GroupViewHolder();
                    holder.image=(ImageView)convertView.findViewById(groupTo[0]);
                    holder.groupName=(TextView)convertView.findViewById(groupTo[1]);
                    holder.childCount=(TextView)convertView.findViewById(groupTo[2]);
                    convertView.setTag(holder);
                }
                else{
                    holder=(GroupViewHolder)convertView.getTag();
                } 
                
                int[] groupIndicator=(int[])groupData.get(groupPosition).get(groupFrom[0]);
                holder.image.setBackgroundResource(groupIndicator[isExpanded?1:0]);
                holder.groupName.setText(groupData.get(groupPosition).get(groupFrom[1]).toString());
                holder.childCount.setText(groupData.get(groupPosition).get(groupFrom[2]).toString());
                
                
                if(groupPosition==groupData.size()-1){
                    convertView.setBackgroundResource(R.drawable.list_lastitem_border);
                }
                else{
                    convertView.setBackgroundResource(R.drawable.list_item_border);
                }
                //else的情况也要考虑,否则在绘制时出现错位现象
                
                return convertView;
                /**
                 * 不要在适配器中调用适配器的内部方法,不然会出现奇怪的异常
                 * 
                 */
            }
    
            @Override
            public boolean hasStableIds() {
                // TODO Auto-generated method stub
                return false;
            }
    
            @Override
            public boolean isChildSelectable(int groupPosition, int childPosition) {
                // TODO Auto-generated method stub
                return true;
            }
    
            /**
             * 在设置ExpandableListView的宽度的时候,要注意每次点击展开或者关闭时,各个Group和所要显示的Item都会重绘
             * 因此在每次绘制完毕之后都需要对height进行更新
             */
            
            @Override
            public void onGroupExpanded(int groupPosition) {
                // TODO Auto-generated method stub
                super.onGroupExpanded(groupPosition);
                System.out.println("已经展开了");
                height+=childData.get(groupPosition).size()*CHILD_HEIGHT;
                ViewGroup.LayoutParams  params= exListView.getLayoutParams();
                params.height=height-2;
                System.out.println("expand current height is "+height);
                exListView.setLayoutParams(params);
                
            }
    
            @Override
            public void onGroupCollapsed(int groupPosition) {
                // TODO Auto-generated method stub
                super.onGroupCollapsed(groupPosition);
                height=height-childData.get(groupPosition).size()*CHILD_HEIGHT;
                ViewGroup.LayoutParams  params= exListView.getLayoutParams();
                System.out.println("collapse current height is "+height);
                params.height=height-2;
                exListView.setLayoutParams(params);
            }
            
            
            private int floatToInt(double f){
                if(f>0){
                    return (int)(f+0.5);
                }
                else{
                    return (int)(f-0.5);
                }
                
            }
    
        }
        
        /**
         * 这个是ListView的适配器
         * @author DragonGN
         *
         */
        public class MyListViewAdapter extends BaseAdapter{
    
            Context context=null;
            ArrayList<HashMap<String,Object>> list=null;
            int layout;
            String[] from=null;
            int[] to;
            
            
            
            public MyListViewAdapter(Context context,
                    ArrayList<HashMap<String, Object>> list, int layout,
                    String[] from, int[] to) {
                super();
                this.context = context;
                this.list = list;
                this.layout = layout;
                this.from = from;
                this.to = to;
            }
    
            @Override
            public int getCount() {
                // TODO Auto-generated method stub
                return list.size();
            }
    
            @Override
            public Object getItem(int arg0) {
                // TODO Auto-generated method stub
                return null;
            }
    
            @Override
            public long getItemId(int arg0) {
                // TODO Auto-generated method stub
                return arg0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                View listItemView= LayoutInflater.from(context).inflate(layout, null);
                ImageView image=(ImageView)listItemView.findViewById(to[0]);
                TextView text=(TextView)listItemView.findViewById(to[1]);
                image.setImageResource((Integer)list.get(position).get(from[0]));
                text.setText((String)list.get(position).get(from[1]));
                return listItemView;
            }
            
        }
        
    
    }

    drawable文件中的图片和背景资源就不传了,若有需要,可以上传整个Demo。

    另外还有一个效果没有实现,休息两天继续更新。

           

                  

  • 相关阅读:
    Linux学习之十九-Linux磁盘管理
    Linux学习之十八-sudo分权管理
    Linux学习之十七-配置Linux简单的脚本文件自启动
    Linux系统救援模式应用:恢复误删的系统文件
    Linux学习之十六-Linux用户管理
    Linux学习之十五-Linux文件特殊权限和附加权限
    Linux学习之十四-Linux文件和目录权限
    Linux学习之十三-vi和vim编辑器及其快捷键
    Linux学习之十二-Linux文件属性
    Linux系统救援模式应用:单用户模式找回密码
  • 原文地址:https://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_12.html
Copyright © 2020-2023  润新知