【高仿微信系列】03、微信录制小视频
来源:互联网 发布:js 只能输入两位小数 编辑:程序博客网 时间:2024/06/05 05:21
本文为个人原创,欢迎转载,但请务必在明显位置注明出处!
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,尽请关注本博客!
1 1
- 【高仿微信系列】微信录制小视频
- 【高仿微信系列】03、微信录制小视频
- Android实现微信录制小视频的计时动画
- 微信视频录制按钮
- Android 仿秒拍,微信录制短视频
- 微信视频的录制按钮
- 仿微信录制小视频
- 微信朋友圈录制视频循环播放源码
- 微信朋友圈录制视频循环播放源码
- 微信朋友圈录制视频循环播放源码
- ffmpeg仿微信录制小视频
- Android之---高仿微信录制小视频(拍摄和查看)
- android 录制视频(环信)
- 微信公开课系列视频 观后感
- 自动保存微信的小视频
- 如何导出微信的小视频
- 微信转发朋友圈小视频
- 下载微信中的小视频
- js判断是否为JSON对象
- 互联网公司年终奖哪家强?都是土豪啊
- 代码才是公司资产
- 升级Centos内核至3.X
- JQueryAPI网址
- 【高仿微信系列】03、微信录制小视频
- 共享内存
- 如何使用NSOperations和NSOperationQueues(二)
- 查看图片
- C#打开、保存图像
- 索尼单反相机跑焦的调整 A700 7D 5D
- 九张 Gif 图回顾 Web 设计的 25 年历史
- 演讲与交流
- iOS开发--底部按钮和应用图标显示未读消息