闲来无事花了一点空暇时间写了一个模仿Android版手机QQ的表情输入框

效果图如下



实现步骤
一:布局
二:录入填充表情
三:监听表情的适配器的触摸监听显示出预览框

一 、布局

我是自己自定一个ExpressionGridView(RelativeLayout)只是集成了这些方法的一个自定义View而已
activity_main.xml

<RelativeLayout 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"><android.support.design.widget.TextInputLayout
        android:id="@+id/expression_text_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="8dp"><EditText
            android:layout_width="match_parent"android:layout_height="wrap_content" /></android.support.design.widget.TextInputLayout><RelativeLayout
        android:layout_width="wrap_content"android:layout_height="223dp"android:layout_alignParentBottom="true"><view.ExpressionGridView
            android:id="@+id/expression_gridview"android:layout_width="fill_parent"android:layout_height="123dp"android:layout_alignParentBottom="true" /><RelativeLayout
            android:id="@+id/face_view"android:layout_width="80dp"android:layout_height="80dp"android:background="#78072026"android:visibility="gone"><!--  预览框--><ImageView
                android:id="@+id/face_img"android:layout_width="30dp"android:layout_height="30dp"android:layout_centerInParent="true" /></RelativeLayout></RelativeLayout>
</RelativeLayout>

二、录入填充表情

下载QQ解压出里面的资源文件获取表情包统一命名规范后定义  因为这些是我以前做个类似软件的时候使用了的现成资源拿来用

定义好的表情

package view;import anroid.qq_expression.shallcheek.shallcheek_qq_expression.R;/*** 表情文件** @author shallcheek* @version 1.0*/
public class Expressions {public static Integer[] expressionImgs = new Integer[]{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};/*** 本地表情的名*/public static String[] expressionImgNames = new String[]{"[f001]","[f002]", "[f003]", "[f004]", "[f005]", "[f006]", "[f007]","[f008]", "[f009]", "[f010]", "[f011]", "[f012]", "[f013]","[f014]", "[f015]", "[f016]", "[f017]", "[f018]", "[f019]","[f020]", "[f021]", "[f022]", "[f023]"};public static Integer[] expressionImgs1 = new Integer[]{R.drawable.f024,R.drawable.f025, R.drawable.f026, R.drawable.f027, 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};/*** 本地表情的名*/public static String[] expressionImgNames1 = new String[]{"[f024]","[f025]", "[f026]", "[f027]", "[f028]", "[f029]", "[f030]","[f031]", "[f032]", "[f033]", "[f034]", "[f035]", "[f036]","[f037]", "[f038]", "[f039]", "[f040]", "[f041]", "[f042]","[f043]", "[f044]", "[f045]", "[f046]"};public static Integer[] expressionImgs2 = new Integer[]{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, R.drawable.f059,R.drawable.f060, R.drawable.f061, R.drawable.f062, R.drawable.f063,R.drawable.f064, R.drawable.f065, R.drawable.f066, R.drawable.f067,R.drawable.f068, R.drawable.f069};/*** 本地表情的名*/public static String[] expressionImgNames2 = new String[]{"[f047]","[f048]", "[f049]", "[f050]", "[f051]", "[f052]", "[f053]","[f054]", "[f055]", "[f056]", "[f057]", "[f058]", "[f059]","[f060]", "[f061]", "[f062]", "[f063]", "[f064]", "[f065]","[f066]", "[f067]", "[f068]", "[f069]"};public static Integer[] expressionImgs3 = new Integer[]{R.drawable.f070,R.drawable.f071, R.drawable.f072, R.drawable.f073, R.drawable.f074,R.drawable.f075, R.drawable.f076, R.drawable.f077, R.drawable.f078,R.drawable.f079, R.drawable.f080, R.drawable.f081, R.drawable.f082,R.drawable.f083, R.drawable.f084, R.drawable.f085, R.drawable.f086,R.drawable.f087, R.drawable.f088, R.drawable.f089, R.drawable.f090,R.drawable.f091, R.drawable.f092};/*** 本地表情的名*/public static String[] expressionImgNames3 = new String[]{"[f070]","[f071]", "[f072]", "[f073]", "[f074]", "[f075]", "[f076]","[f077]", "[f078]", "[f079]", "[f080]", "[f081]", "[f082]","[f083]", "[f084]", "[f085]", "[f086]", "[f087]", "[f088]","[f089]", "[f090]", "[f091]", "[f092]"};public static Integer[] expressionImgs4 = new Integer[]{R.drawable.f093,R.drawable.f094, R.drawable.f095, R.drawable.f096, R.drawable.f097,R.drawable.f098, R.drawable.f099, R.drawable.f100, R.drawable.f101,R.drawable.f102, R.drawable.f103, R.drawable.f104, R.drawable.f105,R.drawable.f106, R.drawable.f107, R.drawable.f108, R.drawable.f109,R.drawable.f110, R.drawable.f111, R.drawable.f112, R.drawable.f113,R.drawable.f114, R.drawable.f115};/*** 本地表情的名*/public static String[] expressionImgNames4 = new String[]{"[f093]","[f094]", "[f095]", "[f096]", "[f097]", "[f098]", "[f099]","[f100]", "[f101]", "[f102]", "[f103]", "[f104]", "[f105]","[f106]", "[f107]", "[f108]", "[f109]", "[f110]", "[f111]","[f112]", "[f113]", "[f114]", "[f115]"};/*** 在存入数据库时,将表情名字进行替换即*/public static String[] replaceStrings(String[] str, String[] str2) {String newStr[] = new String[str.length - 1];for (Integer i = 0; i < str.length; i++) {newStr[i] = str[i].replace(str[i], str2[i]);}return newStr;}}

