您的位置:首页 > 移动开发

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

2015-10-18 22:28 615 查看
效果图:



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 收发表情