zoukankan      html  css  js  c++  java
  • Android开发系列(十六) QQ聊天界面完整版——整合气泡、表情、历史表情等功能

     QQ项目最新Demo:http://vdisk.weibo.com/s/uu2pYkVAKnYWh

     这是寒假最后一弹,首先我应该对持续关注我博客的朋友们表示歉意,因为我只是想完成这个项目,由于其中涉及了很多的知识点,我实在每一办法一一讲出,只能提一些主要的东西,其余的一些细节还希望大家参考我的代码。这些代码实际上也为我以后再次着手学习Android开发打好基础。本节Demo下载链接会尽快上传~

      正如之前所料,果然没有把QQ项目彻底完成。总体上看,总共做出了三个主要的界面,接下来的工作就是进行网络通信等相关的配置了,本节的主要内容是彻底完成QQ聊天界面,众所周知QQ聊天的表情界面有一个“历史表情”的功能,如下图所示:

    【QQ官方效果】

    要实现这一效果,就需要把ViewFlipper放进一个TabView中,PS:如果大家不想用TabView,可以考虑使用Fragment,但由于我对这一控件掌握的并不熟练,所以暂时仍用TabHost+TabView实现,我的效果图如下:

     

    这意味着需要创建两个Activity,分别显示系统原有的表情和“历史表情”,实际上这两个Activity是类似的,只不过表情的列数不一样而已,下面就是其中一个Activity的代码:

    package com.example.android_qqfix;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    
    import com.dragon.face.FaceHistoryData;
    
    
    
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.View.OnClickListener;
    import android.view.View.OnTouchListener;
    import android.view.animation.AnimationUtils;
    import android.widget.BaseAdapter;
    import android.widget.GridView;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ViewFlipper;
    
    public class MyFaceActivity extends Activity{
    
        int[] faceId={R.drawable.f_static_000,R.drawable.f_static_001,R.drawable.f_static_002,R.drawable.f_static_003
                ,R.drawable.f_static_004,R.drawable.f_static_005,R.drawable.f_static_006,R.drawable.f_static_009,R.drawable.f_static_010,R.drawable.f_static_011
                ,R.drawable.f_static_012,R.drawable.f_static_013,R.drawable.f_static_014,R.drawable.f_static_015,R.drawable.f_static_017,R.drawable.f_static_018};
        String[] faceName={"\呲牙","\淘气","\流汗","\偷笑","\再见","\敲打","\擦汗","\流泪","\掉泪","\小声","\炫酷","\发狂"
                 ,"\委屈","\便便","\菜刀","\微笑","\色色","\害羞"};
        
        protected ViewFlipper viewFlipper=null;
        protected LinearLayout pagePoint=null;
        ArrayList<ArrayList<HashMap<String,Object>>> listGrid=null;
        ArrayList<ImageView> pointList=null;
        public static MyFaceHandler faceHandler=null;
        
        public static final int ActivityId=1;
        
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.myface_layout);
            
            System.out.println("MyFace is onCreate");
            
            faceHandler=new MyFaceHandler(Looper.myLooper());
            
            viewFlipper=(ViewFlipper)findViewById(R.id.faceFlipper);
            pagePoint=(LinearLayout)findViewById(R.id.pagePoint);
            listGrid=new ArrayList<ArrayList<HashMap<String,Object>>>();
            pointList=new ArrayList<ImageView>();
            
            
            addFaceData();
            addGridView();
            setPointEffect(0);
        }
        
        public class MyFaceHandler extends Handler{
    
            public MyFaceHandler(Looper looper){
                super(looper);
            }
            
            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                switch(msg.what){
                
                case ChatActivity.ActivityID:
                    if(msg.obj.toString().equals("collapse")){   //关闭表情
                        
                        if(ChatActivity.currentTabTag.equals("face"))
                            viewFlipper.setDisplayedChild(0);
                        setPointEffect(0);
                        
                    }
                    
                    
                }
                
                
            }
            
        }
        
        private void addFaceData(){
            if(listGrid!=null)
                listGrid.clear();                //首先将先前的数据清空
            ArrayList<HashMap<String,Object>> list=null;
            for(int i=0; i<faceId.length; i++){
                if(i%14==0){
                    list=new ArrayList<HashMap<String,Object>>();
                    listGrid.add(list);
                }  
                HashMap<String,Object> map=new HashMap<String,Object>();
                map.put("image", faceId[i]);
                map.put("faceName", faceName[i]);
                
                /**
                 * 这里把表情对应的名字也添加进数据对象中,便于在点击时获得表情对应的名称
                 */
                listGrid.get(i/14).add(map);        
            }
            System.out.println("listGrid size is "+listGrid.size());
        }
        
        private void addGridView(){
            if(viewFlipper!=null){
                viewFlipper.removeAllViews();
                pagePoint.removeAllViews();
                pointList.clear();               //更新前首先清除原有的所有数据
            }
            for(int i=0; i< listGrid.size();i++){
                View view=LayoutInflater.from(this).inflate(R.layout.view_item, null);
                GridView gv=(GridView)view.findViewById(R.id.myGridView);
                gv.setNumColumns(5);
                gv.setSelector(new ColorDrawable(Color.TRANSPARENT));
                MyGridAdapter adapter=new MyGridAdapter(this, listGrid.get(i), R.layout.chat_grid_item, new String[]{"image"}, new int[]{R.id.gridImage});
                gv.setAdapter(adapter);
                gv.setOnTouchListener(new MyTouchListener(viewFlipper));
                viewFlipper.addView(view);
            //    ImageView image=new ImageView(this);
            //    ImageView image=(ImageView)LayoutInflater.from(this).inflate(R.layout.image_point_layout, null);
                /**
                 * 这里不喜欢用Java代码设置Image的边框大小等,所以单独配置了一个Imageview的布局文件
                 */
                View pointView=LayoutInflater.from(this).inflate(R.layout.point_image_layout, null);
                ImageView image=(ImageView)pointView.findViewById(R.id.pointImageView);
                image.setBackgroundResource(R.drawable.qian_point);
                pagePoint.addView(pointView);   
                /**
                 * 这里验证了LinearLayout属于ViewGroup类型,可以采用addView 动态添加view
                 */
                
                pointList.add(image);
                /**
                 * 将image放入pointList,便于修改点的颜色
                 */
            }
        
        }
        
        
        /**
         * 设置游标(小点)的显示效果
         * @param darkPointNum    
         */
        private void setPointEffect(int darkPointNum){
            for(int i=0; i<pointList.size(); i++){
                pointList.get(i).setBackgroundResource(R.drawable.qian_point);
            }
            if(pointList.size()>0)
                pointList.get(darkPointNum).setBackgroundResource(R.drawable.shen_point);
        }
        private boolean moveable=true;
        private float startX=0;
        
        /**
         * 用到的方法 viewFlipper.getDisplayedChild()  获得当前显示的ChildView的索引
         * @author Administrator
         *
         */
        class MyTouchListener implements OnTouchListener{
    
            ViewFlipper viewFlipper=null;
            
            
            public MyTouchListener(ViewFlipper viewFlipper) {
                super();
                this.viewFlipper = viewFlipper;
            }
    
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch(event.getAction()){
                case MotionEvent.ACTION_DOWN:startX=event.getX(); moveable=true; break;
                case MotionEvent.ACTION_MOVE:
                    if(moveable){
                        if(event.getX()-startX>40){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex>0){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(MyFaceActivity.this, R.anim.left_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(MyFaceActivity.this, R.anim.right_out));                        
                                viewFlipper.showPrevious();
                                setPointEffect(childIndex-1);
                            }
                        }
                        else if(event.getX()-startX<-40){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex<listGrid.size()-1){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(MyFaceActivity.this, R.anim.right_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(MyFaceActivity.this, R.anim.left_out));
                                viewFlipper.showNext();
                                setPointEffect(childIndex+1);
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:moveable=true;break;
                default:break;
                }
                
                return false;
            }
            
        }
        
        
        /**
         * GridViewAdapter
         * @param textView
         * @param text
         */
        
        class MyGridAdapter extends BaseAdapter{
    
            Context context;
            ArrayList<HashMap<String,Object>> list;
            int layout;
            String[] from;
            int[] to;
            
            
            public MyGridAdapter(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 position) {
                // TODO Auto-generated method stub
                return null;
            }  
    
            @Override
            public long getItemId(int position) {
                // TODO Auto-generated method stub
                return position;
            }
    
            class ViewHolder{
                ImageView image=null;
            }
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                ViewHolder holder=null;
                if(convertView==null){
                    convertView=LayoutInflater.from(context).inflate(layout, null);
                    holder=new ViewHolder();
                    holder.image=(ImageView)convertView.findViewById(to[0]);
                    convertView.setTag(holder);
                }
                else{
                    holder=(ViewHolder)convertView.getTag();
                }
                holder.image.setImageResource((Integer)list.get(position).get(from[0]));
                class MyGridImageClickListener implements OnClickListener{
    
                    int position;
                    
                    public MyGridImageClickListener(int position) {
                        super();
                        this.position = position;
                    }
    
    
                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        //editText.append((String)list.get(position).get("faceName"));
                        Message msg=new Message();
                        msg.what=MyFaceActivity.ActivityId;
                        msg.arg1=0;
                        msg.obj=(String)list.get(position).get("faceName");
                        ChatActivity.chatHandler.sendMessage(msg);
                        //一个消息不能被多个Handler使用!!!
                        
                        if(FaceHistoryData.faceHistoryList==null){
                            FaceHistoryData.faceHistoryList=new ArrayList<HashMap<String,Object>>();
                        }
                        
                        int index=0;
                        int curFaceId=(Integer)list.get(position).get("image");
                        for(index=0; index<FaceHistoryData.faceHistoryList.size(); index++){
                            if((Integer)FaceHistoryData.faceHistoryList.get(index).get("faceId")==curFaceId){
                                break;
                            }
                        }
                        if(index>=FaceHistoryData.faceHistoryList.size()){
                            HashMap<String,Object> map=new HashMap<String,Object>();
                            map.put("faceId", (Integer)list.get(position).get("image"));
                            map.put("faceName", (String)list.get(position).get("faceName"));
                            FaceHistoryData.faceHistoryList.add(0, map);     //前插入
                            int curSize=FaceHistoryData.faceHistoryList.size();  //控制长度
                            if(curSize>36){
                                FaceHistoryData.faceHistoryList.remove(curSize-1);
                            }
                        }
                            
                        
                    }
                    
                }
                //这里创建了一个方法内部类
                holder.image.setOnClickListener(new MyGridImageClickListener(position));
                        
                return convertView;
            }
            
        }
    
        
    
        @Override
        protected void onResume() {
            // TODO Auto-generated method stub
            addFaceData();
            addGridView();
            setPointEffect(0);
            super.onResume();
        }
    
        
        
        
    
    }


    对应的布局文件

    <?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" >
        
            <ViewFlipper 
                android:id="@+id/faceFlipper"
                android:layout_width="match_parent"
                android:layout_height="130dip"
                android:background="#d0d3d5"
                >
            </ViewFlipper>
        
            <LinearLayout 
                android:id="@+id/pagePoint"
                android:layout_width="match_parent"
                android:layout_height="20dip"
                android:layout_below="@id/faceFlipper"
                android:background="#d0d3d5"
                android:gravity="center"
                android:orientation="horizontal">
                
            </LinearLayout>
    
    </RelativeLayout>


    另外一个是历史表情 FaceHistoryActivity,其实和MyFaceActivity很像

    package com.example.android_qqfix;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.View.OnClickListener;
    import android.view.View.OnTouchListener;
    import android.view.animation.AnimationUtils;
    import android.widget.BaseAdapter;
    import android.widget.GridView;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ViewFlipper;
    
    import com.dragon.face.FaceHistoryData;
    import com.example.android_qqfix.MyFaceActivity.MyFaceHandler;
    import com.example.android_qqfix.MyFaceActivity.MyGridAdapter;
    import com.example.android_qqfix.MyFaceActivity.MyTouchListener;
    import com.example.android_qqfix.MyFaceActivity.MyGridAdapter.ViewHolder;
    
    public class FaceHistoryActivity extends Activity{
    
        int[] faceId;
        String[] faceName;
        
        protected ViewFlipper viewFlipper=null;
        protected LinearLayout pagePoint=null;
        ArrayList<ArrayList<HashMap<String,Object>>> listGrid=null;
        ArrayList<ImageView> pointList=null;
        
        public static final int ActivityId=2;
        public static Handler faceHistoryHandler=null;  
    
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.myface_layout);
            
            
            faceHistoryHandler=new FaceHistoryHandler(Looper.myLooper());
        
            viewFlipper=(ViewFlipper)findViewById(R.id.faceFlipper);
            pagePoint=(LinearLayout)findViewById(R.id.pagePoint);
            listGrid=new ArrayList<ArrayList<HashMap<String,Object>>>();
            pointList=new ArrayList<ImageView>();
            
            viewFlipper.setOnTouchListener(new MyTouchListener(viewFlipper));
            
            parseFaceHistoryList();     //首先创建faceId faceName
            addFaceData();
            addGridView();
            setPointEffect(0);
        }  
        
        
        public class FaceHistoryHandler extends Handler{
    
            public FaceHistoryHandler(Looper looper){
                super(looper);
            }
            
            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                switch(msg.what){
                
                case ChatActivity.ActivityID:
                    if(msg.obj.toString().equals("collapse")){   //关闭表情
                        
                        if(ChatActivity.currentTabTag.equals("faceHistory"))
                            viewFlipper.setDisplayedChild(0);
                        setPointEffect(0);
                        
                    }
                    
                    
                }
                
                
            }
            
        }
    
        
        
        private void parseFaceHistoryList(){
            if(FaceHistoryData.faceHistoryList==null){
                faceId=new int[0];
                faceName=new String[0];
                
                System.out.println("没装进来!!!");
            }
            else{
                faceId=new int[FaceHistoryData.faceHistoryList.size()];
                faceName=new String[FaceHistoryData.faceHistoryList.size()];
                for(int i=0; i<FaceHistoryData.faceHistoryList.size(); i++){
                    faceId[i]=(Integer)FaceHistoryData.faceHistoryList.get(i).get("faceId");
                    faceName[i]=(String)FaceHistoryData.faceHistoryList.get(i).get("faceName");
                }
            }
        }
        
        private void addFaceData(){
            ArrayList<HashMap<String,Object>> list=null;
            if(listGrid!=null)
                listGrid.clear();                //首先将先前的数据清空
            for(int i=0; i<faceId.length; i++){
                if(i%11==0){
                    list=new ArrayList<HashMap<String,Object>>();
                    listGrid.add(list);
                }  
                HashMap<String,Object> map=new HashMap<String,Object>();
                map.put("image", faceId[i]);
                map.put("faceName", faceName[i]);
                
                /**
                 * 这里把表情对应的名字也添加进数据对象中,便于在点击时获得表情对应的名称
                 */
                listGrid.get(i/11).add(map);        
            }
            System.out.println("listGrid size is "+listGrid.size());
        }
        
        private void addGridView(){
            if(viewFlipper!=null){
                viewFlipper.removeAllViews();
                pagePoint.removeAllViews();
                pointList.clear();               //更新前首先清除原有的所有数据
            }
            
            for(int i=0; i< listGrid.size();i++){
                View view=LayoutInflater.from(this).inflate(R.layout.view_item, null);
                GridView gv=(GridView)view.findViewById(R.id.myGridView);
                gv.setNumColumns(4);
                gv.setSelector(new ColorDrawable(Color.TRANSPARENT));
                MyGridAdapter adapter=new MyGridAdapter(this, listGrid.get(i), R.layout.chat_grid_item, new String[]{"image"}, new int[]{R.id.gridImage});
                gv.setAdapter(adapter);
                gv.setOnTouchListener(new MyTouchListener(viewFlipper));
                viewFlipper.addView(view);
            //    ImageView image=new ImageView(this);
            //    ImageView image=(ImageView)LayoutInflater.from(this).inflate(R.layout.image_point_layout, null);
                /**
                 * 这里不喜欢用Java代码设置Image的边框大小等,所以单独配置了一个ImageView的布局文件
                 */
                View pointView=LayoutInflater.from(this).inflate(R.layout.point_image_layout, null);
                ImageView image=(ImageView)pointView.findViewById(R.id.pointImageView);
                image.setBackgroundResource(R.drawable.qian_point);
                pagePoint.addView(pointView);   
                /**
                 * 这里验证了LinearLayout属于ViewGroup类型,可以采用addView 动态添加view
                 */
                
                pointList.add(image);
                /**
                 * 将image放入pointList,便于修改点的颜色
                 */
            }
        
        }
        
        
        /**
         * 设置游标(小点)的显示效果
         * @param darkPointNum
         */
        private void setPointEffect(int darkPointNum){
            for(int i=0; i<pointList.size(); i++){
                pointList.get(i).setBackgroundResource(R.drawable.qian_point);
            }
            if(pointList.size()>0)
                pointList.get(darkPointNum).setBackgroundResource(R.drawable.shen_point);
        }
        private boolean moveable=true;
        private float startX=0;
        
        /**
         * 用到的方法 viewFlipper.getDisplayedChild()  获得当前显示的ChildView的索引
         * @author Administrator
         *
         */
        class MyTouchListener implements OnTouchListener{
    
            ViewFlipper viewFlipper=null;
            
            
            public MyTouchListener(ViewFlipper viewFlipper) {
                super();
                this.viewFlipper = viewFlipper;
            }
    
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch(event.getAction()){
                case MotionEvent.ACTION_DOWN:startX=event.getX(); moveable=true; break;
                case MotionEvent.ACTION_MOVE:
                    if(moveable){
                        if(event.getX()-startX>40){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex>0){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(FaceHistoryActivity.this, R.anim.left_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(FaceHistoryActivity.this, R.anim.right_out));                        
                                viewFlipper.showPrevious();
                                setPointEffect(childIndex-1);
                            }
                        }
                        else if(event.getX()-startX<-40){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex<listGrid.size()-1){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(FaceHistoryActivity.this, R.anim.right_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(FaceHistoryActivity.this, R.anim.left_out));
                                viewFlipper.showNext();
                                setPointEffect(childIndex+1);
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:moveable=true;break;
                default:break;
                }
                
                return false;
            }
            
        }
        
        
        /**
         * GridViewAdapter
         * @param textView
         * @param text
         */
        
        class MyGridAdapter extends BaseAdapter{
    
            Context context;
            ArrayList<HashMap<String,Object>> list;
            int layout;
            String[] from;
            int[] to;
            
            
            public MyGridAdapter(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 position) {
                // TODO Auto-generated method stub
                return null;
            }
    
            @Override
            public long getItemId(int position) {
                // TODO Auto-generated method stub
                return position;
            }
    
            class ViewHolder{
                ImageView image=null;
            }
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                ViewHolder holder=null;
                if(convertView==null){
                    convertView=LayoutInflater.from(context).inflate(layout, null);
                    holder=new ViewHolder();
                    holder.image=(ImageView)convertView.findViewById(to[0]);
                    convertView.setTag(holder);
                }
                else{
                    holder=(ViewHolder)convertView.getTag();
                }
                holder.image.setImageResource((Integer)list.get(position).get(from[0]));
                class MyGridImageClickListener implements OnClickListener{
    
                    int position;
                    
                    public MyGridImageClickListener(int position) {
                        super();
                        this.position = position;
                    }
    
    
                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        //editText.append((String)list.get(position).get("faceName"));
                        Message msg=new Message();
                        msg.what=MyFaceActivity.ActivityId;
                        msg.arg1=0;
                        msg.obj=(String)list.get(position).get("faceName");
                        ChatActivity.chatHandler.sendMessage(msg);
                        
                    }
                    
                }
                //这里创建了一个方法内部类
                holder.image.setOnClickListener(new MyGridImageClickListener(position));
                
                
                
                return convertView;
            }
            
        }
    
    
    
    
        @Override
        protected void onPause() {
            // TODO Auto-generated method stub
            
            super.onPause();
        }
    
        @Override
        protected void onResume() {
            // TODO Auto-generated method stub
            parseFaceHistoryList();    //首先得更新数组!!
            addFaceData();
            addGridView();
            setPointEffect(0);
            super.onResume();
        }
    
    
    
    
    
        @Override
        protected void onStart() {
            // TODO Auto-generated method stub
        
            super.onStart();
        }
        
        
        
    
    }

      因为在点击表情界面的某个表情时会修改ChatActivity中EditText中的内容,所以需要为ChatActivity设置Handler,该Handler对象绑定ChatActivity的Looper,这里把Handler设置为静态公共 public static对象,以使在其他Activity中能够访问到这一个handler,具体代码实现方式可以参考 ChatActivity ,MyFaceActivity中的 Handler的相关操作。

    ChatActivity:

    package com.example.android_qqfix;
    
    import android.app.Activity;
    import android.app.TabActivity;
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.text.Spannable;
    import android.text.SpannableString;
    import android.text.SpannableStringBuilder;
    import android.text.style.ImageSpan;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.View.OnTouchListener;
    import android.view.ViewGroup.LayoutParams;
    import android.view.animation.AnimationUtils;
    import android.view.inputmethod.InputMethodManager;
    import android.view.ViewGroup;
    import android.view.Window;
    import android.widget.*;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.TabHost.OnTabChangeListener;
    import android.widget.TabHost.TabSpec;
    
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    
    
    
    
    
    
    public class ChatActivity extends TabActivity{
    
        int[] faceId={R.drawable.f_static_000,R.drawable.f_static_001,R.drawable.f_static_002,R.drawable.f_static_003
                ,R.drawable.f_static_004,R.drawable.f_static_005,R.drawable.f_static_006,R.drawable.f_static_009,R.drawable.f_static_010,R.drawable.f_static_011
                ,R.drawable.f_static_012,R.drawable.f_static_013,R.drawable.f_static_014,R.drawable.f_static_015,R.drawable.f_static_017,R.drawable.f_static_018};
        String[] faceName={"\呲牙","\淘气","\流汗","\偷笑","\再见","\敲打","\擦汗","\流泪","\掉泪","\小声","\炫酷","\发狂"
                 ,"\委屈","\便便","\菜刀","\微笑","\色色","\害羞"};
        
        HashMap<String,Integer> faceMap=null;
        ArrayList<HashMap<String,Object>> chatList=null;
        String[] from={"image","text"};
        int[] to={R.id.chatlist_image_me,R.id.chatlist_text_me,R.id.chatlist_image_other,R.id.chatlist_text_other};
        int[] layout={R.layout.chat_listitem_me,R.layout.chat_listitem_other};
        String userQQ=null;
        /**
         * 这里两个布局文件使用了同一个id,测试一下是否管用
         * TT事实证明这回产生id的匹配异常!所以还是要分开。。
         * 
         * userQQ用于接收Intent传递的qq号,进而用来调用数据库中的相关的联系人信息,这里先不讲
         * 先暂时使用一个头像
         */
        
        public final static int OTHER=1;
        public final static int ME=0;
        public final static int ActivityID=0;
        
        
        protected ListView chatListView=null;
        protected Button chatSendButton=null;
        protected EditText editText=null;
    
        protected ImageButton chatBottomLook=null;
        protected RelativeLayout faceLayout=null;
        protected TabHost tabHost=null;
        protected TabWidget tabWidget=null;
    
       
        private boolean expanded=false;
    
        protected MyChatAdapter adapter=null;
        protected View tabFaceHistory=null,tabFace=null;
        protected ImageView tabFaceHistoryImage=null,tabFaceImage=null;
        public static Handler chatHandler=null;
        public static String currentTabTag="face";
        public TabSpec tabSpecFaceHistory,tabSpecFace;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_chat);
        
            chatHandler=new MyChatHandler(Looper.myLooper());
            
            faceMap=new HashMap<String,Integer>();    
            chatList=new ArrayList<HashMap<String,Object>>();
            
            
            
            addTextToList("不管你是谁", ME);
            addTextToList("群发的我不回
      ^_^", OTHER);
            addTextToList("哈哈哈哈", ME);
            addTextToList("新年快乐!", OTHER);
            
            chatSendButton=(Button)findViewById(R.id.chat_bottom_sendbutton);
            editText=(EditText)findViewById(R.id.chat_bottom_edittext);
            chatListView=(ListView)findViewById(R.id.chat_list);
            tabWidget=(TabWidget)findViewById(android.R.id.tabs);
            tabHost=(TabHost)findViewById(android.R.id.tabhost);
    
            
            chatBottomLook=(ImageButton)findViewById(R.id.chat_bottom_look);
            faceLayout=(RelativeLayout)findViewById(R.id.faceLayout);
    
     
            
            /**
             * 添加选项卡
             */
            tabSpecFaceHistory=tabHost.newTabSpec("faceHistory");
            tabFaceHistory=LayoutInflater.from(this).inflate(R.layout.tabwidget_image_disselected,null);
            tabFaceHistoryImage=(ImageView)tabFaceHistory.findViewById(R.id.tabImage_disselected);
            tabFaceHistoryImage.setImageResource(R.drawable.face_history_disselected);
            tabSpecFaceHistory.setIndicator(tabFaceHistory);
            Intent intent1=new Intent();
            intent1.setClass(ChatActivity.this, FaceHistoryActivity.class);
            tabSpecFaceHistory.setContent(intent1);
            tabHost.addTab(tabSpecFaceHistory);
            
            tabSpecFace=tabHost.newTabSpec("face");
            tabFace=LayoutInflater.from(this).inflate(R.layout.tabwidget_image_selected, null);
            tabFaceImage=(ImageView)tabFace.findViewById(R.id.tabImage_selected);
            tabFaceImage.setImageResource(R.drawable.face_look_selected);
            tabSpecFace.setIndicator(tabFace);
            Intent intent2=new Intent();
            intent2.setClass(ChatActivity.this, MyFaceActivity.class);
            tabSpecFace.setContent(intent2);
            tabHost.addTab(tabSpecFace);
            
            tabHost.setCurrentTabByTag("face");     
            tabHost.setOnTabChangedListener(new OnTabChangeListener() {
                
                @Override
                public void onTabChanged(String tabId) {
                    // TODO Auto-generated method stub
                //    System.out.println("current Selected Tab "+tabId);
                    currentTabTag=tabId;
        
                    
                    
                    if(tabId.equals("face")){
                        
                        
                                         
                        tabFace.setBackgroundResource(R.drawable.tabwidget_selected);
                        tabFaceImage.setImageResource(R.drawable.face_look_selected);
                        tabSpecFace.setIndicator(tabFace);
                        
                        
                        tabFaceHistory.setBackgroundResource(R.drawable.tab_widget_disselected);
                        tabFaceHistoryImage.setImageResource(R.drawable.face_history_disselected);
                        tabSpecFaceHistory.setIndicator(tabFaceHistory);
                    }
                    else if(tabId.equals("faceHistory")){
                        
                        tabFace.setBackgroundResource(R.drawable.tabwidget_disselected);
                        tabFaceImage.setImageResource(R.drawable.face_look_disselected);
                        tabSpecFace.setIndicator(tabFace);
                        
                        tabFaceHistory.setBackgroundResource(R.drawable.tabwidget_selected);
                        tabFaceHistoryImage.setImageResource(R.drawable.face_history_selected);
                        tabSpecFaceHistory.setIndicator(tabFaceHistory);
                        
                    }
                    
                    
                }
            });
            
            
            chatBottomLook.setOnClickListener(new OnClickListener(){
      
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    if(expanded){
                        setFaceLayoutExpandState(false);
                        expanded=false;
                        
                        
                        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);  
                        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);  
    
                        /**height不设为0是因为,希望可以使再次打开时viewFlipper已经初始化为第一页 避免
                        *再次打开ViewFlipper时画面在动的结果,
                        *为了避免因为1dip的高度产生一个白缝,所以这里在ViewFlipper所在的RelativeLayout
                        *最上面添加了一个1dip高的黑色色块
                        */
                        
                        
                    }
                    else{
    
                        setFaceLayoutExpandState(true);  
                        expanded=true;
                        
    
                    }
                }
                
            });
            
            /**EditText从未获得焦点到首次获得焦点时不会调用OnClickListener方法,所以应该改成OnTouchListener
             * 从而保证点EditText第一下就能够把表情界面关闭
            editText.setOnClickListener(new OnClickListener(){
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    ViewGroup.LayoutParams params=viewFlipper.getLayoutParams();
                    params.height=0;
                    viewFlipper.setLayoutParams(params);
                    expanded=false;
                    System.out.println("WHYWHWYWHYW is Clicked");
                }
                
            });
            **/
            editText.setOnTouchListener(new OnTouchListener() {
                
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    // TODO Auto-generated method stub
                    if(expanded){
                        
                        setFaceLayoutExpandState(false);
                        expanded=false;
                    }
                    return false;
                }
            });
            adapter=new MyChatAdapter(this,chatList,layout,from,to);            
            chatSendButton.setOnClickListener(new OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    String myWord=null;
                    
                    /**
                     * 这是一个发送消息的监听器,注意如果文本框中没有内容,那么getText()的返回值可能为
                     * null,这时调用toString()会有异常!所以这里必须在后面加上一个""隐式转换成String实例
                     * ,并且不能发送空消息。
                     */
                    
                    myWord=(editText.getText()+"").toString();
                    if(myWord.length()==0)
                        return;
                    editText.setText("");
                    addTextToList(myWord, ME);
                    /**
                     * 更新数据列表,并且通过setSelection方法使ListView始终滚动在最底端
                     */
                    adapter.notifyDataSetChanged();
                    chatListView.setSelection(chatList.size()-1);
                    
                } 
            });
            
            chatListView.setAdapter(adapter);
            
            chatListView.setOnItemClickListener(new OnItemClickListener() {
    
                @Override
                public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                        long arg3) {
                    // TODO Auto-generated method stub
                    setFaceLayoutExpandState(false);
                    ((InputMethodManager)ChatActivity.this.getSystemService(INPUT_METHOD_SERVICE)).
                    hideSoftInputFromWindow(ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                    expanded=false;
                }
            });
            
            /**
             * 为表情Map添加数据
             */
            for(int i=0; i<faceId.length; i++){
                faceMap.put(faceName[i], faceId[i]);
            }
            
            
            
            
            
        }
        
        
        
        public class MyChatHandler extends Handler{
            
            public MyChatHandler(Looper looper){
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                switch(msg.what){
                case MyFaceActivity.ActivityId:
                    if(msg.arg1==0){            //添加表情字符串
                        editText.append(msg.obj.toString());
                    }
                
                }
            }
            
            
            
        }
        
        
        /**
         * 打开或者关闭软键盘,之前若打开,调用该方法后关闭;之前若关闭,调用该方法后打开
         */
        
        private void setSoftInputState(){
            ((InputMethodManager)ChatActivity.this.getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
        }
        
        private void setFaceLayoutExpandState(boolean isexpand){
            if(isexpand==false){
    
                    
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=1;
                faceLayout.setLayoutParams(params);    
                
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_look);
                Message msg=new Message();
                msg.what=this.ActivityID;
                msg.obj="collapse";
                if(MyFaceActivity.faceHandler!=null)
                    MyFaceActivity.faceHandler.sendMessage(msg);
                
                Message msg2=new Message();
                msg2.what=this.ActivityID;
                msg2.obj="collapse";
                if(FaceHistoryActivity.faceHistoryHandler!=null)
                    FaceHistoryActivity.faceHistoryHandler.sendMessage(msg2);
        
                chatListView.setSelection(chatList.size()-1);//使会话列表自动滑动到最低端
                
            }
            else{
                
                ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow
                (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=185;
            //    faceLayout.setLayoutParams(new RelativeLayout.LayoutParams( ));    
                RelativeLayout.LayoutParams relativeParams=new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);
                relativeParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
                faceLayout.setLayoutParams(relativeParams);
                
                
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_keyboard);
    
            }
        }
        
        
        
        
        
        
        
        
        private void setFaceText(TextView textView,String text){
            SpannableString spanStr=parseString(text);
            textView.setText(spanStr);
        }
        
        private void setFace(SpannableStringBuilder spb, String faceName){
            Integer faceId=faceMap.get(faceName);
            if(faceId!=null){
                Bitmap bitmap=BitmapFactory.decodeResource(getResources(), faceId);
                bitmap=Bitmap.createScaledBitmap(bitmap, 30, 30, true);
                ImageSpan imageSpan=new ImageSpan(this,bitmap);
                SpannableString spanStr=new SpannableString(faceName);
                spanStr.setSpan(imageSpan, 0, faceName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                spb.append(spanStr);    
            }
            else{
                spb.append(faceName);
            }
            
        }
        
        private SpannableString parseString(String inputStr){
            SpannableStringBuilder spb=new SpannableStringBuilder();
            Pattern mPattern= Pattern.compile("\\..");
            Matcher mMatcher=mPattern.matcher(inputStr);
            String tempStr=inputStr;
            
            while(mMatcher.find()){
                int start=mMatcher.start();
                int end=mMatcher.end();
                spb.append(tempStr.substring(0,start));
                String faceName=mMatcher.group();
                setFace(spb, faceName);
                tempStr=tempStr.substring(end, tempStr.length());
                /**
                 * 更新查找的字符串
                 */
                mMatcher.reset(tempStr);
            }
            spb.append(tempStr);
            return new SpannableString(spb);
        }
        
        
        
        protected void addTextToList(String text, int who){
            HashMap<String,Object> map=new HashMap<String,Object>();
            map.put("person",who );
            map.put("image", who==ME?R.drawable.contact_0:R.drawable.contact_1);
            map.put("text", text);
            chatList.add(map);
        }
        
        private class MyChatAdapter extends BaseAdapter{
    
            Context context=null;
            ArrayList<HashMap<String,Object>> chatList=null;
            int[] layout;
            String[] from;
            int[] to;
              
            
            
            public MyChatAdapter(Context context,
                    ArrayList<HashMap<String, Object>> chatList, int[] layout,
                    String[] from, int[] to) {
                super();
                this.context = context;
                this.chatList = chatList;
                this.layout = layout;
                this.from = from;
                this.to = to;
            }
    
            @Override
            public int getCount() {
                // TODO Auto-generated method stub
                return chatList.size();
            }
    
            @Override
            public Object getItem(int arg0) {
                // TODO Auto-generated method stub
                return null;
            }
    
            @Override
            public long getItemId(int position) {
                // TODO Auto-generated method stub
                return position;
            }
    
            class ViewHolder{
                public ImageView imageView=null;
                public TextView textView=null;
            
            }
            
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                // TODO Auto-generated method stub
                ViewHolder holder=null;   
                int who=(Integer)chatList.get(position).get("person");
     
                    convertView= LayoutInflater.from(context).inflate(
                            layout[who==ME?0:1], null);
                    holder=new ViewHolder();
                    holder.imageView=(ImageView)convertView.findViewById(to[who*2+0]);
                    holder.textView=(TextView)convertView.findViewById(to[who*2+1]);
                
                
                holder.imageView.setBackgroundResource((Integer)chatList.get(position).get(from[0]));
                setFaceText(holder.textView, chatList.get(position).get(from[1]).toString());
                return convertView;
            }
            
        }
        
        
    
    }

      此外点击Tab选项卡时所显示出的一些动画效果是我 调用了tabHost的onTabChangedListener()方法,根据选中的不同Tab,调用 TabSpec的setIndicator设置不同的布局文件,关于背景效果的设置采用的是layer-list  这一知识点我在前面讲联系人列表的界面时已经提到,这个东西确实非常非常强大!,从最终效果可以看出,用TabView也可以做出比较漂亮的界面,也没有必要因为它被谷歌弃用了就完全抛弃它,实际上在实现一些简单的tab导航页时使用tabView更加简洁。

      另外要提的一点是:上面说的历史表情是怎样实现的呢?原理很简单:创建一个ArrayList类型或者队列类型的静态对象,用户使用某个表情后,如果这个表情之前没有使用过,就把它添加进静态对象中,然后在FaceHistoryActivity中调用parseFaceHistoryList进行解析成相应的表情列表,可以参考FaceHistoryActivity中的相关代码。

    为了从这个静态对象我单独创建了一个类,实际上只有一行代码:

    public class FaceHistoryData {
        
        public static ArrayList<HashMap<String,Object>>  faceHistoryList=null;
        
    
    }


     

      最后一点是很遗憾,我并没有实现动态表情的效果,我发现这个效果所用到的技术并不是我短时间内能掌握的,还望海涵。如果我以后找到了实现动态表情的好方法,还会更新本节的博客~~

         好啦,以上就是本节的全部内容,《Android开发系列》也会暂时告一段落了。新学期开始,我要去迎接新的挑战,重启这一系列应该是这个暑假的事情了,这一段时间在Android应用的控件和UI美化上花了一些功夫,也确实感到比半年前初学安卓时有了更进一步的理解。下次我应该会把QQ项目彻底完成了,如果时间允许可能会涉及Android游戏的开发...不多说了,希望新的大家都会取得新的成绩。

  • 相关阅读:
    两种四元数插值的区别
    欧拉角与万向锁
    Unity3D 中脚本丢失问题
    Unity3D 中的定时器
    Unity3D Layout 快捷键
    Sublime Text 2 代码片断
    使用 Box2D 做一个 JansenWalker 机器人
    使用 Sublime Text 2 开发 Unity3D 项目
    Ant 常用语法及选项
    OC基础数据类型-NSNumber
  • 原文地址:https://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_16.html
Copyright © 2011-2022 走看看