[Android通信]基于socket的聊天app(五):收发表情

效果图:

[Android通信]基于socket的聊天app(五):收发表情_第1张图片


1.点击表情按钮,弹出表情选择的列表,再次点击表情按钮,关闭列表。这个比较简单,就是设置GridView的可见性了。

下面给出界面布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#f0f0e0" >

    <RelativeLayout
        android:id="@+id/rl_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/mmtitle_bg" >

        <Button
            android:id="@+id/btn_back"
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:background="@drawable/selector_btn_back"
            android:text="返回"
            android:textColor="#ffffff"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="聊天中。。。"
            android:textColor="#ffffff"
            android:textSize="20.0sp" />

    </RelativeLayout>

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:scrollbars="none" />
    
    <RelativeLayout
        android:id="@+id/rl_bottom"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@drawable/layout_bg1" >

        <Button
            android:id="@+id/btn_face"
            android:layout_width="60dp"
            android:layout_height="40dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:text="表情" />
        
        <Button
            android:id="@+id/btn_send"
            android:layout_width="60dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:text="发送" />

        <EditText
            android:id="@+id/editText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_weight="1"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/btn_send"
            android:layout_toRightOf="@id/btn_face"
            android:background="@drawable/edittext1" />
    </RelativeLayout>
   
    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:numColumns="7"
        android:visibility="gone"
        android:background="@color/grey">

    </GridView>

</LinearLayout>

2.我们知道,一张表情图片对应着一个描述性词语,例如


对应着:[天使]。这里我建立了一个类存储这种对应关系:

package com.example.irun;

public class Expression {

	public static int[] drawable = new int[]
	{	
		R.drawable.dra,R.drawable.drb,R.drawable.drc,R.drawable.drd,R.drawable.dre,R.drawable.drf,
		R.drawable.drg,R.drawable.drh,R.drawable.dri,R.drawable.drg,R.drawable.drk,R.drawable.drl,
	};
	
	public static String[] describe = new String[] 
	{
		"[天使]","[恶魔]","[微笑]","[嘴馋]","[喜欢]","[帅气]",
		"[发呆]","[无语]","[冒汗]","[什么]","[小亲]","[大亲]",
	};
}

3.表情出现的地方有三处:表情选择列表,输入框,消息列表。

a.表情选择列表

主要就是为GridView设置适配器,这里可以简单地使用SimpleAdapter,每一项就是一个ImageView

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/expression"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp" />

</LinearLayout>

b.输入框

当点击表情选择列表中的某一个表情后,获得这个表情的描述性信息,同时可以通过SpannableString在输入框中加上表情图片


c.消息列表

虽然在输入框中看到的是文字+表情图片,但实际上只是一个字符串而已,其中表情实际是:[中文] 这样的形式。如果直接用setText(String)这样的方式,最终只会看到表情是:[中文] 这样的表示,所以要用setText(SpannableString)。同时,为了将所有[中文]的格式转换为表情,需要用到正则表达式去识别。当发送消息时,将输入框中的String通过正则表达式转换为包含了表情图片的SpannableString,再setText;当接受消息时,也将接受到的String通过正则表达式转换为SpannableString。

package com.example.irun;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.widget.Toast;

public class RegularExpressionUtil {

	private static Context context;
	public static void init(Context c)
	{
		if(context == null)
		{
			context = c;
		}
	}
	
	public static SpannableString change(String content)
	{
		//   \\表示\     \[表示[       [\u4e00-\u9fa5]表示中文
		//这个正则表达式用于检测以[]为边界,[]里面有一个或以上中文的字符串
		Pattern pattern = Pattern.compile("\\[[\u4e00-\u9fa5]{1,}\\]");
		SpannableString ss = new SpannableString(content);
		Matcher matcher = pattern.matcher(ss);
		while (matcher.find()) 
		{
			int position = check(matcher.group());
			if(position != -1)
			{
				Drawable d = context.getResources().getDrawable(Expression.drawable[position]);    
		        d.setBounds(0, 0, d.getIntrinsicWidth()/2, d.getIntrinsicHeight()/2);
		        ImageSpan span = new ImageSpan(d);
		        //以aaa[呵呵]bbb为例
		        //start-3,end-7;setSpan不包括起点,包括终点
		        ss.setSpan(span, matcher.start(), matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
			}
		}
		return ss;
	}
	
	//检测是否有表情
	private static int check(String s)
	{
		for (int i = 0; i < Expression.describe.length; i++) 
		{
			if(s.equalsIgnoreCase(Expression.describe[i]))
			{
				return i;
			}
		}
		return -1;
	}
}

package com.example.irun;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

//聊天界面
public class ChatFragment extends Fragment implements 
      OnClickListener, OnItemClickListener {

	private EditText editText;//输入要发送的信息
	private Button sendButton;//发送按钮
	private Button backButton;//返回按钮
	private Button faceButton;//表情按钮
	private TextView titleText;//标题
	
	private ListView listView;//聊天信息的滑动列表
	private ChatMsgListViewAdapter adapter;//聊天信息的适配器
	private List<ChatMsgEntity> list;//聊天信息的数据

	private GridView gridView;//表情的列表
	private SimpleAdapter gridAdapter;//表情列表的适配器
	private List<Map<String, Object>> gridList;//表情列表的数据
	
	private String toID;//表示跟谁聊天的窗口
	private MessageReceiver messageReceiver;//接受监听的对象
	
	public ChatFragment(String toID)
	{
		this.toID = toID;
	}
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment_chat, container, false);
		
