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

【高仿微信系列】03、微信录制小视频

2015-07-03 11:06 531 查看
本文为个人原创,欢迎转载,但请务必在明显位置注明出处!

GitHub地址:https://github.com/motianhuo/wechat

微信从6.0版本开始推出小视频功能,随着4G网络的出现,视频将会是一个趋势,他能表达出文字所不能表现的东西,增加了微信的黏性。还记得微信小视频这个功能一推出,如同病毒一样席卷朋友圈。



作为爱美的我们,怎么能把我们的窘态暴露给朋友圈的小伙伴呢,必须正能量!美好的!必须美化! So,录制小视频后,加各种滤镜,炫酷MV主题,妈妈再也不担心我的猪窝了…

“小视频”对于微信如此之重要。那么,如何实现呢?

先看下咱们的实现效果:



PS:gif 图片比较大,如果等不及的童鞋,可以点击这里查看视频

OK,先看下,消息列表页面的 下滑显示眼睛动画效果的实现方式:

自定义ListView:PullDownListView.java

PullDownListView来自guojunyi的分享, GitHub项目地址:点击这里

package com.example.wechat01.widght;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import android.widget.RelativeLayout;

import com.example.wechat01.R;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;

public class PullDownListView extends RelativeLayout implements
OnScrollListener {
static int MAX_PULL_TOP_HEIGHT;
static int MAX_PULL_BOTTOM_HEIGHT;

static int REFRESHING_TOP_HEIGHT;
static int REFRESHING_BOTTOM_HEIGHT;

private boolean isTop;
private boolean isBottom;
private boolean isRefreshing;
private boolean isAnimation;

RelativeLayout layoutHeader;
RelativeLayout layoutFooter;

private int mCurrentY = 0;
boolean pullTag = false;
OnScrollListener mOnScrollListener;
OnPullHeightChangeListener mOnPullHeightChangeListener;

public void setOnPullHeightChangeListener(
OnPullHeightChangeListener listener) {
this.mOnPullHeightChangeListener = listener;
}

public void setOnScrollListener(OnScrollListener listener) {
mOnScrollListener = listener;
}

public PullDownListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

public boolean isRefreshing() {
return this.isRefreshing;
}

private ListView mListView = new ListView(getContext()) {

int lastY = 0;

@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isAnimation || isRefreshing) {
return super.onTouchEvent(ev);
}
RelativeLayout parent = (RelativeLayout) mListView.getParent();

int currentY = (int) ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE: {
boolean isToBottom = currentY - lastY >= 0 ? true : false;

int step = Math.abs(currentY - lastY);
lastY = currentY;

if (isTop && mListView.getTop() >= 0) {

if (isToBottom && mListView.getTop() <= MAX_PULL_TOP_HEIGHT) {
MotionEvent event = MotionEvent.obtain(ev);
ev.setAction(MotionEvent.ACTION_UP);
super.onTouchEvent(ev);
pullTag = true;

if (mListView.getTop() > layoutHeader.getHeight()) {
step = step / 2;
}
if ((mListView.getTop() + step) > MAX_PULL_TOP_HEIGHT) {
mCurrentY = MAX_PULL_TOP_HEIGHT;
scrollTopTo(mCurrentY);
} else {
mCurrentY += step;
scrollTopTo(mCurrentY);
}
} else if (!isToBottom && mListView.getTop() > 0) {
MotionEvent event = MotionEvent.obtain(ev);
ev.setAction(MotionEvent.ACTION_UP);
super.onTouchEvent(ev);
if ((mListView.getTop() - step) < 0) {
mCurrentY = 0;
scrollTopTo(mCurrentY);
} else {
mCurrentY -= step;
scrollTopTo(mCurrentY);
}
} else if (!isToBottom && mListView.getTop() == 0) {
if (!pullTag) {
return super.onTouchEvent(ev);
}

}

return true;
} else if (isBottom
&& mListView.getBottom() <= parent.getHeight()) {
if (!isToBottom
&& (parent.getHeight() - mListView.getBottom()) <= MAX_PULL_BOTTOM_HEIGHT) {
MotionEvent event = MotionEvent.obtain(ev);
ev.setAction(MotionEvent.ACTION_UP);
super.onTouchEvent(ev);
pullTag = true;
if (parent.getHeight() - mListView.getBottom() > layoutFooter
.getHeight()) {
step = step / 2;
}

if ((mListView.getBottom() - step) < (parent
.getHeight() - MAX_PULL_BOTTOM_HEIGHT)) {
mCurrentY = -MAX_PULL_BOTTOM_HEIGHT;
scrollBottomTo(mCurrentY);
} else {
mCurrentY -= step;
scrollBottomTo(mCurrentY);
}
} else if (isToBottom
&& (mListView.getBottom() < parent.getHeight())) {
if ((mListView.getBottom() + step) > parent.getHeight()) {
mCurrentY = 0;
scrollBottomTo(mCurrentY);
} else {
mCurrentY += step;
scrollBottomTo(mCurrentY);
}
} else if (isToBottom
&& mListView.getBottom() == parent.getHeight()) {
if (!pullTag) {
return super.onTouchEvent(ev);
}
}
return true;
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
pullTag = false;

if (mListView.getTop() > 0) {
if (mListView.getTop() > REFRESHING_TOP_HEIGHT) {
animateTopTo(layoutHeader.getMeasuredHeight());
isRefreshing = true;
if (null != mOnPullHeightChangeListener) {
mOnPullHeightChangeListener.onRefreshing(true);
}
} else {
animateTopTo(0);
}

} else if (mListView.getBottom() < parent.getHeight()) {
if ((parent.getHeight() - mListView.getBottom()) > REFRESHING_BOTTOM_HEIGHT) {
animateBottomTo(-layoutFooter.getMeasuredHeight());
isRefreshing = true;
if (null != mOnPullHeightChangeListener) {
mOnPullHeightChangeListener.onRefreshing(false);
}
} else {
animateBottomTo(0);
}
}

}

return super.onTouchEvent(ev);
}

};

