[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;
}
}
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(); } } } }
相关文章推荐
- [Android通信]基于socket的聊天app(三):传输json数据
- [Android通信]基于socket的聊天app(四):实现一对一通信
- 做个类似微信的社交即时聊天App软件|广州明智科技App开发公司
- [Android通信]基于socket的聊天app(六):表情分组
- 实时聊天APP(websocket+hybridAPP)
- Android: 使用JitPack发布你的Github开源库
- java4Android(24)hashCode()/toString()
- 黑马程序员——ObjectiveC基础——封装、@property和点语法
- Android 属性动画
- Android技术简答题
- Android: Activity在Restore时的数据缓存问题
- Android: Fragment中getActivity()返回null的问题
- Android应用开发系列(二)登陆界面
- 仿QQ圆头像裁剪开源项目——ClipCircleHeadLikeQQ
- unity 右键关闭
- Android目录
- android.view.WindowLeaked解决办法
- Android TextView 文字多时加省略号
- PhysicsEditor 学习
- [Object C]_[初级]_[NSArray排序]