基于socket的可发送表情简单即时通讯

前言

这段时间做的东西比较杂,但是对学习来说还是很有帮助的,这次做的基于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上实现的,逻辑不难,主要是界面上的操作,大家可以参考一下。这里先贴出界面图,比较丑陋,大家见谅。
模仿QQ主界面发送图片

主界面主要采用的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确实是亲测可用的。

猜你喜欢

转载自blog.csdn.net/firesmog/article/details/61409561