public void scrollBottomTo(int y) {
mListView.layout(mListView.getLeft(), y, mListView.getRight(),
this.getMeasuredHeight() + y);
if (null != mOnPullHeightChangeListener) {
mOnPullHeightChangeListener.onBottomHeightChange(
layoutHeader.getHeight(), -y);
}
}

public void animateBottomTo(final int y) {
ValueAnimator animator = ValueAnimator.ofInt(mListView.getBottom()
- this.getMeasuredHeight(), y);
animator.setDuration(300);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
int frameValue = (Integer) animation.getAnimatedValue();
mCurrentY = frameValue;
scrollBottomTo(frameValue);
if (frameValue == y) {
isAnimation = false;
}
}
});
isAnimation = true;
animator.start();
}

public void scrollTopTo(int y) {
mListView.layout(mListView.getLeft(), y, mListView.getRight(),
this.getMeasuredHeight() + y);
if (null != mOnPullHeightChangeListener) {
mOnPullHeightChangeListener.onTopHeightChange(
layoutHeader.getHeight(), y);
}
}

public void animateTopTo(final int y) {
ValueAnimator animator = ValueAnimator.ofInt(mListView.getTop(), y);
animator.setDuration(300);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
int frameValue = (Integer) animation.getAnimatedValue();
mCurrentY = frameValue;
scrollTopTo(frameValue);
if (frameValue == y) {
isAnimation = false;
}
}
});
isAnimation = true;
animator.start();
}

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

REFRESHING_TOP_HEIGHT = layoutHeader.getMeasuredHeight();
REFRESHING_BOTTOM_HEIGHT = layoutFooter.getMeasuredHeight();

MAX_PULL_TOP_HEIGHT = this.getMeasuredHeight();
MAX_PULL_BOTTOM_HEIGHT = this.getMeasuredHeight();
}

