关于Android中的表情处理

Android中的表情处理在于,发送的表情要转换成Unicode码进行发送,接收的时候再通过Unicode码解析成相应的表情图片,这个Unicode码是通用的,所以不会导致Android和Ios显示混乱,但是他们的表情资源是不一样的,也就是我们通常看见的两个系统的手机表情显示不一样,再加上Android版本的表情看上去不好看,所以我们需要有一套自己的表情资源,映射着统一的Unicode码。

Emoji-1是一个完善的可以设置google、ios、emojione、twitter四种表情资源的开源库,通过添加依赖的方式就能使用,只不过不能外部设置表情的大小。

开源地址,感谢分享


你可以很轻松的读懂他的设置代码,并且根据自己的聊天界面去修改他demo中的代码。


第一次写聊天界面,记录一下:

首先是左右聊天信息显示的问题,我的思路是,建一个MessageData类,里面记录着每一条message所需要的信息,像头像地址(应该是静态的,在打开聊天界面的时候就应该通过网络获取初始化,包括对方的和自己的)、名字、消息内容、时间(如果有,在一定的时间间隔之外显示,后台计算,如果超过时间间隔就返回,否则不用返回,Android端有就显示,没有就不显示)等,然后我们还需要一个type字段,因为我们要知道这条消息是显示在左边还是右边(也就是接收到的消息还是发送出去的消息)。

package com.cloudcns.lottery.util.emoji;

/**
 * @author 马鹏昊
 * @date {2017.8.4}
 * @des 聊天的每一条消息的属性类
 * @updateAuthor
 * @updateDate
 * @updateDes
 */

public class MessageData {

    //对方的头像地址
    public static String otherPortrait ;
    //我的头像地址
    public static String myPortrait ;
    //左侧显示
    public static final int LEFT_SHOW = 0 ;
    //右侧显示
    public static final int RIGHT_SHOW = 1 ;

    //标志着在左侧(接收的)显示还是右侧(发送的)
    private int type ;

    //发送者头衔
    private String rank ;
    //发送者昵称
    private String name ;
    //消息内容
    private String content ;
    //发送时间(比较上一条的时间,如果大于5分钟则显示)
    private String date ;

    public String getRank() {
        return rank;
    }

    public void setRank(String rank) {
        this.rank = rank;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}


然后我们实现RecyclerView的adapter,在它的onBindViewHolder()方法中做文章,就是根据不同的type设置不同的效果:

关于左右分别显示,我的思路是ViewHolder中的View(也就是RecyclerView每一项的布局)最外层设置为LinearLayout,oriention设置成vertical,然后在其中,最上面定义显示时间的控件,再下面是整个消息体的显示布局,在ViewHolder中除了绑定需要显示数据的控件之外,把消息体显示布局的父容器也绑定,这样我们在onBindViewHolder中可以根据type去设置LinearLayout.LayoutParams的gravity属性为Gravity.LEFT还是Gravity.RIGHT来决定消息体是显示在左边还是右边。

时间显示可以根据后台返回的date是否为空来显示,有的话就把显示时间的控件设置为VISIBLE,并且setText(),没有则置为GONE。

package com.cloudcns.lottery.util.emoji;

import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.cloudcns.lottery.R;
import com.facebook.drawee.view.SimpleDraweeView;
import com.vanniktech.emoji.EmojiInformation;
import com.vanniktech.emoji.EmojiTextView;
import com.vanniktech.emoji.EmojiUtils;

import java.util.ArrayList;
import java.util.List;

public final class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
    private final List<MessageData> messages = new ArrayList<>();

    @Override
    public ChatViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
        final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        return new ChatViewHolder(layoutInflater.inflate(R.layout.adapter_chat, parent, false));
    }

    @Override
    public void onBindViewHolder(final ChatViewHolder chatViewHolder, final int position) {
        final MessageData messageData = messages.get(position);

        //类型
        int type = messageData.getType();
        LinearLayout.LayoutParams params ;
        if (type == MessageData.LEFT_SHOW) {
            //默认最左侧,但必须要设置,否则复用会混乱
            params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.gravity = Gravity.LEFT;
            chatViewHolder.mContainer.setLayoutParams(params);
            chatViewHolder.portrait.setImageURI(MessageData.otherPortrait);

        }else if(type == MessageData.RIGHT_SHOW){
            params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.gravity = Gravity.RIGHT;
            chatViewHolder.mContainer.setLayoutParams(params);
            chatViewHolder.portrait.setImageURI(MessageData.myPortrait);
        }

        //发送者头衔
        String rank = messageData.getRank();
        //发送者昵称
        String name = messageData.getName();
        //消息内容
        String content = messageData.getContent();
        //发送时间(比较上一条的时间,如果大于5分钟则显示)
        String date = messageData.getDate();

        final EmojiInformation emojiInformation = EmojiUtils.emojiInformation(content);
        final int res = R.dimen.emoji_size_default;

        //    if (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() == 1) {
        //      res = R.dimen.emoji_size_single_emoji;
        //    } else if (emojiInformation.isOnlyEmojis && emojiInformation.emojis.size() > 1) {
        //      res = R.dimen.emoji_size_only_emojis;
        //    } else {
        //      res = R.dimen.emoji_size_default;
        //    }

        if (TextUtils.isEmpty(date)) {
            chatViewHolder.date.setVisibility(View.GONE);
        }else {
            chatViewHolder.date.setVisibility(View.VISIBLE);
            chatViewHolder.date.setText(date);
        }
        chatViewHolder.rank.setText(rank);
        chatViewHolder.name.setText(name);

        chatViewHolder.textView.setEmojiSizeRes(res, false);
        chatViewHolder.textView.setText(content);
    }

    @Override
    public int getItemCount() {
        return messages.size();
    }

    /**
     * 本方发送消息加入消息列表
     *
     * @param message 所编辑的本次发送的消息
     */
    public void add(final MessageData message) {
        messages.add(message);
        notifyDataSetChanged();
    }

    static class ChatViewHolder extends RecyclerView.ViewHolder {

        final RelativeLayout mContainer;

        final TextView date;
        final EmojiTextView textView;
        final SimpleDraweeView portrait;
        final TextView rank;
        final TextView name;

        ChatViewHolder(final View view) {
            super(view);

            mContainer = (RelativeLayout) view.findViewById(R.id.container);

            date = (TextView) view.findViewById(R.id.date);
            textView = (EmojiTextView) view.findViewById(R.id.adapter_chat_text_view);
            portrait = (SimpleDraweeView) view.findViewById(R.id.portrait);
            rank = (TextView) view.findViewById(R.id.rank);
            name = (TextView) view.findViewById(R.id.name);
        }
    }
}