定义好一个mode 将弄成数组制作填充进去

    List<ExpressionMode> expressionUtils = new ArrayList<>();List<Integer> imgs = new ArrayList<>();imgs.addAll(Arrays.<Integer>asList(Expressions.expressionImgs));imgs.addAll(Arrays.<Integer>asList(Expressions.expressionImgs1));imgs.addAll(Arrays.<Integer>asList(Expressions.expressionImgs2));imgs.addAll(Arrays.<Integer>asList(Expressions.expressionImgs3));imgs.addAll(Arrays.<Integer>asList(Expressions.expressionImgs4));List<String> nams = new ArrayList<>();nams.addAll(Arrays.asList(Expressions.expressionImgNames));nams.addAll(Arrays.asList(Expressions.expressionImgNames1));nams.addAll(Arrays.asList(Expressions.expressionImgNames2));nams.addAll(Arrays.asList(Expressions.expressionImgNames3));nams.addAll(Arrays.asList(Expressions.expressionImgNames4));for (int i = 0; i < imgs.size(); i++) {ExpressionMode expressionMode = new ExpressionMode();expressionMode.setValue(nams.get(i));expressionMode.setResources(imgs.get(i));expressionUtils.add(expressionMode);}

ExpressionMode.java

/*** Created by Shall on 2015.9.11.*/
public class ExpressionMode {private int resources;private int resourcesgif;private String value;public int getResources() {return resources;}public void setResources(int resources) {this.resources = resources;}public int getResourcesgif() {return resourcesgif;}public void setResourcesgif(int resourcesgif) {this.resourcesgif = resourcesgif;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

自定义View ExpressionGridView 里面集成了ViewPage 使用GridView填充表情

