您的位置:首页 > 移动开发 > 微信开发

微信Android客户端中表情雨效果的实现

2014-02-21 17:54 501 查看
微信android客户端中的表情雨效果在聊天中生动活泼,具体会出现特殊效果的词有恭喜发财、年年有余、想你了、生日快乐、么么哒等等,随着节日来临会有更新词库及图片内容出现。具体效果如下图:



现就其实现过程进行一详细分析。

这个功能可以分为两步走:

第一步是取用服务器词库及表情图片,以及对聊天页面发送的文字的监听,这一点并没有什么要特别注意的,本文略过。

第二步也就是实现表情雨的关键,即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轴竖直平移,均可在此基础上加以改进,给聊天页面增添生动的效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息