【高仿微信系列】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
原创粉丝点击