zoukankan      html  css  js  c++  java
  • 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


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


     

  • 相关阅读:
    GET和POST两种基本请求方法的区别
    GET与POST类型接口
    TCP连接与断开详解(socket通信)
    QC02
    QC01
    tcp三次握手和四次挥手
    ssh整合
    redis主从切换
    缓存解释(一级缓存,二级缓存)
    cxf整合spring代码
  • 原文地址:https://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_15.html
Copyright © 2011-2022 走看看