关于设置MessageData的部分(这里数据是写死的,替换成网络获取的即可):

 final String text = editText.getText().toString().trim();

                if (text.length() > 0) {
                    MessageData messageData = new MessageData();
                    messageData.setType(MessageData.RIGHT_SHOW);
                    messageData.setContent(text);
                    messageData.setRank("军长");
                    messageData.setName("Stephen.Curry");
                    String date = "15:34" ;//后台给的时间(如果和上次消息的时间间隔大于?分钟则显示,后台算)
                    if (!TextUtils.isEmpty(date)) {
                        messageData.setDate(date);
                    }
                    chatAdapter.add(messageData);
                    recyclerView.scrollToPosition(chatAdapter.getItemCount()-1);
                    editText.setText("");
                }
注意recyclerView.scrollToPosition(chatAdapter.getItemCount()-1);显示添加的最新一条消息。


到此为止,还有一个问题,聊天界面通常有一个标题栏,上面有返回键和当前和你聊天的人的名字,或者还有更多设置,那么当你打开软键盘的时候,你会发现整个布局被顶到上面去了,这怎么行呢,你可以通过设置当前Activity的android:windowSoftInputMode="adjustResize|stateHidden..."来设置成不顶布局,但此时就变成了覆盖了,我的思路是覆盖的时候通过滚动RecyclerView来实现不覆盖,但是我没有找到当软键盘显示的时候的监听方法,在网上找到了一个大神写的可以实现监听软键盘显示/隐藏的监听器,完美解决了我的问题。使用这个监听器的方法:

mKeyboardChangeListener = new KeyboardChangeListener(this);
mKeyboardChangeListener.setKeyBoardListener(this);

ChatActivity extends MyBaseActivity implements KeyboardChangeListener.KeyBoardListener

@Override
    public void onKeyboardChange(boolean isShow, int keyboardHeight) {
        if (isShow){
            recyclerView.scrollToPosition(chatAdapter.getItemCount()-1);
        }
    }

附上大神的实现代码:

package com.cloudcns.lottery.util.emoji;

import android.app.Activity;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;

/**
 * simple and powerful Keyboard show/hidden listener,view {@android.R.id.content} and {@ViewTreeObserver.OnGlobalLayoutListener}
 * Created by yes.cpu@gmail.com 2016/7/13.
 */
public class KeyboardChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {
    private static final String TAG = "ListenerHandler";
    private View mContentView;
    private int mOriginHeight;
    private int mPreHeight;
    private KeyBoardListener mKeyBoardListen;

    public interface KeyBoardListener {
        /**
         * call back
         * @param isShow true is show else hidden
         * @param keyboardHeight keyboard height
         */
        void onKeyboardChange(boolean isShow, int keyboardHeight);
    }

    public void setKeyBoardListener(KeyBoardListener keyBoardListen) {
        this.mKeyBoardListen = keyBoardListen;
    }

    public KeyboardChangeListener(Activity contextObj) {
        if (contextObj == null) {
            Log.i(TAG, "contextObj is null");
            return;
        }
        mContentView = findContentView(contextObj);
        if (mContentView != null) {
            addContentTreeObserver();
        }
    }

    private View findContentView(Activity contextObj) {
        return contextObj.findViewById(android.R.id.content);
    }

    private void addContentTreeObserver() {
        mContentView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        int currHeight = mContentView.getHeight();
        if (currHeight == 0) {
            Log.i(TAG, "currHeight is 0");
            return;
        }
        boolean hasChange = false;
        if (mPreHeight == 0) {
            mPreHeight = currHeight;
            mOriginHeight = currHeight;
        } else {
            if (mPreHeight != currHeight) {
                hasChange = true;
                mPreHeight = currHeight;
            } else {
                hasChange = false;
            }
        }
        if (hasChange) {
            boolean isShow;
            int keyboardHeight = 0;
            if (mOriginHeight == currHeight) {
                //hidden
                isShow = false;
            } else {
                //show
                keyboardHeight = mOriginHeight - currHeight;
                isShow = true;
            }

            if (mKeyBoardListen != null) {
                mKeyBoardListen.onKeyboardChange(isShow, keyboardHeight);
            }
        }
    }

    public void destroy() {
        if (mContentView != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                mContentView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        }
    }
}
地址: 点击打开链接




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值