@Override
public void onFinishInflate() {

mListView.setBackgroundColor(0xffffffff);
mListView.setCacheColorHint(Color.TRANSPARENT);
mListView.setVerticalScrollBarEnabled(false);
mListView.setLayoutParams(new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mListView.setOnScrollListener(this);
mListView.setDividerHeight(0);
this.addView(mListView);

layoutHeader = (RelativeLayout) this.findViewById(R.id.layoutHeader);
layoutFooter = (RelativeLayout) this.findViewById(R.id.layoutFooter);
super.onFinishInflate();
}

public ListView getListView() {
return this.mListView;
}
public void pullUp() {
isRefreshing = false;
if (mListView.getTop() > 0) {
animateTopTo(0);
} else if (mListView.getBottom() < this.getHeight()) {
animateBottomTo(0);
}
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
if (null != mOnScrollListener) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
if (mListView.getCount() > 0) {
if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
View lastItem = (View) mListView
.getChildAt(visibleItemCount - 1);
if (null != lastItem) {

if (lastItem.getBottom() == mListView.getHeight()) {
Log.e("my", lastItem.getBottom() + "");
isBottom = true;
} else {
isBottom = false;
}
}
} else {
isBottom = false;
}
} else {
isBottom = false;
}
if (mListView.getCount() > 0) {
if (firstVisibleItem == 0) {
View firstItem = mListView.getChildAt(0);
if (null != firstItem) {
if (firstItem.getTop() == 0) {
isTop = true;
} else {
isTop = false;
}
}
} else {
isTop = false;
}
} else {
isTop = true;
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if (null != mOnScrollListener) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
// listener call back
public interface OnPullHeightChangeListener {
public void onTopHeightChange(int headerHeight, int pullHeight);
public void onBottomHeightChange(int footerHeight, int pullHeight);
public void onRefreshing(boolean isTop);
}
}


再看,小眼睛的实现方式:EyeView.java

package com.example.wechat01.widght;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;

import com.example.wechat01.R;

public class EyeView extends ImageView {
Paint mPaint;
float progress;
boolean isAnimate;
int rotateProgress;
Handler mHandler = new Handler();

public EyeView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
mPaint = new Paint();
mPaint.setAntiAlias(true);
rotateProgress = 0;
progress = 0.0f;
}

@Override
public void onDraw(Canvas canvas) {
int minWidth = (int) (this.getWidth() * progress);
int minHeight = (int) (this.getHeight() * progress);
if (minWidth > 1 && minHeight > 1) {
Bitmap bitmap = getBitmap();
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap.recycle();

}
}

public Bitmap getBitmap() {
Bitmap origin1 = null;
Bitmap origin2 = null;
if (progress >= 1.0) {
BitmapDrawable drawable1 = (BitmapDrawable) this.getResources()
.getDrawable(R.drawable.eye_light1);
origin1 = drawable1.getBitmap();
BitmapDrawable drawable2 = (BitmapDrawable) this.getResources()
.getDrawable(R.drawable.eye_light2);
origin2 = drawable2.getBitmap();
} else {
BitmapDrawable drawable1 = (BitmapDrawable) this.getResources()
.getDrawable(R.drawable.eye_gray_1);
origin1 = drawable1.getBitmap();
BitmapDrawable drawable2 = (BitmapDrawable) this.getResources()
.getDrawable(R.drawable.eye_gray_2);
origin2 = drawable2.getBitmap();
}
Paint paint = new Paint();
paint.setAntiAlias(true);
float scale = (float) origin1.getWidth() / (float) getWidth();
int maxWidth = (int) (origin1.getWidth() / scale);
int maxHeight = (int) (origin1.getHeight() / scale);

int maskSize = 1;

if (progress > 0.3f) {
maskSize = (int) (maxHeight * (progress - 0.3) / 0.7);
}

Bitmap temp1 = Bitmap.createScaledBitmap(origin1, (int) (maxWidth),
(int) (maxHeight), true);

Canvas canvas = new Canvas();
Bitmap mask = Bitmap.createBitmap(temp1.getWidth(), temp1.getWidth(),
Config.ARGB_8888);
canvas.setBitmap(mask);
canvas.drawCircle(mask.getWidth() / 2, mask.getHeight() / 2, maskSize,
mPaint);

Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Config.ARGB_8888);
canvas.setBitmap(bitmap);
canvas.drawBitmap(temp1, (getWidth() - temp1.getWidth()) / 2,
(getHeight() - temp1.getHeight()) / 2, paint);

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(mask, (getWidth() - mask.getWidth()) / 2,
(getHeight() - mask.getHeight()) / 2, paint);
paint.setXfermode(null);

float scaleProgress = progress / 0.3f;
if (scaleProgress > 1.0f) {
scaleProgress = 1.0f;
}
Bitmap temp2 = Bitmap.createScaledBitmap(origin2,
(int) (maxWidth * scaleProgress),
(int) (maxHeight * scaleProgress), true);
Matrix matrix = new Matrix();
matrix.postRotate(rotateProgress);
temp2 = Bitmap.createBitmap(temp2, 0, 0, temp2.getWidth(),
temp2.getHeight(), matrix, false);
canvas.drawBitmap(temp2, (getWidth() - temp2.getWidth()) / 2,
(getHeight() - temp2.getHeight()) / 2, paint);

temp1.recycle();
temp2.recycle();
mask.recycle();
return bitmap;
}

