QQ联系人,ExpandableListView

前两天电脑坏掉了,导致这一两天没有更新。发现之前写的几篇博客浏览量已经接近1000,有种受宠若惊的感觉,写这个系列的博客主要是假期想干一些平时没有时间做的兴趣爱好,并把自己的一点一滴记录下来。这一段时间由于各种原因以致于更新速度很慢,之前的打算是先把QQ2013的Activity的布局搭建出来,然后再设计其中的核心代码,如网络编程,搭建一个小型服务器,进而实现QQ基本的功能,从现在的进度来看这个计划可能完不成了。不过我仍然会继续做这个项目,相信就算不会彻底完成也能够搭出漂亮的QQ框架来。

  进入正题。上一节结束时提到QQ联系人列表界面还有一个效果没有实现,这个效果是当联系人一级条目滑动到屏幕的顶端时,该一级条目就会悬停在屏幕的顶端,当往下拉时又会随着ExpandableListView的Item正常移动,如官方效果图图所示:

 QQ联系人,ExpandableListView

  注意观察“我的好友”那一栏,本节实现的即上述效果。

首先讲一下思路:

  1、不能使用onScroll()方法,这一方法会在ExpandableListView展开的时候调用,至于其调用的周期并不是太明确,但可以确定的是onScroll并不能直接实现上述效果。              

  2、这里应该获得ScrollView的实例(findViewById方法),然后调用setOnTouch()方法,在setOnTouch()方法中进一步进行设置。

  3、这里的基本思路是:当拖动屏幕时,setOnTouch方法被调用,在该方法中开辟一个子线程,这个子线程用于检测屏幕中的ScrollView对象是否仍在运动,直到不再拖动屏幕或者屏幕上的控件在惯性的作用下运动至停止时,该线程才会自动终结。在该线程中判断一级条目(这里用groupItem表示)是否向上滚出了屏幕。

  4、在该Activity的主布局文件中ScrollView的后面添加一个LinearLayout控件,该控件的布局和GroupItem的布局文件的内容是一样的,将其位置设置为至于屏幕(父控件)的顶端,平时设置其Visibility为View.GONE 不可见不可操作状态,当某一个已经展开(expanded)的groupItem滚出了屏幕时,就将该隐藏的控件设置为可见,并将其内容设置为和刚刚滚出屏幕的groupItem的内容一摸一样。若所有的groupItem都为滑出屏幕,那么将该控件重新设置为不可见(GONE);

  5、本节中遇见的最大的障碍就是我没有找到获得每一个groupItem在屏幕上的实时位置的方法,所以这里对原来的groupData对象做了修改,在HashMap对象中新添加了一个键值对 key="location"  value= 该groupItem在ExpandableListView中的相对纵坐标。在后在上面创建的坐标检测子线程中获得groupData中存放的groupItem的相对坐标再加上ExpandableListView的绝对纵坐标,然后判断groupItem是否滑出了屏幕,进而对“隐藏控件”执行相应的操作。注意这里应该先判断groupPosition靠后的groupItem的坐标,悬停的控件显示的是最后滑出屏幕的groupItem的信息。

  6、本节将Activity的标题去掉了,去掉的方法是在onCreate()方法中 setContentView之前(必须是之前)调用requestWindowFeature(Window.FEATURE_NO_TITLE);

  7、本节中需要在子线程中修改MainActivity 安卓UI,而直接修改是不允许的,所以需要使用一个自定义的Handler对象,调用父类的构造方法

      public MyHandler(Looper looper){

                         super(looper);

      }

          并复写handleMessage()方法修改MainActivity的UI。

          关于Android消息处理机制我目前不想再多讲,做开发的话够用就行,没必要掌握的太彻底,如果想对Handler有一个比较好的了解,可以参考:

          http://blog.csdn.net/zkdemon/article/details/6914161

   8、ExpandableListView可以调用collapseGroup(int groupPosition)或者ExpandGroup(int groupPosition)强制的关闭或者展开一个Group。这对于实现悬浮控件的点击事件具有重要作用!

  鉴于代码量越来越多了,这里先把修改的地方列出来:

主布局文件:

复制代码
<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" android:fadingEdge="none"> <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> <LinearLayout android:layout_width="match_parent" android:layout_height="40dip" android:id="@+id/titleGroupView" android:orientation="horizontal" android:visibility="gone" android:background="@drawable/list_item_border"> <ImageView android:id="@+id/title_groupImage" android:layout_width="match_parent" android:layout_height="16dip" android:layout_weight="1.8" android:layout_gravity="center" android:background="@drawable/todown" /> <TextView android:id="@+id/title_groupName" android:layout_width="match_parent" android:layout_height="42dip" android:layout_weight="1" android:paddingLeft="15dip" android:paddingTop="11dip" android:textSize="7pt" android:text="fdg" /> <TextView android:id="@+id/title_childCount" android:layout_width="match_parent" android:layout_height="42dip" android:layout_weight="1" android:gravity="right" android:paddingRight="10dip" android:paddingTop="11dip" android:text="ewsf"/> </LinearLayout> </RelativeLayout>
复制代码


展开或者关闭group时都会对每一个groupItem的纵坐标进行更新。

复制代码
@Override public void onGroupExpanded(int groupPosition) { // TODO Auto-generated method stub super.onGroupExpanded(groupPosition); /** * 更新groupItem的相对坐标 */ groupData.get(groupPosition).put("expanded", true); for(int i=groupPosition+1; i<groupData.size(); i++){
                groupData.get(i).put("location",(Integer)groupData.get(i).get("location")+childData.get(groupPosition).size()*CHILD_HEIGHT );
            } float deviationFix=0; //对ExpandableListView高度的误差修正 for(int i=0;i<groupData.size() ;i++){
                deviationFix +=(Integer)groupData.get(i).get("location")/CHILD_HEIGHT*0.5;
            }
            
            height+=childData.get(groupPosition).size()*CHILD_HEIGHT;
            ViewGroup.LayoutParams  params= exListView.getLayoutParams();
            params.height=height-floatToInt(deviationFix);
            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();
            params.height=height;
            exListView.setLayoutParams(params); /** * 更新groupItem的相对坐标 */ groupData.get(groupPosition).put("expanded", false); for(int i=groupPosition+1; i<groupData.size(); i++){
                groupData.get(i).put("location",(Integer)groupData.get(i).get("location")-childData.get(groupPosition).size()*CHILD_HEIGHT );
            }
        }
复制代码


对ScrollView设置监听器: 

复制代码
qqScroll.setOnTouchListener(new OnTouchListener() {
            
            @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub if(event.getAction()==MotionEvent.ACTION_MOVE && isChecking==false){ new LocationCheckThread().start();
                } return false;
            }
        });
复制代码

坐标检测线程:

复制代码
/** * 实现这个Activity的最后一个效果,即滑动到某一个Group那么改GroupItem就在屏幕顶端悬停的效果
     * 方法:需要使用onTouchEvent方法,这个比用onScrollView方法靠谱 */ private boolean isChecking=false; private class LocationCheckThread extends Thread{

        @Override public void run() { // TODO Auto-generated method stub super.run();
            isChecking=true; int[] location=new int[2]; int beforeY=0; int i;
            ExpandableHandler mHandler=new ExpandableHandler(Looper.getMainLooper()); while(true){ //exListView.getLocationOnScreen(location);  exListView.getLocationOnScreen(location); if(beforeY==location[1]){ //控件停止运动,线程关闭 isChecking=false; return;
                } else{
                    beforeY=location[1]; for(i=groupData.size()-1; i>=0; i--){ if((Boolean)groupData.get(i).get("expanded")&&(Integer)groupData.get(i).get("location")+location[1]<=24){
                            

                            Message msg=new Message();
                            msg.arg1=childData.get(i).size();
                            msg.arg2=i;
                            msg.obj=groupData.get(i).get("groupName");
                            msg.what=VISIBILITY_VISIBLE;
                            mHandler.sendMessage(msg); break;
                        }
                    } if(i<0){
                        Message msg=new Message();
                        msg.what=VISIBILITY_GONE;
                        msg.obj=""; //必须加这一步,否则会有空指针异常  mHandler.sendMessage(msg);
                    }
                    
                } try { this.sleep(80); //sleep的时间不能过短!! } catch (InterruptedException e) { // TODO Auto-generated catch block  e.printStackTrace();
                }
                
            }
            
        }
    
    }
复制代码


在线程中判断GroupItem是否有滑动出屏幕的,创建Message对象,绑定响应的groupItem的信息(position,Name,childCount,是否显示Visibility),然后由Handler对象发出[sendMessage();] 注意sleep的时间不能过短,因为当手指不再脱离屏幕而ListView 自由滑动时,由于滑动的速度较慢,导致两次检测坐标是结果一样,从而意外的终止线程。

isChecking是boolean型数据,标记线程工作的状态,避免在滑动的过程中同时开辟多个子线程产生异常。

 

然后是MyHandler类:

复制代码
public class ExpandableHandler extends Handler{ public ExpandableHandler(Looper looper){ super(looper);
        }
        @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); int listNum=msg.arg1; int visibility=msg.what; int groupPos=msg.arg2;
            String groupName=msg.obj.toString(); if(visibility==VISIBILITY_GONE){
                titleGroupView.setVisibility(View.GONE); return;
            } else{

                titleGroupView.setVisibility(View.VISIBLE);
                titleGroupName.setText(groupName);
                titleChildCount.setText(""+listNum);
                
                titleGroupView.setTag(groupPos); //给这个View控件设置一个标签属性,用于存放groupPosition /** * setText()中的参数不能使int型!! */ }
            
        }
        
    }
复制代码

  创建MyHandler对象,在其中复写handleMessage();方法,在该方法中设置悬浮控件的显示效果(更新系统UI)

最后还需要实现一个效果,当悬浮控件出现时,点击悬浮控件,会把该控件对应的groupItem关掉(collapseGroup),因此需要设置一个点击监听器:

复制代码
titleGroupView.setOnClickListener(new OnClickListener(){

            @Override public void onClick(View v) { // TODO Auto-generated method stub int groupPosition=(Integer)titleGroupView.getTag();

                exListView.collapseGroup(groupPosition); new LocationCheckThread().start();
                
                
            }
            
        });
复制代码


注意这里又一次启动了坐标检测线程,是因为需要在调用collapseGroup之后对系统的UI做一次刷新,避免产生显示异常。

 

以上就是主要需要修改的地方,然后附上整个Activity的代码:

代码太多折叠起来了:

  View Code


最后附上效果图:

QQ联系人,ExpandableListViewQQ联系人,ExpandableListViewQQ联系人,ExpandableListView

 头像重复并不是代码错误,我只是重复添加了几个联系人,详情展开代码参考。

你可能感兴趣的:(QQ联系人,ExpandableListView)