		editText = (EditText)view.findViewById(R.id.editText);
		sendButton = (Button)view.findViewById(R.id.btn_send);
		backButton = (Button)view.findViewById(R.id.btn_back);
		faceButton = (Button)view.findViewById(R.id.btn_face);
		titleText = (TextView)view.findViewById(R.id.title);
		
		listView = (ListView)view.findViewById(R.id.listview);
		gridView = (GridView)view.findViewById(R.id.gridview);
		
		sendButton.setOnClickListener(this);
		backButton.setOnClickListener(this);
		faceButton.setOnClickListener(this);
		titleText.setText("跟" + toID + "的聊天");
		
		list = new ArrayList<ChatMsgEntity>();
		adapter = new ChatMsgListViewAdapter(getActivity(), list);
		listView.setAdapter(adapter);
		
		//填充数据
		gridList = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < Expression.drawable.length; i++) {
			Map<String, Object> item = new HashMap<String, Object>();
			item.put("face", Expression.drawable[i]);
	        gridList.add(item);
		}
		//第三个参数是单个grid的布局文件
		//第四个参数是Map对象的哪些key对应的value来生成列表项
		//第五个参数表示要填充的组件, Map对象key对应的资源与填充组件的顺序有对应关系 
		gridAdapter = new SimpleAdapter(getActivity(), gridList, R.layout.grid_face, 
				new String[]{"face"}, new int[]{R.id.expression});
		gridView.setAdapter(gridAdapter);
		gridView.setOnItemClickListener(this);
		
		RegularExpressionUtil.init(getActivity());
		initMessageReceiver();
		
		return view;
	}
	
	@Override
	public void onClick(View v) {
		if(v.getId() == R.id.btn_send) {
			send();
		}
		else if(v.getId() == R.id.btn_back) {
			MainActivity.bottomBar.setVisibility(View.VISIBLE);
			FragmentManager fm = getFragmentManager();  
	        FragmentTransaction tx = fm.beginTransaction();  
	        tx.hide(this);
	        tx.show(fm.findFragmentByTag("ChooseChatFragment"));
	        tx.commit();
		}
		else if(v.getId() == R.id.btn_face) {
			if(gridView.getVisibility() == View.VISIBLE) {
				gridView.setVisibility(View.GONE);
			} else {
				gridView.setVisibility(View.VISIBLE);
			}
		}
	}
	
	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position,
			long id) {
		String describe = Expression.describe[position];
		
		SpannableString ss = new SpannableString(describe);
		Drawable d = getResources().getDrawable(Expression.drawable[position]);    
        d.setBounds(0, 0, d.getIntrinsicWidth()/2, d.getIntrinsicHeight()/2);
        ImageSpan span = new ImageSpan(d);
        ss.setSpan(span, 0, describe.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        editText.append(ss);
	}
	
	private void send()
	{
		String content = editText.getText().toString();
		if(content.length() > 0) {
			ChatMsgEntity entity = new ChatMsgEntity();
			entity.setName(UserInfo.getID());
			entity.setDate(getDate());
			entity.setMessage(RegularExpressionUtil.change(content));
			entity.setMsgType(true);
			
			list.add(entity);
			adapter.notifyDataSetChanged();//通知ListView,数据已发生改变
			listView.setSelection(listView.getCount() - 1);//发送一条消息时,ListView显示选择最后一项
			
			editText.setText("");
			
			try {
				JSONObject root = new JSONObject();
				root.put("content", content);
				root.put("fromID", UserInfo.getID());
				root.put("toID", toID);
				SocketService.send(root.toString());
			} catch (JSONException e) {
				e.printStackTrace();
			}	
		}	
	}
	
	private String getDate() 
	{
		long time = System.currentTimeMillis();;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date d = new Date(time);  
        return format.format(d);  
    } 
	
	private void initMessageReceiver()
	{
		messageReceiver = new MessageReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("SocketService");
        getActivity().registerReceiver(messageReceiver,filter);
	}
		
	public class MessageReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
						
			String content = intent.getStringExtra("jsonString");
			try 
			{
				JSONObject root = new JSONObject(content);
				
				//错误:root.getString("toID").equalsIgnoreCase(UserInfo.getID())
				//因为对于每一个窗口来说,UserInfo.getID()都一样
				//toID表示发信息给谁
				boolean a = (root.getString("toID").equalsIgnoreCase("Group")) && (toID.equalsIgnoreCase("Group"));
				boolean b = (!root.getString("toID").equalsIgnoreCase("Group")) && (toID.equalsIgnoreCase(root.getString("fromID")));
				if(a || b)
				{
					if(content != null) 
					{
						ChatMsgEntity entity = new ChatMsgEntity();
						entity.setName(root.getString("fromID"));
						entity.setDate(getDate());
						SpannableString ss = RegularExpressionUtil.change(root.getString("content"));
						entity.setMessage(ss);
						entity.setMsgType(false);
						
						list.add(entity);
						adapter.notifyDataSetChanged();//通知ListView,数据已发生改变
						listView.setSelection(listView.getCount() - 1);//发送一条消息时,ListView显示选择最后一项
					}
				}
				
			} catch (JSONException e) {
				e.printStackTrace();
			}		
		}		
    }
}

你可能感兴趣的:(聊天app,收发表情)