public void setProgress(float progress) {
this.progress = progress;
this.invalidate();
}

public void startAnimate() {
if (!isAnimate) {
isAnimate = true;
// mHandler.post(mRunnable);
}
}

public void stopAnimate() {
isAnimate = false;
// mHandler.removeCallbacks(mRunnable);
rotateProgress = 0;
}

public Runnable mRunnable = new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
rotateProgress += 10;
if (rotateProgress > 360) {
rotateProgress = 0;
}

if (isAnimate) {
mHandler.postDelayed(this, 10);
}
EyeView.this.invalidate();
}
};
}


再看下消息列表界面,监听Listview下拉的高度,来绘制EyeView,并打开视频录制界面。

package com.example.wechat01;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

import com.example.wechat01.adpter.NewMsgAdpter;
import com.example.wechat01.widght.EyeView;
import com.example.wechat01.widght.PullDownListView;
import com.example.wechat01.widght.PullDownListView.OnPullHeightChangeListener;
import com.yixia.camera.demo.ui.record.MediaRecorderActivity;

/**
* 消息界面
*
* @author allenjuns@yahoo.com
*
*/
public class Fragment_Msg extends Fragment {
private Activity ctx;
private View layout;
private ListView listview;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (layout == null) {
ctx = this.getActivity();
layout = ctx.getLayoutInflater().inflate(R.layout.framen_msg, null);
initView();
initPullDownView();
} else {
ViewGroup parent = (ViewGroup) layout.getParent();
if (parent != null) {
parent.removeView(layout);
}
}
return layout;
}

private void initView() {
// TODO 实现本页面的布局
}

private void initPullDownView() {
final PullDownListView pullDownListView = (PullDownListView) layout
.findViewById(R.id.pullDownListView);
final EyeView eyeView = (EyeView) layout.findViewById(R.id.eyeView);

pullDownListView.getListView().setAdapter(
new NewMsgAdpter(getActivity()));

pullDownListView
.setOnPullHeightChangeListener(new OnPullHeightChangeListener() {

@Override
public void onTopHeightChange(int headerHeight,
int pullHeight) {
// TODO Auto-generated method stub
float progress = (float) pullHeight
/ (float) headerHeight;

if (progress < 0.5) {
progress = 0.0f;
} else {
progress = (progress - 0.5f) / 0.5f;
}

if (progress > 1.0f) {
progress = 1.0f;
}

if (!pullDownListView.isRefreshing()) {
eyeView.setProgress(progress);
}
}

@Override
public void onBottomHeightChange(int footerHeight,
int pullHeight) {
// TODO Auto-generated method stub
float progress = (float) pullHeight
/ (float) footerHeight;

if (progress < 0.5) {
progress = 0.0f;
} else {
progress = (progress - 0.5f) / 0.5f;
}
if (progress > 1.0f) {
progress = 1.0f;
}
if (!pullDownListView.isRefreshing()) {
}
}

@Override
public void onRefreshing(final boolean isTop) {
// TODO Auto-generated method stub
if (isTop) {
eyeView.startAnimate();
} else {
// progressView.startAnimate();
}
// 打开视频录制页面
Intent intent = new Intent(ctx,
MediaRecorderActivity.class);
ctx.startActivity(intent); ctx.overridePendingTransition(R.anim.push_up_in,
R.anim.push_up_out);
pullDownListView.pullUp();
}

});

pullDownListView.getListView().setOnItemClickListener(
new OnItemClickListener() {

@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
}
});
}
}


这个Demo比较复杂,具体实现方式请大家去Github研究一下代码吧~

另外还有视频录制这块的功能,有些复杂,这块是用的秒拍团队提供的视频录制SDK,感兴趣的可以去官网膜拜~

官网地址:





OK,项目的完整代码可以去 Github (点击这里) 下载。

本系列文章会教你一步步打造自己的高仿微信APP,尽请关注本博客!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: