首先我说一下我的大致经过,因为公司是做时尚类的所有表情的做法直接模仿了小红书和好,没有做全部的表情代码,只做了80个常用表情,表情图片自己去准备。因为设计刚好离职的缘故,所以自己偷懒,直接解压了nice的apk包,然后采用了nice的表情图片(64 * 64px)。然后有两种做法,一种是edittext编辑的时候插入对应的表情图片,用到imagespan,然后自己跟服务端匹配了对应的表情标识(各种转码...超级麻烦)。然后做完之后发现图片不论我怎么剪裁,都不能完美的匹配字体大小,具体情况就是输入表情时输入框文字高度会出现下移,具体原因没有找到,后来我用搜狗输入法的表情输入之后,发现它为什么能完全的匹配,而且服务端不需要任何匹配,它也可以正常的显示,于是我想找到它是怎么做的,然后各种查找如何调用输入法的emoji表情,结果无奈并没有找到结果。于是我想是不是系统自己做了某些处理,因为android系统本身也有有表情的,那么输入法也是调用的系统的表情,是在我就先找到了unicode码大全 表情符号表 ,找到你需要的unicode码,然后用集合封装起来,与你的emoji表情对应。我是直接建了一个emoji工具类,用来封装emoji对象。
public class EmojiUtil {
private static ArrayList mEmojiList;
//获取emoji表情集合
public static ArrayList getEmojiList(){
if(mEmojiList == null){
mEmojiList = generateEmojis();
}}
return mEmojiList;
}}
private static ArrayList generateEmojis(){
ArrayList list = new ArrayList <>();
for(int i = 0; i
static {
mEmojiList = generateEmojis();
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
// 缩放图片
public static Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) {
// 获得图片的宽高
int width = bm.getWidth();
int height = bm.getHeight();
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 得到新的图片
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return newbm;
}
public static Bitmap getBitmapFromRes(Resources res, int resId, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
return zoomImg(bitmap, reqWidth, reqHeight);
}
/**
* 通过unicode码转换成字符串
*
* @param unicode
* @return
*/
public static String getEmojiStringByUnicode(int unicode) {
return new String(Character.toChars(unicode));
}
public class Emoji implements Serializable {
// emoji图片资源id
private int emojiResId;
// emoji代表的utf-8代码
private int emojiUnicode;
public int getEmojiResId(){
return emojiResId;
}}
public void setEmojiResId(int emojiResId){
this.emojiResId = emojiResId;
}}
public int getEmojiUnicode(){
return emojiUnicode;
}}
public void setEmojiUnicode(int emojiUnicode){
this.emojiUnicode = emojiUnicode;
}}
}}
@Override
public void onEmojiDelete() {
String text = mInputComment.getText().toString();
if (text.isEmpty()) {
return;
}
int action = KeyEvent.ACTION_DOWN;
int code = KeyEvent.KEYCODE_DEL;
KeyEvent event = new KeyEvent(action, code);
mInputComment.onKeyDown(KeyEvent.KEYCODE_DEL, event);
}
@Override
public void onEmojiClick(Emoji emoji) {
LogUtil.e("点击了emoji=" + emoji.getEmojiUnicode());
if (emoji != null) {
int index = mInputComment.getSelectionStart();
Editable editable = mInputComment.getEditableText();
if (index < 0 || index >= editable.length()) {
editable.append(EmojiUtil.getEmojiStringByUnicode(emoji.getEmojiUnicode()));
} else {
editable.insert(index, EmojiUtil.getEmojiStringByUnicode(emoji.getEmojiUnicode()));
}
}
}
最后关于表情页面片段我没有具体写,直接把github上面的大神写的拿过来用了,你也可以参考一下
public class FaceFragment extends Fragment {
public static FaceFragment Instance() {
FaceFragment instance = new FaceFragment();
Bundle bundle = new Bundle();
instance.setArguments(bundle);
return instance;
}
ViewPager faceViewPager;
EmojiIndicatorView faceIndicator;
ArrayList ViewPagerItems = new ArrayList<>();
ArrayList emojiList;
private int columns = 7;
private int rows = 3;
private OnEmojiClickListener listener;
public void setListener(OnEmojiClickListener listener) {
this.listener = listener;
}
@Override
public void onAttach(Activity activity) {
if (activity instanceof OnEmojiClickListener) {
this.listener = (OnEmojiClickListener) activity;
}
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
emojiList = EmojiUtil.getEmojiList();
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_face, container, false);
faceViewPager = (ViewPager) view.findViewById(R.id.face_viewPager);
faceIndicator = (EmojiIndicatorView) view.findViewById(R.id.face_indicator);
initViews();
return view;
}
private void initViews() {
initViewPager(emojiList);
}
private void initViewPager(ArrayList list) {
intiIndicator(list);
ViewPagerItems.clear();
for (int i = 0; i < getPagerCount(list); i++) {
ViewPagerItems.add(getViewPagerItem(i, list));
}
FaceVPAdapter mVpAdapter = new FaceVPAdapter(ViewPagerItems);
faceViewPager.setAdapter(mVpAdapter);
faceViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
int oldPosition = 0;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
faceIndicator.playBy(oldPosition, position);
oldPosition = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void intiIndicator(ArrayList list) {
faceIndicator.init(getPagerCount(list));
}
/**
* 根据表情数量以及GridView设置的行数和列数计算Pager数量
*
* @return
*/
private int getPagerCount(ArrayList list) {
int count = list.size();
return count % (columns * rows - 1) == 0 ? count / (columns * rows - 1)
: count / (columns * rows - 1) + 1;
}
private View getViewPagerItem(int position, ArrayList list) {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.layout_face_grid, null);//表情布局
GridView gridview = (GridView) layout.findViewById(R.id.chart_face_gv);
/**
* 注:因为每一页末尾都有一个删除图标,所以每一页的实际表情columns * rows - 1; 空出最后一个位置给删除图标
* */
final List subList = new ArrayList<>();
subList.addAll(list.subList(position * (columns * rows - 1),
(columns * rows - 1) * (position + 1) > list
.size() ? list.size() : (columns
* rows - 1)
* (position + 1)));
/**
* 末尾添加删除图标
* */
if (subList.size() < (columns * rows - 1)) {
for (int i = subList.size(); i < (columns * rows - 1); i++) {
subList.add(null);
}
}
Emoji deleteEmoji = new Emoji();
deleteEmoji.setEmojiResId(R.drawable.emoji_delete);
subList.add(deleteEmoji);
FaceGVAdapter mGvAdapter = new FaceGVAdapter(subList, getActivity());
gridview.setAdapter(mGvAdapter);
gridview.setNumColumns(columns);
// 单击表情执行的操作
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
if (position == columns * rows - 1) {
if (listener != null) {
listener.onEmojiDelete();
}
return;
}
if (listener != null) {
listener.onEmojiClick(subList.get(position));
}
}
});
return gridview;
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
class FaceGVAdapter extends BaseAdapter {
private List list;
private Context mContext;
public FaceGVAdapter(List list, Context mContext) {
super();
this.list = list;
this.mContext = mContext;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_face, null);
holder.iv = (ImageView) convertView.findViewById(R.id.face_image);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (list.get(position) != null) {
holder.iv.setImageBitmap(EmojiUtil.decodeSampledBitmapFromResource(getActivity().getResources(), list.get(position).getEmojiResId(),
EmojiUtil.dip2px(getActivity(), 32), EmojiUtil.dip2px(getActivity(), 32)));
}
return convertView;
}
class ViewHolder {
ImageView iv;
}
}
class FaceVPAdapter extends PagerAdapter {
// 界面列表
private List views;
public FaceVPAdapter(List views) {
this.views = views;
}
@Override
public void destroyItem(View arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView((View) (arg2));
}
@Override
public int getCount() {
return views.size();
}
// 初始化arg1位置的界面
@Override
public Object instantiateItem(View arg0, int arg1) {
((ViewPager) arg0).addView(views.get(arg1));
return views.get(arg1);
}
// 判断是否由对象生成界
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return (arg0 == arg1);
}
}
public interface OnEmojiClickListener {
void onEmojiDelete();
void onEmojiClick(Emoji emoji);
}
}
public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {
public interface SoftKeyboardStateListener {
void onSoftKeyboardOpened(int keyboardHeightInPx);
void onSoftKeyboardClosed();
}
private final List listeners = new LinkedList<>();
private final View activityRootView;
private int lastSoftKeyboardHeightInPx;
private boolean isSoftKeyboardOpened;
public SoftKeyboardStateWatcher(View activityRootView) {
this(activityRootView, false);
}
public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
this.activityRootView = activityRootView;
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
final Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
//此处判断键盘弹出高度的时候需要考虑部分机型(如:华为,魅族等)虚拟键盘的高度
int virtualKeyHeight = ScreenUtil.getVirtualKeyHeight();
// if more than 100 pixels, its probably a keyboard...
if (!isSoftKeyboardOpened && heightDiff >= (virtualKeyHeight + 100)) {
isSoftKeyboardOpened = true;
notifyOnSoftKeyboardOpened(heightDiff);
} else if (isSoftKeyboardOpened && heightDiff < (virtualKeyHeight + 100)) {
isSoftKeyboardOpened = false;
notifyOnSoftKeyboardClosed();
}
}
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
}
public boolean isSoftKeyboardOpened() {
return isSoftKeyboardOpened;
}
/**
* Default value is zero {@code 0}.
*
* @return last saved keyboard height in px
*/
public int getLastSoftKeyboardHeightInPx() {
return lastSoftKeyboardHeightInPx;
}
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.add(listener);
}
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.remove(listener);
}
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardOpened(keyboardHeightInPx);
}
}
}
private void notifyOnSoftKeyboardClosed() {
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardClosed();
}
}
}
}
这个东西知道吧,输入法的监听器,我遇到了虚拟键盘的问题,所以稍加了改动。
他就可以用来监听输入法的高度了,你获取到输入法高度之后永久记录下来然后以后动态设置给表情框,你会问如果我第一次打开表情怎么办?对,这确实是个问题,没什么解决办法,只能设置一个默认值了,然后啥时候监听到输入法的高度了之后再保存吧!好了所有的内容都在这了,希望对你有帮助。本来发图片的结果大小受限,就不发了。
最后声明一下,没有demo,没有demo,没有demo,重要的事说三遍,如果你真的需要,肯定也会自己研究一下吧。