微信Android客户端中表情雨效果的实现
2014-02-21 17:54
501 查看
微信android客户端中的表情雨效果在聊天中生动活泼,具体会出现特殊效果的词有恭喜发财、年年有余、想你了、生日快乐、么么哒等等,随着节日来临会有更新词库及图片内容出现。具体效果如下图:
![](https://img-blog.csdn.net/20140221175224484?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1aGFvYm9v/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
现就其实现过程进行一详细分析。
这个功能可以分为两步走:
第一步是取用服务器词库及表情图片,以及对聊天页面发送的文字的监听,这一点并没有什么要特别注意的,本文略过。
第二步也就是实现表情雨的关键,即UI的展示效果及其中涉及的一系列问题。下面做一简要分析及实现。
从微信表情雨的效果来看,是一张表情图icon以随机位置、随机速度、随机初始时间(皆在指定的范围内)从屏幕顶端向屏幕底端的平移运动,认清了这一点,实现起来逻辑就比较清晰了。按照这一目标,可以自定义一个EmotionsView如下,这里假设使用本地图片drawable/test_icon.png,并设置有20个表情。
并自定义BackgroundView如下:
在聊天页面中,编写表情雨动画函数showEmotionsView(),通过一个Handler实现表情动画。节选代码如下:
以该思路实现的表情雨动画在各版本Android中均测试通过,由于采用了读取手机屏幕宽、高,利用随机数计算的办法,妥善解决了屏幕适配的问题,如将问题引申,如表情随着运动有大小闪烁变化,或运动轨迹不仅是y轴竖直平移,均可在此基础上加以改进,给聊天页面增添生动的效果。
现就其实现过程进行一详细分析。
这个功能可以分为两步走:
第一步是取用服务器词库及表情图片,以及对聊天页面发送的文字的监听,这一点并没有什么要特别注意的,本文略过。
第二步也就是实现表情雨的关键,即UI的展示效果及其中涉及的一系列问题。下面做一简要分析及实现。
从微信表情雨的效果来看,是一张表情图icon以随机位置、随机速度、随机初始时间(皆在指定的范围内)从屏幕顶端向屏幕底端的平移运动,认清了这一点,实现起来逻辑就比较清晰了。按照这一目标,可以自定义一个EmotionsView如下,这里假设使用本地图片drawable/test_icon.png,并设置有20个表情。
public class EmotionsView extends View { // 表情图片 Bitmap bitmap_emotion = null; private boolean isEnd = true; public boolean isEnd() { return isEnd; } public void setEnd(boolean isEnd) { this.isEnd = isEnd; } private final Paint mPaint = new Paint(); private static final Random RNG = new Random(); private Coordinate[] emotions = new Coordinate[20]; int view_height = 0; int view_width = 0; private int[] emotionX = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private int[] emotionY = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private int[] emotionZ = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; public EmotionsView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public EmotionsView(Context context, AttributeSet attrs) { super(context, attrs); } public void LoadEmotionImage() { Resources r = this.getContext().getResources(); bitmap_emotion = ((BitmapDrawable) r.getDrawable(R.drawable.test_icon)).getBitmap(); } public void LoadEmotionImage(int intDrawable) { Resources r = this.getContext().getResources(); bitmap_emotion = ((BitmapDrawable) r.getDrawable(intDrawable)).getBitmap(); } public void setView(int height, int width) { view_height = height - 100; view_width = width - 50; } public void clearAllEmotions() { emotionX = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; emotionY = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; emotionZ = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; } /** * 随机生成位置 */ public void addRandomEmotion() { calculateNextCoordinate(); for (int i = 0; i < 20; i++) { if (emotions[i] == null) { emotions[i] = new Coordinate(emotionX[i], emotionY[i]); } else { emotions[i].setXY(emotionX[i], emotionY[i]); } if (emotionY[i] >= view_height) { emotions[i] = null; } } } private void calculateNextCoordinate() { for (int i = 0; i < 20; i++) { if (emotionX[i] == 0) { emotionY[i] = RNG.nextInt(1000) - 1000; emotionX[i] = RNG.nextInt(view_width - 1) + 1; emotionZ[i] = RNG.nextInt(20) + 10; } else { emotionY[i] += emotionZ[i]; } } } private class Coordinate { public int x; public int y; public Coordinate(int newX, int newY) { x = newX; y = newY; } public boolean equals(Coordinate other) { if (x == other.x && y == other.y) { return true; } return false; } @Override public String toString() { return "Coordinate: [" + x + "," + y + "]"; } public void setXY(int x, int y) { this.x = x; this.y = y; } } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); if (isEnd) { return; } boolean temp = true; for (int x = 0; x < 20; x += 1) { if (emotions[x] != null && bitmap_emotion != null && !bitmap_emotion.isRecycled()) { canvas.drawBitmap(bitmap_emotion, ((float) emotions[x].x), ((float) emotions[x].y), mPaint); temp = false; } } if (temp) { isEnd = true; } }}
并自定义BackgroundView如下:
public class BackgroundView extends View { private static final String TAG = "BackgroundView"; // 屏幕的高度和宽度 int view_height = 0; int view_width = 0; Bitmap bmp = null; /** * 构造器 */ public BackgroundView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public BackgroundView(Context context, AttributeSet attrs) { super(context, attrs); } /** * 设置当前窗体的实际高度和宽度 */ public void SetView(int height, int width) { view_height = height; view_width = width; } public void SetBitmap(Bitmap bitmap) { bmp = bitmap; } @SuppressLint("DrawAllocation") @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // MyLog.d(TAG, view_width + " " + view_height); if (bmp == null) { bmp = BitmapFactory.decodeResource(getResources(), R.drawable.wish_background); } Rect src = new Rect();// 图片 Rect dst = new Rect();// 屏幕位置及尺寸 // src 这个是表示绘画图片的大小 src.left = 0; // 0,0 src.top = 0; src.right = bmp.getWidth();// mBitDestTop.getWidth();,这个是桌面图的宽度, src.bottom = bmp.getHeight();// mBitDestTop.getHeight()/2;// // 这个是桌面图的高度的一半 // 下面的 dst 是表示 绘画这个图片的位置 dst.left = 0; // miDTX,//这个是可以改变的,也就是绘图的起点X位置 dst.top = 0; // mBitQQ.getHeight();//这个是QQ图片的高度。 也就相当于 桌面图片绘画起点的Y坐标 dst.right = view_width; // miDTX + mBitDestTop.getWidth();// // 表示需绘画的图片的右上角 dst.bottom = view_height; // mBitQQ.getHeight() + // mBitDestTop.getHeight();//表示需绘画的图片的右下角 canvas.drawBitmap(bmp, src, dst, null);// 这个方法 第一个参数是图片原来的大小,第二个参数是 // 绘画该图片需显示多少。也就是说你想绘画该图片的某一些地方,而不是全部图片,第三个参数表示该图片绘画的位置 src = null; dst = null; }
在聊天页面中,编写表情雨动画函数showEmotionsView(),通过一个Handler实现表情动画。节选代码如下:
private RefreshHandler mRedrawHandler = new RefreshHandler(); class RefreshHandler extends Handler { @Override public void handleMessage(Message msg) { if (ev == null || ev.isEnd()) { return; } ev.addRandomEmotion(); ev.invalidate(); sleep(50); } public void sleep(long delayMillis) { this.removeMessages(0); sendMessageDelayed(obtainMessage(0), delayMillis); } }; public void update() { ev.setEnd(false); ev.clearAllEmotions(); ev.addRandomEmotion(); mRedrawHandler.removeMessages(0); mRedrawHandler.sleep(200); } private void showEmotionsView() { // 获得表情雨视图,加载icon到内存(在布局文件中置入自定义EmotionsView) ev = (EmotionsView) findViewById(R.id.emotion_view); // 此处可实现表情图片的更替,具体判断来自发送的文本内容 int intDrawable = R.drawable.test_icon2; ev.LoadEmotionImage(intDrawable); BV.invalidate(); ev.setVisibility(View.VISIBLE); // 获取当前屏幕的高和宽 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); ev.setView(dm.heightPixels, dm.widthPixels); update(); }
以该思路实现的表情雨动画在各版本Android中均测试通过,由于采用了读取手机屏幕宽、高,利用随机数计算的办法,妥善解决了屏幕适配的问题,如将问题引申,如表情随着运动有大小闪烁变化,或运动轨迹不仅是y轴竖直平移,均可在此基础上加以改进,给聊天页面增添生动的效果。
相关文章推荐
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- 解決Linux下Android开发真机调试设备不被识别问题
- [Android]在代码里运行另一个程序的方法
- 我是运营,我没有假期
- 如何做到日消息量100万的微信公众号?
- 论微信取消推送功能的可能性(原创)
- 微信的成功,靠的是QQ导流吗?
- [软件咨询]WPS2012正式版已发布 金山Office移动版4.0发布
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- android USB如何修改VID具体实现
- Android增量升级的方法和原理详细介绍
- Android Mouse实现过程详细笔记
- 深入Android Browser配置管理的详解
- PHP限制页面只能在微信自带浏览器访问的代码
- Android Mms之:深入理解对话列表管理
- 微信小程序去哪里找 小程序到底如何使用(附小程序名单)