• Android开发系列(十五) QQ聊天界面升级版——QQ聊天表情与SpannableString


      好几天没有更新博客了,马上就要开学,心情悲怆。

         首先说一下 QQ开源项目的Demo已经更新至本节,欢迎下载~ 地址: http://ishare.iask.sina.com.cn/f/67274366.html

      本节继续介绍QQ界面的开发,上一节做了QQ聊天界面的1.0版,前几日上传的Demo发现有很多下载(导致我的新浪爱问积分瞬间爆满^^,过几日我会取消下载积分,供大家自由下载),使用过的朋友会发现上一节并没有实现QQ聊天表情的效果,本节的主要内容就是实现这一功能,首先呈上效果图:

     

      虽然只是增加一个表情功能,但使用到的知识点相当多:

    一、表情列表的实现

      需要用到的控件:GridView和ViewFlipper用GridView存放表情,因为可能有多个页面的表情,所以要把GridView放进ViewFlipper中

      首先讲一下GridView:GridView类似于ListView,也需要自己定义适配器,只不过ListView的每一个条目是一行一行排放的,而GridView则是以格子的形式排放的,因此完全可以按照ListView的方式设置这里的GridView,GridView有两个特殊的方法:

         setNumColumns()  设置GridView的行数;

         setSelector(new ColorDrawable(Color.TRANSPARENT)); 设置选中GridView的某一个格子时,改格子的颜色为透明色,默认是橘黄色

         GridView的每一个格子我称其为GridItem,其对应的布局为:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" >
        
        <ImageView 
            android:id="@+id/gridImage"
            android:layout_width="30dip"
            android:layout_height="30dip"
            android:layout_margin="10dip"/>
       
    </RelativeLayout>

      GridView自定义的适配器如下(和ListView其实很像)

    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"));
                    }
                    
                }
                //这里创建了一个方法内部类
                holder.image.setOnClickListener(new MyGridImageClickListener(position));
                
                
                
                return convertView;
            }
            
        }

    然后在Java代码中通过findViewById()方法获得GridView对象,然后设置适配器即可,关于如何配置数据后面会讲。

      ViewFlipper:ViewFlipper用来实现翻页效果,即如果有多个页数的表情,那么把每一个GridView作为ViewFlipper的一个ChildView添加进去,形成多个页面,最后通过设置onTouchListener实现翻页效果,这和我之前做过的一个ImageSwitcher的思路是完全相同的,当时的那个项目完全可以用ViewFlipper实现

      ViewFlipper 对象常用的一些方法:

          addView()  把ChildView添加进去;

          setDisplayedChild(int index)  人为设置要显示的childView

          getDisplayedChild();   获得当前显示的childView的索引

          showNext();  显示下一个childView;

          showPrevious();   显示前一个 childView;

    注意在ViewFlipper中添加GridView后 ViewFlipper的onTouchListener就失效了,因此这里为GridView设置的监听器,根据滑动情况设置翻页效果。

    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>60){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex>0){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_out));                        
                                viewFlipper.showPrevious();
                                setPointEffect(childIndex-1);
                            }
                        }
                        else if(event.getX()-startX<-60){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex<listGrid.size()-1){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_out));
                                viewFlipper.showNext();
                                setPointEffect(childIndex+1);
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:moveable=true;break;
                default:break;
                }
                
                return false;
            }
            
        }


    下面讲一下如何配置GridView的数据源和把GridView添加进ViewFlipper,这里我设计了两个方法,根据 表情图片资源数组 faceId[]  和 表情名称数组 faceName[] 自动生成

    GridView的数据源ArrayList<ArrayList<HashMap<String,Object>>>  gridList对象,并把多个(根据表情的个数决定)GridView设置onTouchListener 然后添加进 ViewFlipper中,代码如下:

    private void addFaceData(){
            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(){
            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,便于修改点的颜色
                 */
            }
        
        }


    注意addGridView最后一部分是用来设置效果图最下方的那两个圆点,关于实现原理接下来会讲。

      表情列表的布局分析:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        
        <LinearLayout 
            android:layout_width="match_parent"
            android:layout_height="44dip"   
            android:id="@+id/chat_title"
            android:layout_alignParentTop="true"
            android:background="@drawable/chat_title_layer">
            <Button 
                android:id="@+id/chat_msg_button"
                android:layout_width="match_parent"
                android:layout_height="36dip"
                android:layout_weight="1.9"
                android:layout_marginLeft="8dip"
                android:layout_marginTop="3dip"
                android:text="消息(0)"
                android:textColor="@android:color/white"
                android:textSize="7pt"
                android:background="@drawable/msg_button_back"/>
            <TextView 
                android:id="@+id/chat_contact_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="龙行天下"
                android:textSize="8pt"
                android:textColor="@android:color/white"
                android:gravity="center"
                android:layout_gravity="center_vertical"/>
            <ImageButton 
                android:id="@+id/chat_contact_button"
                android:layout_width="match_parent"
                android:layout_height="36dip"
                android:layout_weight="2"
                android:layout_marginRight="8dip"
                android:layout_marginTop="3dip"
                android:background="@drawable/chat_contact_back"/>
            
        </LinearLayout>
        
        <RelativeLayout 
            android:id="@+id/faceLayout"
            android:layout_width="match_parent"
            android:layout_height="1dip"
            android:layout_alignParentBottom="true">
            
               <ViewFlipper 
                android:id="@+id/faceFlipper"
                android:layout_width="match_parent"
                android:layout_height="130dip"
                android:background="#d0d3d5"
                >
            </ViewFlipper>
            <LinearLayout 
                android:id="@+id/fill_the_gap"
                android:layout_width="match_parent"
                android:layout_height="1dip"
                android:background="#272b34"
                android:orientation="horizontal">
                
            </LinearLayout>
            <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>
        
        
        
        
        
          
        <LinearLayout
            android:id="@+id/chat_bottom_linear"
            android:layout_width="match_parent"
            android:layout_height="42dip"
            android:background="@drawable/chat_title_layer"
    
            android:orientation="horizontal"
            android:layout_above="@id/faceLayout"
            android:paddingTop="5dip"
            android:paddingBottom="3dip">
            
            <ImageButton 
                android:id="@+id/chat_bottom_look"
                android:layout_width="match_parent"
                android:layout_height="26dip"
                android:layout_weight="3.5"
                android:layout_marginLeft="7dip"
                android:layout_marginTop="5dip"
                android:background="@drawable/chat_bottom_look"/>
            <ImageButton 
                android:id="@+id/chat_bottom_add"
                android:layout_width="match_parent"
                android:layout_height="26dip"
                android:layout_weight="3.5"
                android:layout_marginLeft="7dip"
                android:layout_marginTop="5dip"
                android:background="@drawable/chat_bottom_add"/>
            <EditText 
                android:id="@+id/chat_bottom_edittext"
                android:layout_width="match_parent"
                android:layout_height="32dip"
                android:layout_marginLeft="5dip"
                android:layout_marginRight="7dip"
                android:layout_weight="1.5"
                android:background="@drawable/edit_fillet_shape"/>
    
            <Button
                android:id="@+id/chat_bottom_sendbutton"
                android:layout_width="match_parent"
                android:layout_height="26dip"
                android:layout_marginBottom="9dip"
                android:layout_marginRight="4dip"
                android:layout_weight="3.2"
                android:layout_gravity="top"
                android:background="@drawable/chat_button_fillet_shape"
                android:text="发送"
                android:textColor="@android:color/white" />
    "
            
            
        </LinearLayout>
        
        
        <ListView 
            android:id="@+id/chat_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/chat_title"
            android:layout_above="@id/chat_bottom_linear"
            android:fadingEdge="none"
            android:background="#f0f0f0"
            android:divider="#aaaaaa"
            android:dividerHeight="0px">        
        </ListView> 
        
        
    
    </RelativeLayout>

    这是整个ChatActivity的布局文件,找到含有ViewFlipper的那一块:

    <RelativeLayout
           
    android:id="@+id/faceLayout"
            android:layout_width
    ="match_parent" android:layout_height="1dip"
            android:layout_alignParentBottom
    ="true">
           
               <ViewFlipper
               
    android:id="@+id/faceFlipper"
                android:layout_width
    ="match_parent"
                android:layout_height
    ="130dip"
                android:background
    ="#d0d3d5"
                >
            </ViewFlipper>
            <LinearLayout
               
    android:id="@+id/fill_the_gap"
                android:layout_width
    ="match_parent"
                android:layout_height
    ="1dip"
                android:background
    ="#272b34"
                android:orientation
    ="horizontal">
               
            </LinearLayout>
            <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>

    id为pagePoint 的的LinearLayout就是盛放圆点ImageView的一个布局空间(ViewGroup),ViewFlipper中有几个子View就对应几个圆点,也通过addView()添加ImageView,这一点在addGridView中有体现,为了实现好看的效果这里为每一个圆点设置了布局文件,具体可参考我上传的Demo中point_image_layout

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical" >
        
        <ImageView 
            android:id="@+id/pointImageView"
            android:layout_width="6dip"
            android:layout_height="6dip"
            android:layout_marginLeft="8dip"
            android:layout_marginRight="8dip"/>
    
    </LinearLayout>


    为什么设置id为 fill_the_gap的Linearlayout? 这个比较麻烦讲,在本例中我关闭表情界面的方法是把id为faceLayout的RelativeLayout高度设为1dip。我想在关闭表情时,让表情界面回到初始状态(即显示第一页表情),这样每次打开表情界面都是第一页,和官方QQ一样,但是如果viewFlipper如果在屏幕上没有一点像素显示就无法调用setDisplayedChild(0)方法,因此需要把faceLayout的高度设为1dip 而非0dip ,这样又带来另一个问题是屏幕最下方出现了一条白线,不好看,所以就在ViewFlipper的最上方重叠了一个高为1dip 的色条,用于把这个白缝“封住”,大家可以尝试直接把高度设为0dip,会发现体验较差。

    这里封装了一个设置高度的方法:

    private void setFaceLayoutExpandState(boolean isexpand){
            if(isexpand==false){
    
                viewFlipper.setDisplayedChild(0);    
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=1;
                faceLayout.setLayoutParams(params);    
                /**height不设为0是因为,希望可以使再次打开时viewFlipper已经初始化为第一页 避免
                *再次打开ViewFlipper时画面在动的结果,
                *为了避免因为1dip的高度产生一个白缝,所以这里在ViewFlipper所在的RelativeLayout中ViewFlipper
                *上层添加了一个1dip高的黑色色块
                *
                *viewFlipper必须在屏幕中有像素才能执行setDisplayedChild()操作
                */
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_look);
                
                
            }
            else{
                /**
                 * 让软键盘消失
                 */
                ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow
                (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    
                
                
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=150;
                faceLayout.setLayoutParams(params);    
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_keyboard);
    
            }
        }


    另外这里涉及到了对键盘的操作,主要目的是为了实现和官方版差不多的效果:

    实现关闭键盘的方法:

    ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow
       (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);

    实现切换键盘状态的方法:原来打开则关闭,原来关闭则打开

    ((InputMethodManager)ChatActivity.this.getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);

      由于本节的内容相对较多,所以临时决定拆成两节,下一节会介绍如何在textView 中添加表情SpannableString的使用方法,并可能会添加进TabHost+FrameLayout控件,敬请期待~

         最后附上ChatActivity的代码:点击展开

    package com.example.android_qqfix;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Color;
    import android.graphics.drawable.ColorDrawable;
    import android.os.Bundle;
    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 java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    
    
    
    
    
    
    public class ChatActivity 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={"\呲牙","\淘气","\流汗","\偷笑","\再见","\敲打","\擦汗","\流泪","\掉泪","\小声","\炫酷","\发狂"
                 ,"\委屈","\便便","\菜刀","\微笑","\色色","\害羞"};
        
        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;
        
        ArrayList<ImageView> pointList=null;
        ArrayList<ArrayList<HashMap<String,Object>>> listGrid=null;
        protected ListView chatListView=null;
        protected Button chatSendButton=null;
        protected EditText editText=null;
        protected ViewFlipper viewFlipper=null;
        protected ImageButton chatBottomLook=null;
        protected RelativeLayout faceLayout=null;
        protected LinearLayout pagePoint=null,fillGapLinear=null;
       
        private boolean expanded=false;
        
        
        
        protected MyChatAdapter adapter=null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_chat);
        
            faceMap=new HashMap<String,Integer>();    
            chatList=new ArrayList<HashMap<String,Object>>();
            listGrid=new ArrayList<ArrayList<HashMap<String,Object>>>();
            pointList=new ArrayList<ImageView>();
            
            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);
            viewFlipper=(ViewFlipper)findViewById(R.id.faceFlipper);
            chatBottomLook=(ImageButton)findViewById(R.id.chat_bottom_look);
            faceLayout=(RelativeLayout)findViewById(R.id.faceLayout);
            pagePoint=(LinearLayout)findViewById(R.id.pagePoint);
            fillGapLinear=(LinearLayout)findViewById(R.id.fill_the_gap);
            
            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;
                        setPointEffect(0);
    
                    }
                }
                
            });
            
            /**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]);
            }
            
            
            addFaceData();
            addGridView();
            
            
        }
        
        private void addFaceData(){
            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(){
            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,便于修改点的颜色
                 */
            }
        
        }
        
        /**
         * 打开或者关闭软键盘,之前若打开,调用该方法后关闭;之前若关闭,调用该方法后打开
         */
        
        private void setSoftInputState(){
            ((InputMethodManager)ChatActivity.this.getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
        }
        
        private void setFaceLayoutExpandState(boolean isexpand){
            if(isexpand==false){
    
                viewFlipper.setDisplayedChild(0);    
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=1;
                faceLayout.setLayoutParams(params);    
                /**height不设为0是因为,希望可以使再次打开时viewFlipper已经初始化为第一页 避免
                *再次打开ViewFlipper时画面在动的结果,
                *为了避免因为1dip的高度产生一个白缝,所以这里在ViewFlipper所在的RelativeLayout中ViewFlipper
                *上层添加了一个1dip高的黑色色块
                *
                *viewFlipper必须在屏幕中有像素才能执行setDisplayedChild()操作
                */
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_look);
                
                
            }
            else{
                /**
                 * 让软键盘消失
                 */
                ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow
                (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    
                
                
                ViewGroup.LayoutParams params=faceLayout.getLayoutParams();
                params.height=150;
                faceLayout.setLayoutParams(params);    
                chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_keyboard);
    
            }
        }
        
        /**
         * 设置游标(小点)的显示效果
         * @param darkPointNum
         */
        private void setPointEffect(int darkPointNum){
            for(int i=0; i<pointList.size(); i++){
                pointList.get(i).setBackgroundResource(R.drawable.qian_point);
            }
            pointList.get(darkPointNum).setBackgroundResource(R.drawable.shen_point);
        }
        
        /**
         * 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"));
                    }
                    
                }
                //这里创建了一个方法内部类
                holder.image.setOnClickListener(new MyGridImageClickListener(position));
                
                
                
                return convertView;
            }
            
        }
        
        
        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>60){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex>0){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_out));                        
                                viewFlipper.showPrevious();
                                setPointEffect(childIndex-1);
                            }
                        }
                        else if(event.getX()-startX<-60){
                            moveable=false;
                            int childIndex=viewFlipper.getDisplayedChild();
                            /**
                             * 这里的这个if检测是防止表情列表循环滑动
                             */
                            if(childIndex<listGrid.size()-1){
                                viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_in));
                                viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_out));
                                viewFlipper.showNext();
                                setPointEffect(childIndex+1);
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:moveable=true;break;
                default:break;
                }
                
                return false;
            }
            
        }
        
        
        
        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]);
                
                
                System.out.println(holder);
                System.out.println(holder.imageView);
                holder.imageView.setBackgroundResource((Integer)chatList.get(position).get(from[0]));
                setFaceText(holder.textView, chatList.get(position).get(from[1]).toString());
                return convertView;
            }
            
        }
        
        
    
    }
    View Code


     希望大家继续支持我,你们的支持是我前进的动力^^


     

  • 相关阅读:
    ubuntu12.04 死机 卡屏 画面冻结解决方案
    Install Firefox 20 in Ubuntu 13.04, Ubuntu 12.10, Ubuntu 12.04, Linux Mint 14 and Linux Mint 13 by PPA
    ListView1.SelectedItems.Clear()
    android studio 下载地址
    jquery.slider.js jquery幻灯片测试
    jquery.hovermenu.js
    jquery.tab.js选项卡效果
    适配 placeholder,jquery版
    jquery.autoscroll.js jquery自动滚动效果
    将 Google Earth 地图集成到自己的窗体上的 简单控件
  • 原文地址:https://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_15.html
Copyright © 2020-2023  润新知