效果图:
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>
对应着:[天使]。这里我建立了一个类存储这种对应关系:
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[] { "[天使]","[恶魔]","[微笑]","[嘴馋]","[喜欢]","[帅气]", "[发呆]","[无语]","[冒汗]","[什么]","[小亲]","[大亲]", }; }
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>
当点击表情选择列表中的某一个表情后,获得这个表情的描述性信息,同时可以通过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(); } } } }