  public void setExpressionModes(List<ExpressionMode> expressionModes) {this.expressionModes = expressionModes;page = expressionModes.size() / (PAGE_MAX_NUMBER) + 1;expressionGridViewList = new ArrayList<>();for (int i = 0; i < page; i++) {final GridView gridView = (GridView) View.inflate(getContext(), R.layout.gridview, null);List<ExpressionMode> list = this.expressionModes.subList(PAGE_MAX_NUMBER * i, PAGE_MAX_NUMBER * (i + 1) > expressionModes.size() ? expressionModes.size() - 1 : PAGE_MAX_NUMBER * (i + 1));lists.add(list);if (i == 0) {this.list = list;}final ExpressionAdapter expressionAdatper = new ExpressionAdapter(list, getContext());gridView.setAdapter(expressionAdatper);gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {expressionViewPager.setScanScroll(true);//屏蔽ViewPage滑动事件isMove = true;setSelect(choose);return false;}});gridView.setOnTouchListener(this);gridView.setOnItemClickListener(this);expressionGridViewList.add(gridView);}expressionViewPager = (ExpressionViewPager) View.inflate(getContext(), R.layout.viewpage, null);addView(expressionViewPager);PagerAdapter mPagerAdapter = new PagerAdapter() {@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic int getCount() {return expressionGridViewList.size();}@Overridepublic void destroyItem(View container, int position, Object object) {((ViewPager) container).removeView(expressionGridViewList.get(position));}@Overridepublic Object instantiateItem(View container, int position) {((ViewPager) container).addView(expressionGridViewList.get(position));return expressionGridViewList.get(position);}};expressionViewPager.setAdapter(mPagerAdapter);expressionViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//                relativeLayout.setVisibility(GONE);
//                imageView.setVisibility(View.GONE);}@Overridepublic void onPageSelected(int position) {list = lists.get(position);}@Overridepublic void onPageScrollStateChanged(int state) {}});}

表情的插入根据OnItemClickListener 的点击获取到点击的mode 填充到输入框

    @Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {ExpressionAdapter expressionAdapter = (ExpressionAdapter) parent.getAdapter();ExpressionMode expressionMode = (ExpressionMode) expressionAdapter.getItem(position);ExpressionUtil.installExpression(getContext(), editText, expressionMode);}

实现代码

    /*** 插入表情*/public static void installExpression(Context mCon, EditText mEditTextContent, ExpressionMode expressionMode) {Bitmap bitmap = BitmapFactory.decodeResource(mCon.getResources(), expressionMode.getResources());bitmap = Bitmap.createScaledBitmap(bitmap, dip2px(30, mCon), dip2px(30, mCon), true);ImageSpan imageSpan = new ImageSpan(mCon, bitmap);SpannableString spannableString = new SpannableString(expressionMode.getValue().substring(1, expressionMode.getValue().length() - 1));spannableString.setSpan(imageSpan, 0, expressionMode.getValue().length() - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);mEditTextContent.getText().insert(mEditTextContent.getSelectionStart(), spannableString);}

三、滑动监听探出预览框
获取到到View的滑动X,Y左边计算出滑动的item 并展示出来预览框

//首先需要长按时间完成之后才处理监听事件  屏蔽ViewPage的事件监听gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {expressionViewPager.setScanScroll(true);//屏蔽ViewPage滑动事件isMove = true;setSelect(choose);return false;}});gridView.setOnTouchListener(this);

触摸事件的处理

@Overridepublic boolean onTouch(View v, MotionEvent event) {final int action = event.getAction();final float x = event.getX();final float y = event.getY();Log.e("12","x:"+x+"y:"+y);int heighnum = (int) (y / (v.getHeight() / VERTICAL_NUMBER));int widthnum = (int) (x / (v.getWidth() / TRANSVERSE_NUMBER));//超出View 范围if (heighnum <= 0) heighnum = 0;if (heighnum >= VERTICAL_NUMBER) heighnum = VERTICAL_NUMBER - 1;if (widthnum <= 0) widthnum = 0;if (widthnum >= TRANSVERSE_NUMBER) widthnum = TRANSVERSE_NUMBER - 1;//没有填充满int listSize = list.size();if (heighnum >= listSize / TRANSVERSE_NUMBER && widthnum >= listSize % TRANSVERSE_NUMBER) {widthnum = listSize % TRANSVERSE_NUMBER;heighnum = listSize / TRANSVERSE_NUMBER;heighnum--;}final int c = (heighnum) * TRANSVERSE_NUMBER + widthnum;choose = c;if (choose >= listSize)choose = listSize - 1;//需要减1 不要越界switch (action) {case MotionEvent.ACTION_UP:choose = -1;//expressionViewPager.setScanScroll(false);isMove = false;relativeLayout.setVisibility(GONE);imageView.setVisibility(View.GONE);break;default:if (isMove)setSelect(choose);break;}return false;}

ViewPage监听处理

public class ExpressionViewPager extends ViewPager {private boolean isCanScroll = false;public ExpressionViewPager(Context context) {super(context);}public ExpressionViewPager(Context context, AttributeSet attrs) {super(context, attrs);}public void setScanScroll(boolean isCanScroll) {this.isCanScroll = isCanScroll;}@Overrideprotected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {if (v instanceof ViewPager) {return isCanScroll;}return super.canScroll(v, checkV, dx, x, y);}@Overridepublic void scrollTo(int x, int y) {super.scrollTo(x, y);}}
   imageView.setVisibility(View.VISIBLE);relativeLayout.setVisibility(VISIBLE);int w = choose % TRANSVERSE_NUMBER;int h = choose / TRANSVERSE_NUMBER;//设置弹出框的大小个位置View v = expressionGridViewList.get(0);RelativeLayout.LayoutParams layoutParams = new LayoutParams((v.getWidth() / TRANSVERSE_NUMBER), (v.getWidth() / TRANSVERSE_NUMBER));layoutParams.setMargins(w * (v.getWidth() / TRANSVERSE_NUMBER), (h) * (v.getHeight() / VERTICAL_NUMBER) - 30 + v.getHeight() / VERTICAL_NUMBER, 0, 0);relativeLayout.setLayoutParams(layoutParams);imageView.setBackgroundResource(list.get(choose > list.size() ? list.size() : select).getResources());

介绍的简单凑合着看吧 不懂可以留言可以加我微信 shallcheek

开发环境Android Studio

下载地址

Android 模仿手机QQ表情输入和表情预览框相关推荐

  1. Android: 实现类似QQ、微信的表情输入键盘

    需求 最近在写北邮人论坛客户端时,有一个需求是实现像手机QQ.微信那样的表情输入键盘,效果图: 表情键盘本身并不难做,无非就是一个带SlidingTab的ViewPager,困扰我的地方在于,如何正确 ...

  2. android测试qq,Android平台手机QQ和360手机卫士测试

    Android平台手机QQ和360手机卫士测试 Android平台手机QQ和360手机卫士测试 目前最热的Android手机平台,使用豌豆荚手机精灵完成最新版安装后,对两者的运行兼容情况进行了详细的体 ...

  3. android QQ动态tab,变化忒大了!Android版手机QQ 5.0体验

    变化忒大了!Android版手机QQ 5.0体验 出处:快科技 2014-07-28 12:02:56     作者:随心 编辑:随心[爆料] 收藏文章 内容导航: 第[01]页:[Android版手 ...

  4. 模仿手机QQ以视频流背景的登录界面

    1. 思路:很简单,VideoView播放视频. 布局方式如下: <?xml version="1.0" encoding="utf-8"?> &l ...

  5. Android模仿淘宝语音输入条形动画,录音动画自定义View

    Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...

  6. Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解

    Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 目录 Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 一.OpenGL ES渲染管线 1.基本处 ...

  7. php拍照从手机相册中选择,微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法示例...

    本文实例讲述了微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法.分享给大家供大家参考,具体如下: 目前中js-sdk 1.0版本中,预览图片提供了2个接口,接口的定义参考官方文档 1.预览 ...

  8. 手机端点击图片全屏预览

    <!doctype html> 手机端点击图片全屏预览 <div class="category"><img src="1.jpg" ...

  9. Android仿手机QQ空间动态评论,自动定位到输入框

    手机QQ空间浏览好友动态时,可以直接对动态评论,点击某条评论,动态列表自动滚动,使输入框刚好在该评论下面,而不会覆盖住评论内容.如下图所示, 首先要实现输入框刚好在输入面板上面,且动态列表不会被挤上去 ...

最新文章

  1. Oracle数据库日常维护知识总结
  2. “轻雀会议”跨越社交的“云沟通”
  3. 上班4年“武功”全废,想跳槽却被HR嘲讽,这位搞AI的硕士小哥心态崩了
  4. java 操作数据库
  5. python报错TabError: inconsistent use of tabs and spaces in indentation解决方案
  6. 分布式CAP理论:为什么CAP理论中的三个指标不能同时满足呢?
  7. 计算机程序备份,将应用程序快照备份到计算机
  8. QT TextEdit设置背景、明文加密、弹出网站、弹出自定义对话框、gif动态图片、程序启动动画、打包程序、关闭事件
  9. [Vue.js] 模块化 -- 前端模块化
  10. docker下的mysql my.ini_docker部署mysql启动失败
  11. SpringMVC_跟踪请求
  12. 从数据库中导出数据库文档(新增了索引及表的描述信息)
  13. 剑指offer——复习1:二叉树三种遍历方式的迭代与递归实现
  14. Redis面试题汇总(附答案),面试突击专用
  15. setNavigationBarTitle小程序基础性操作标题改变
  16. 全网首发:LINUX上编译ARM(AARCH)版本的OpenJDK8
  17. 【过关斩将】如何制作高水平简历-原则篇
  18. d盘信号灯超时时间已到_20下教师资格证笔试成绩什么时候公布?今年面试是什么时候?这些时间节点抢先了解!...
  19. 棋牌游戏框架解析(一)
  20. python-一个简易的购物系统

热门文章

  1. R安装包失败解决办法
  2. Windows 无法访问指定设备,路径或文件。你可能没有合适的权限访问这个项目。
  3. Scratch3.0 运动和绘图
  4. 易康ecognition软件及其插件EPS1/2
  5. spring boot设置session超时时长(自定义spring boot session超时时长)
  6. 计算机应用基础试卷分析报告,计算机应用基础 试卷分析.doc
  7. 基于Heritrix的特定主题的网络爬虫配置与实现
  8. SpringBoot的easyui实现导入和导出功能到excel
  9. Linux内核4.14版本——watchdog看门狗框架分析
  10. CSRF和SSRF详解