基于socket的可发送表情简单即时通讯
来源:互联网 发布:淘宝主店怎么绑定分店 编辑:程序博客网 时间:2024/04/27 15:46
前言
这段时间做的东西比较杂,但是对学习来说还是很有帮助的,这次做的基于socket的即时通讯也是为了更加了解IM,本来是打算使用openfire或者apollo服务器来实现的,但是中途时间上的问题,临时改了需求,后期的语音通话还是要借助第三方服务器的,这里主要是自己用myeclipse写了一个简单的代理服务器,主要是将客户端发来的消息转发到已经保存到链表中的socket中,实现比较简单,主要是解决发送图片的模块,我参考了很多文章,但是大多写的都不是很全,故自己写了一下,[socket即时通讯源码](https://github.com/firesmog/MyGreenDao),写这篇博客,一是为了记录自己的学习进度,而是希望能给正在学习路上的各位同学一点点帮助。
服务端介绍
这里的服务端是采用myeclipse写的,比较简单单,主要负责将任意客户端传过来的消息的进行转发,然后使得客户端收到信息后能在自己的界面上进行展示,具体代码如下,仅供参考。
public class tcpservice { private static final int SERVERPORT = 8090; private static List<Socket> mClientList = new ArrayList<Socket>(); private ExecutorService mExecutorService; private ServerSocket mServerSocket; public static void main(String[] args) { new tcpservice(); } public tcpservice() { try { //服务端套接字,这里给定客户端要连接的目标端口即可 mServerSocket = new ServerSocket(SERVERPORT); //使用线程池,来一个客户端,开一个线程处理 mExecutorService = Executors.newCachedThreadPool(); System.out.println("start..."); Socket client = null; while (true) { //监听客户端的连接,一旦客户端连接上,则将客户端套接字赋值给client client = mServerSocket.accept(); //在链表中保存客户端,以便后面的消息转发 mClientList.add(client); System.out.println(client.toString()); //开启线程 mExecutorService.execute(new ThreadServer(client)); } } catch (IOException e) { e.printStackTrace(); } } //新建自己的线程类,实现线程内run方法的实现 static class ThreadServer implements Runnable { private Socket mSocket; private BufferedReader mBufferedReader; private PrintWriter mPrintWriter; private String mStrMSG; //构造方法,将客户端套接字socket传进来 public ThreadServer(Socket socket) throws IOException { this.mSocket = socket; //获得客户端输入流,用于读取客户端数据 mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); mStrMSG = "user:"+this.mSocket.getInetAddress()+" come total:" + mClientList.size(); } //run方法的实现 public void run() { try { //判断客户端发送过来的信息是否是要退出聊天 while ((mStrMSG = mBufferedReader.readLine()) != null) { if (mStrMSG.trim().equals("exit")) { mClientList.remove(mSocket); mBufferedReader.close(); mPrintWriter.close(); mStrMSG = "user:"+this.mSocket.getInetAddress()+" exit total:" + mClientList.size(); mSocket.close(); sendMessage(); break; } else { // mStrMSG = mSocket.getInetAddress() + ":" + mStrMSG; System.out.println( mStrMSG); sendMessage(); } } } catch (IOException e) { e.printStackTrace(); } } private void sendMessage() throws IOException { System.out.println(mStrMSG); //遍历所有的客户端套接字,将消息转发出去 for (Socket client : mClientList) { mPrintWriter = new PrintWriter(client.getOutputStream(), true); mPrintWriter.println(mStrMSG); mPrintWriter.flush(); } } }}
服务端就介绍到,这里,因为实现比较简单,所以也不多做赘述。
客户端介绍
这里的客户端实在Androidstudio上实现的,逻辑不难,主要是界面上的操作,大家可以参考一下。这里先贴出界面图,比较丑陋,大家见谅。
主界面主要采用的ViewPger+Recycleview,发送表情主要是采用Recycleview+girdView+EditText,,下面写给出主界面布局,以及聊天界面布局,大家仅参考,具体内容,数据资源都在源码中,大家自行下载。
//主界面<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="example.com.myapplication.MainActivity"> <example.com.myapplication.MyViewPager.MyViewPager android:id="@+id/vp_main" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" > android:persistentDrawingCache="animation" </example.com.myapplication.MyViewPager.MyViewPager> <RadioGroup android:id="@+id/rd_group" android:layout_width="match_parent" android:layout_height="90dp" android:layout_alignParentBottom="true" android:orientation="horizontal"> <RadioButton android:id="@+id/rd_1" style="@style/tab_menu_item" android:drawableTop="@drawable/message" android:text="消息"/> <RadioButton android:id="@+id/rd_2" style="@style/tab_menu_item" android:drawableTop="@drawable/contactor" android:text="联系人"/> <RadioButton android:id="@+id/rd_3" style="@style/tab_menu_item" android:drawableTop="@drawable/statues" android:text="动态"/> </RadioGroup></LinearLayout>
//聊天界面<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:orientation="vertical" > <android.support.v7.widget.RecyclerView android:id="@+id/msg_recyclerView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </android.support.v7.widget.RecyclerView> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal" > <ImageView android:id="@+id/iv_motion" android:onClick="true" android:layout_marginLeft="10dp" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@drawable/chat_icon"/> <EditText android:onClick="true" android:id="@+id/input_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="Type something here" android:maxLines="2"/> <Button android:layout_marginLeft="20dp" android:id="@+id/btnsend" android:layout_width="80dp" android:layout_height="50dp" android:layout_marginTop="10dp" android:text="发送" android:textSize="20dp"/> </LinearLayout> <android.support.v4.view.ViewPager android:paddingTop="5dp" android:id="@+id/vp_motion" android:layout_width="match_parent" android:layout_height="150dp" android:visibility="gone"> </android.support.v4.view.ViewPager></LinearLayout>
聊天界面当中的girdview是为了展示图片内容的,在初始的时候采用Gone将其隐藏起来,点击的时候将其设置为Visable展示出来,这里要注意的是图片的显示与软键盘弹出来的冲突,具体完全解决问题的做法,本人还没做好,在本demo中只是做了点强制的切换,隐藏,使得在软键盘和图片展示界面切换的时候有点卡顿,大家感兴趣的自己解决一下,解决好了的可以教一下我,谢谢。这里顺便说一下,主界面中使用了MyViewPger是自定义的viewpager主要目的,其实是我自己之前做仿qq的Recycleview的侧滑删除Item操作时,测试事件分发机制用的,感兴趣的同学可以自行添加事件分发方法,打log查看一下事件分发顺序。这里已经有点偏离话题,大家勿怪。
MyViewPger代码如下:
package example.com.myapplication.MyViewPager;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
/**
* Created by firesmog on 2017/2/22.
*/
public class MyViewPager extends ViewPager {
private boolean noScroll = false;
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MyViewPager(Context context) {
super(context);
}
public void setNoScroll(boolean noScroll) {
this.noScroll = noScroll;
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
}
//下面内容其实是为了测试事假分发,其实有点绕,一段时间不做,就会有点生疏
@Override
public boolean onTouchEvent(MotionEvent arg0) {
/* return false;//super.onTouchEvent(arg0); */
Log.i(MyViewPager.class.getSimpleName(), ” onTouchEvent” + ” event = ” + arg0);
if (noScroll){
return false;
}
else
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev){
Log.i(MyViewPager.class.getSimpleName(), ” dispatchTouchEvent” + ” event = ” + ev + ” noScroll = ” + noScroll);
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
Log.i(MyViewPager.class.getSimpleName(), ” onInterceptTouchEvent” + ” event = ” + arg0);
if (noScroll) {
return false;
}
else
return super.onInterceptTouchEvent(arg0);
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {
super.setCurrentItem(item, smoothScroll);
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item);
}
}
到了这里的话,就需要给girdview,Recycleview等添加适配器了,这两个适配器在写的时候注意的地方有很多,我也是参考了别人的代码,做了修改,时间有点久,不记得是谁的博客了,见谅。首先需要展示图片,所以列出girdview的adapter。代码如下所示。
package example.com.myapplication.Adapter;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.List;import example.com.myapplication.Bean.HeaderViewBean;import example.com.myapplication.R;/** * Created by firesmog on 2017/3/3. */public class GridViewAdapter extends BaseAdapter { private List<HeaderViewBean> mDatas; private LayoutInflater mLayoutInflater; private Context context; private int columnWidth; /** * 页数下标,从0开始 */ private int mIndex; /** * 每页显示最大条目个数 ,默认是dimes.xml里 HomePageHeaderColumn 属性值的两倍 */ private int mPageSize; /** * @param context 上下文 * @param mDatas 传递的数据 * @param columnWidth * @param mIndex 页码 */ public GridViewAdapter(Context context, List<HeaderViewBean> mDatas, int columnWidth, int mIndex) { this.context = context; this.mDatas = mDatas; mLayoutInflater = LayoutInflater.from(context); this.mIndex = mIndex; mPageSize =28; this.columnWidth= columnWidth; } public GridViewAdapter(Context context, List<HeaderViewBean> mDatas, int index) { this.context = context; this.mDatas = mDatas; mLayoutInflater = LayoutInflater.from(context); this.mIndex = index; mPageSize =28; } /** * 先判断数据集的大小是否足够显示满本页?mDatas.size() > (mIndex+1)*mPageSize, * 如果够,则直接返回每一页显示的最大条目个数mPageSize, * 如果不够,则有几项返回几,(mDatas.size() - mIndex * mPageSize); */ @Override public int getCount() { return mDatas.size() > (mIndex + 1) * mPageSize ? mPageSize : (mDatas.size() - mIndex * mPageSize); } @Override public Object getItem(int position) { return mDatas.get(position + mIndex * mPageSize); } @Override public long getItemId(int position) { return position + mIndex * mPageSize; } @Override public View getView(final int position, View convertView, ViewGroup parent) { Log.i("TAG", "position:" + position); ViewHolder vh = null; if (convertView == null) { convertView = mLayoutInflater.inflate(R.layout.item_gird, parent, false); vh = new ViewHolder(); vh.iv = (ImageView) convertView.findViewById(R.id.id_iv_icon); convertView.setTag(vh); } else { vh = (ViewHolder) convertView.getTag(); } /** * 在给View绑定显示的数据时,计算正确的position = position + mIndex * mPageSize, */ int pos = position + mIndex * mPageSize; vh.iv.setImageResource(mDatas.get(pos).iconRes); return convertView; } class ViewHolder { public TextView tv; public ImageView iv; }}
有了展示图片的adapter的界面后,主要就是聊天对话框的dapter,代码如下:
package example.com.myapplication.Adapter;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.TextView;import java.util.List;import example.com.myapplication.Bean.Msg;import example.com.myapplication.R;/** * Created by firesmog on 2017/3/3. */public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder>{ List<Msg> msgList; @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycly, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { Msg msg = msgList.get(position); if (msg.getType() == Msg.TYPE_RECEIVE) { holder.leftLayout.setVisibility(View.VISIBLE); holder.rightLayout.setVisibility(View.GONE); holder.leftMsg.setText(msg.getContent()); } else if (msg.getType() == Msg.TYPE_SEND) { holder.rightLayout.setVisibility(View.VISIBLE); holder.leftLayout.setVisibility(View.GONE); holder.rightMsg.setText(msg.getContent()); } } public MsgAdapter(List<Msg> msgs) { this.msgList = msgs; } @Override public int getItemCount() { return msgList.size(); } static class ViewHolder extends RecyclerView.ViewHolder { LinearLayout leftLayout; LinearLayout rightLayout; TextView leftMsg; TextView rightMsg; ViewHolder(View view) { super(view); //这里要分为左边对话框 leftLayout = (LinearLayout) view.findViewById(R.id.left_layout); //这里要分为右边对话框 rightLayout = (LinearLayout) view.findViewById(R.id.right_layout); //这里要分为左边消息 leftMsg = (TextView) view.findViewById(R.id.left_msg); //这里要分为右边消息 rightMsg = (TextView) view.findViewById(R.id.right_msg); } }}
最后这里附上聊天Activity的代码,中间主要实现了socket连接、发送,在输入图片是,对图片的转换,这
里的将图片显示在编辑框中方法也写在了该activity中,接收服务器的图片再经过转化后才能在对话框中将图
片显示出来,这里注意,获取到的图片内容,并不是图片本身,而是用一个与图片资源对应的字符串数组来
替代图片,但是编辑框和对话框并不能通过string将图片展示出来,需要将之先转化为spannerString,但是
我们最好收到服务器的图片显示的时候,使用handler传输时,进行传值时,必须用CharSequence而不是
String ,说的乱七八糟,具体大家看代码吧。**关键点就是编辑框、对话框不能直接显示图片,它们只能显示
String,所以要将图片先转化对应的SpannerString传到对话框中才能显示,这是个人笨拙的简易理解,应
该有错误**
public class ChatActivity extends AppCompatActivity implements View.OnClickListener { private ViewPager mViewPager; private List<View> mViewPagerGridList; private static final String SERVERIP = "172.28.12.130"; private static final int SERVERPORT = 8090; private Thread mThread = null; private Socket mSocket = null; private BufferedReader mBufferedReader = null; private PrintWriter mPrintWriter = null; private static String mStrMSG = ""; private static String TAG = "TCP"; private List<Msg> msgList = new ArrayList<>(); private EditText inputText; private ImageView iv_motion; private Boolean IsShow=false; private ViewPager vp_motion; private List<HeaderViewBean> mDatas = new ArrayList<>(); private Button btnSend; private Button btLogin; private RecyclerView recyclerView; private MsgAdapter adapter; private int[] drawable; private TextView tv_Show; private boolean IsSend=false; private String[] faceScr; private Pattern pattern; private HashMap<String, Integer> faceBook; private Handler handler = new Handler() { // 该方法运行在主线程中 // 接收到handler发送的消息,对UI进行操作 @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub if (msg.what == 1) { // tv_Show.setText(msg.getData().getCharSequence("time")); //这里必须是CharSequence,如果传过来是String,图片是显示不出来的,大家可试一下 msgList.add(new Msg(msg.getData().getCharSequence("time"), Msg.TYPE_SEND)); adapter.notifyItemInserted(msgList.size() - 1); recyclerView.scrollToPosition(msgList.size() - 1); }else if(msg.what==2){ //这里必须是CharSequence,如果传过来是String,图片是显示不出来的,大家可试一下 msgList.add(new Msg(msg.getData().getCharSequence("recive"), Msg.TYPE_RECEIVE)); adapter.notifyItemInserted(msgList.size() - 1); recyclerView.scrollToPosition(msgList.size() - 1); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chat); initMsg(); initDatas(); //连接服务器 ConnectServ(); btnSend = (Button) this.findViewById(R.id.btnsend); // btLogin=(Button) this.findViewById(R.id.btLogin); inputText = (EditText) this.findViewById(R.id.input_text); mViewPager = (ViewPager) findViewById(R.id.vp_motion); iv_motion = (ImageView) findViewById(R.id.iv_motion); vp_motion=(ViewPager)findViewById(R.id.vp_motion); recyclerView = (RecyclerView) this.findViewById(R.id.msg_recyclerView); mViewPagerGridList = new ArrayList<>(); LayoutInflater inflater = getLayoutInflater(); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); adapter = new MsgAdapter(msgList); recyclerView.setAdapter(adapter); btnSend.setOnClickListener(this); //btLogin.setOnClickListener(this); iv_motion.setOnClickListener(this); inputText.setOnClickListener(this); //下面部分是针对girdview的,对girdview进行相关操作,显示图片 **// 每页显示最大条目个数 int pageSize = 28; //页数 int pageCount = (int) Math.ceil(mDatas.size() * 1.0 / pageSize); //获取屏幕的宽度,单位px int screenWidth = getResources().getDisplayMetrics().widthPixels; //获取GridView中每个item的宽度 = 屏幕宽度 / GridView显示的列数 int columnWidth = (int) Math.ceil((screenWidth) * 1.0 / 7); for (int index = 0; index < pageCount; index++) { GridView grid = (GridView) inflater.inflate(R.layout.gird_motion, mViewPager, false); //设置GridView每个item的宽度 grid.setColumnWidth(columnWidth); //设置GirdView的布局参数(宽和高,宽为包裹父容器,高 = columnWidth) grid.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, columnWidth)); grid.setAdapter(new GridViewAdapter(this, mDatas, index)); //响应图片选择事件 grid.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.i("TAG",position+"******************"+id+""); faceReplace(getApplicationContext(),drawable[(int) id],faceScr[(int)id],inputText); } }); mViewPagerGridList.add(grid);** } mViewPager.setAdapter(new MyViewPagerAdapter(mViewPagerGridList)); } //发送信息到服务器 public void sendMsg(){//因为,聊天界面分为发送端和接收端,两端的数据分别显示在左边和右边,需要用IsSend标记自己是发送端 IsSend=true; if (!content.equals("")) { mPrintWriter.println(content); mPrintWriter.flush(); inputText.setText(""); } mThread = new Thread(mRunnable); mThread.start(); } private Runnable mRunnable = new Runnable() { public void run() { while (true) { try { if ((mStrMSG = mBufferedReader.readLine()) != null) { if(IsSend){ Message message=new Message(); Bundle bundle=new Bundle(); //这里是将接收到的消息,先判断是否有图片,如果有在抽取出来显示, //而且这里返回值我用的是CharSequence,如果是String则显示不出图片 CharSequence addSmileySpans=addSmileySpans(mStrMSG); bundle.putCharSequence("time",addSmileySpans); message.setData(bundle);//bundle传值,耗时,效率低 handler.sendMessage(message);//发送message信息 message.what=1;//标志是哪个线程传数据 }else{ Message message=new Message(); Bundle bundle=new Bundle(); CharSequence addSmileySpans=addSmileySpans(mStrMSG); bundle.putCharSequence("recive",addSmileySpans); message.setData(bundle);//bundle传值,耗时,效率低 handler.sendMessage(message);//发送message信息 message.what=2;//标志是哪个线程传数据 } IsSend=false; } } catch (Exception e) { Log.e(TAG, e.toString()); } } } }; private void initMsg() { Msg msg1 = new Msg("hello sealong", Msg.TYPE_RECEIVE); msgList.add(msg1); Msg msg2 = new Msg("hello peipei", Msg.TYPE_SEND); msgList.add(msg2); Msg msg = new Msg("What are you doing", Msg.TYPE_RECEIVE); msgList.add(msg); } public void ConnectServ(){ new Thread(new Runnable() { @Override public void run() { try { Log.i(TAG, "lianjie "); // ①Socket实例化,连接服务器 mSocket = new Socket(SERVERIP, SERVERPORT); // ②获取Socket输入输出流进行读写操作 mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); mPrintWriter = new PrintWriter(mSocket.getOutputStream(), true); } catch (Exception e) { // TODO: handle exception Log.e(TAG, e.toString()); } } }).start(); } private void initDatas() { initMotion(); for(int i=0;i<drawable.length;i++){ HeaderViewBean headerViewBean = new HeaderViewBean("Item " + (i + 1), drawable[i]); mDatas.add(headerViewBean); headerViewBean=null; } } //下面两个数据时加载图片资源,并且为图片资源设置对应的名称,用于后期使用正则表达式将图片分离出来 **public void initMotion(){ drawable = new int[] { R.drawable.f000,R.drawable.f001,R.drawable.f002,R.drawable.f003,R.drawable.f004,R.drawable.f005, R.drawable.f006,R.drawable.f007,R.drawable.f008,R.drawable.f009,R.drawable.f010,R.drawable.f011, R.drawable.f012,R.drawable.f013,R.drawable.f014,R.drawable.f015,R.drawable.f016,R.drawable.f017, R.drawable.f018,R.drawable.f019,R.drawable.f020,R.drawable.f021,R.drawable.f022,R.drawable.f023, R.drawable.f024,R.drawable.f025,R.drawable.f026,R.drawable.f027,R.drawable.f059,R.drawable.f028, R.drawable.f029,R.drawable.f030,R.drawable.f031,R.drawable.f032,R.drawable.f033,R.drawable.f034, R.drawable.f035,R.drawable.f036,R.drawable.f037,R.drawable.f038,R.drawable.f039,R.drawable.f040, R.drawable.f041,R.drawable.f042,R.drawable.f043,R.drawable.f044,R.drawable.f045,R.drawable.f046, R.drawable.f047,R.drawable.f048,R.drawable.f049,R.drawable.f050,R.drawable.f051,R.drawable.f052, R.drawable.f053,R.drawable.f054,R.drawable.f055,R.drawable.f056,R.drawable.f057,R.drawable.f058, }; faceScr = new String[] { "#000", "#001", "#002", "#003", "#004", "#005", "#006", "#007", "#008", "#009", "#010", "#011", "#012", "#013", "#014", "#015", "#016", "#017", "#018", "#019", "#020", "#021", "#022", "#023", "#024", "#025", "#026", "#027", "#028", "#029", "#030", "#031", "#032", "#033", "#034", "#035", "#036", "#037", "#038", "#039", "#040", "#041", "#042", "#043", "#044", "#045", "#046", "#047", "#048", "#049", "#050", "#051", "#052", "#053", "#054", "#055", "#056", "#057", "#058", "#059" }; faceBook=buildSmileyToRes(); pattern=buildPattern(); }****//下面的方法是抽取出图片,并将图片转化SpannableString private HashMap<String, Integer> buildSmileyToRes() { /** * 文字和ID不匹配,异常 */ if (drawable.length != faceScr.length) { throw new IllegalStateException("Smiley resource ID/text mismatch"); } HashMap<String, Integer> smileyToRes = new HashMap<String, Integer>( faceScr.length); for (int i = 0; i < faceScr.length; i++) { smileyToRes.put(faceScr[i], drawable[i]); } return smileyToRes; } public void showMotion(){ if(IsShow==false){ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(inputText.getWindowToken(),0); vp_motion .setVisibility(View.VISIBLE); IsShow=true; }else { vp_motion.setVisibility(View.GONE); IsShow=false; } } public void faceReplace(Context context, int resourceId, String spannableStr, EditText edit) { /** * 根据ID获取图片资源 */ Drawable dr = context.getResources().getDrawable(resourceId); dr.setBounds(0, 0, dr.getIntrinsicWidth()*4/5, dr.getIntrinsicHeight()*4/5); /** * SpannableString 配置被替代的文字描述;ImageSpan 配置替代的图片 */ SpannableString spanStr = new SpannableString(spannableStr); ImageSpan spanImg = new ImageSpan(dr, ImageSpan.ALIGN_BASELINE); /** * 将文字如【smile】替换为表情 参数二、参数三分别指定所要替代字符的位置 */ spanStr.setSpan(spanImg, 0, spanStr.length(), spanStr.SPAN_EXCLUSIVE_EXCLUSIVE); edit.append(spanStr); } public CharSequence addSmileySpans(CharSequence text) { SpannableStringBuilder builder = new SpannableStringBuilder(text); Matcher matcher = pattern.matcher(text); while (matcher.find()) { int resId = faceBook.get(matcher.group()); Drawable dr = this.getResources().getDrawable(resId); dr.setBounds(0, 0, dr.getIntrinsicWidth()*4/5, dr.getIntrinsicHeight()*4/5); builder.setSpan(new ImageSpan(dr, ImageSpan.ALIGN_BASELINE), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return builder; } private Pattern buildPattern() { StringBuilder patternString = new StringBuilder(faceScr.length * 3); patternString.append('('); for (String s : faceScr) { patternString.append(Pattern.quote(s)); patternString.append('|'); } patternString.replace(patternString.length() - 1, patternString.length(), ")"); return Pattern.compile(patternString.toString()); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btnsend: sendMsg(); break; case R.id.iv_motion: showMotion(); break; case R.id.input_text: vp_motion.setVisibility(View.GONE); break; }** }}
最后给大家道歉一下,因为,本人写的博客不多,有点乱,时间也有点紧,所以可能看起来会很费力,希望大家谅解,也真心希望能帮到大家,该demo确实是亲测可用的。
- 基于socket的可发送表情简单即时通讯
- android 基于socket的表情发送
- Android 基于Socket发送表情
- 用java写的基于Socket的简单即时通讯程序
- 用java写的基于Socket的简单即时通讯程序
- 即时通讯基于socket的tcp方式
- 基于可编辑DIV的带表情编辑器
- [Android通信]基于socket的聊天app(五):收发表情
- [Android通信]基于socket的聊天app(六):表情分组
- 即时通讯的动态表情的处理
- python学习系列(八) --- socket实现简单的即时通讯
- Java Socket---一个简单的即时通讯小Demo
- 基于环信的仿QQ即时通讯的简单实现
- C#基于SMTP协议和SOCKET通信,实现邮件内容和附件的发送,并可隐藏收件人
- 基于socket的发送接收文本
- 基于XMPP的即时通讯
- 基于socket的简单通讯
- 基于socket的简单网络程序设计
- SlidingMenu侧滑
- 字符串分割c++
- C#中DllImport用法汇总
- 算法练习
- springmvc的头文件格式
- 基于socket的可发送表情简单即时通讯
- Spring Boot application.propertis配置文件的相关通用属性
- sqlMapConfig.xml的头文件格式
- PREV-34矩阵翻硬币 (高精度开方,相乘)
- 比赛排名
- 子串计算
- log4j的文件配置
- matlab例题及demo分析
- Java Custom HashMap