Android自定义控件之仿京东商城下拉刷新

来源:互联网 发布:中国网络好声音苏棱然 编辑:程序博客网 时间:2024/04/29 00:56

前面写了4篇自定义控件的博客,并且开通了一个专栏,把4篇文章添加到专栏中了,耐心等待博客专栏的徽章出现,奈何等了几周后还是没有出现,后来发现至少需要5篇文章才能出现专栏徽章,于是有了这篇仿我大京东快递小哥的下拉刷新。
直接上图先!
这里写图片描述

分析

这个下拉刷新效果分为两个部分:
step1:快递小哥和快递包裹的缩放效果,看上去就像是快递小哥跑过来一手拿过快递的样子
step2:快递小哥拿到包裹后,开启暴走模式!玩命送快递

PS:不得不赞一下京东的快递,真的很快!

step1

好了马屁拍完了,我们先来看一看第一部分的效果是怎么实现的。首先快递小哥和包裹是两张图片
这里写图片描述
这里写图片描述
我们看到快递小哥是从小变大的,快递包裹也是从小变大的,所以我们要自定义一个控件,就起名为FirstStepView.java吧

public class FirstSetpView extends View{    private Bitmap goods;    private Bitmap people;    private Bitmap peopleWithGoods;    private int measuredWidth;    private int measuredHeight;    private float mCurrentProgress;    private int mCurrentAlpha;    private Paint mPaint;    private Bitmap scaledPeople;    private Bitmap scaledGoods;    public FirstSetpView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init();    }    public FirstSetpView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public FirstSetpView(Context context) {        super(context);        init();    }    private void init(){        //包裹bitmap        goods = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_goods_0);        //快递小哥bitmap        people = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_0);        //这是后面动画中的最后一张图片,拿这张图片的作用是用它的宽高来测量        //我们这个自定义View的宽高        peopleWithGoods = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_3);        //来个画笔,我们注意到快递小哥和包裹都有一个渐变效果的,我们用        //mPaint.setAlpha来实现这个渐变的效果        mPaint = new Paint();        //首先设置为完全透明        mPaint.setAlpha(0);    }    /**     * 测量方法     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));    }    //测量宽度    private int measureWidth(int widthMeasureSpec){        int result = 0;        int size = MeasureSpec.getSize(widthMeasureSpec);        int mode = MeasureSpec.getMode(widthMeasureSpec);        if (MeasureSpec.EXACTLY == mode) {            result = size;        }else {            result = peopleWithGoods.getWidth();            if (MeasureSpec.AT_MOST == mode) {                result = Math.min(result, size);            }        }        return result;    }    //测量高度    private int measureHeight(int heightMeasureSpec){        int result = 0;        int size = MeasureSpec.getSize(heightMeasureSpec);        int mode = MeasureSpec.getMode(heightMeasureSpec);        if (MeasureSpec.EXACTLY == mode) {            result = size;        }else {            result = peopleWithGoods.getHeight();            if (MeasureSpec.AT_MOST == mode) {                result = Math.min(result, size);            }        }        return result;    }    //在这里面拿到测量后的宽和高,w就是测量后的宽,h是测量后的高    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        measuredWidth = w;        measuredHeight = h;        //根据测量后的宽高来对快递小哥做一个缩放        scaledPeople = Bitmap.createScaledBitmap(people,measuredWidth,measuredHeight,true);        //根据测量后的宽高来对快递包裹做一个缩放        scaledGoods = Bitmap.createScaledBitmap(goods, scaledPeople.getWidth()*10/27, scaledPeople.getHeight()/5, true);    }    /**     * 绘制方法     * @param canvas     */    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //由于包裹和快递小哥要分别来画,所以使用save和restore方法        //save        //画包裹        //restore        //save        //画小哥        //restore        canvas.save();        canvas.scale(mCurrentProgress, mCurrentProgress ,  measuredWidth-scaledGoods.getWidth()/2 , measuredHeight/2);        mPaint.setAlpha(mCurrentAlpha);        canvas.drawBitmap(scaledGoods, measuredWidth-scaledGoods.getWidth(), measuredHeight/2-scaledGoods.getHeight()/2, mPaint);        canvas.restore();        canvas.save();        canvas.scale(mCurrentProgress, mCurrentProgress , 0 , measuredHeight/2);        mPaint.setAlpha(mCurrentAlpha);        canvas.drawBitmap(scaledPeople, 0,0,mPaint);        canvas.restore();    }    /**     * 根据进度来对小哥和包裹进行缩放     * @param currentProgress     */    public void setCurrentProgress(float currentProgress){        this.mCurrentProgress = currentProgress;        this.mCurrentAlpha = (int) (currentProgress*255);    }}

还是老方法,我们写完这个效果,就先来测试一下吧,还是用一个SeekBar来模拟一个进度值

public class MainActivity extends Activity {    private SeekBar sb;    private FirstSetpView mFirstStepView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        sb = (SeekBar) findViewById(R.id.seekbar);        mFirstStepView = (FirstSetpView) findViewById(R.id.firstview);        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onProgressChanged(SeekBar seekBar, int progress,                    boolean fromUser) {                float currentProgress = (float)seekBar.getProgress()/(float)seekBar.getMax();                mFirstStepView.setCurrentProgress(currentProgress);                mFirstStepView.invalidate();            }        });    }}

这里写图片描述

step2

好了,第一部分已经完成,接下来我们看第二部分,第二部分就简单了,就是一个帧动画,一共有3张图片
这里写图片描述这里写图片描述这里写图片描述
我们在res/drawable/下写一个帧动画second_step_animation.xml

<?xml version="1.0" encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android"     android:oneshot="false"    >   <item android:drawable="@mipmap/app_refresh_people_1" android:duration="70"/>   <item android:drawable="@mipmap/app_refresh_people_2" android:duration="70"/>   <item android:drawable="@mipmap/app_refresh_people_3" android:duration="70"/></animation-list>

我们为了保证第一阶段和第二阶段的View的宽和高一致,我们还要再自定义一个View,不过我们不用去画什么,只需要测量一下宽和高,让他和FirstSetpView的宽高保持一致

public class SecondStepView extends View{    private Bitmap endBitmap;    public SecondStepView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init();    }    public SecondStepView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public SecondStepView(Context context) {        super(context);        init();    }    private void init(){        //拿到帧动画第三章图片,我们的FirstStepView的宽高也是根据这张图片来测量的,所以我们就能        //保证两个View的宽高一致了        endBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_3);    }    /**     * 只需要测量方法     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));    }    private int measureWidth(int widthMeasureSpec){        int result = 0;        int size = MeasureSpec.getSize(widthMeasureSpec);        int mode = MeasureSpec.getMode(widthMeasureSpec);        if (MeasureSpec.EXACTLY == mode) {            result = size;        }else {            result = endBitmap.getWidth();            if (MeasureSpec.AT_MOST == mode) {                result = Math.min(size, result);            }        }        return result;    }    private int measureHeight(int heightMeasureSpec){        int result = 0;        int size = MeasureSpec.getSize(heightMeasureSpec);        int mode = MeasureSpec.getMode(heightMeasureSpec);        if (MeasureSpec.EXACTLY == mode) {            result = size;        }else {            result = endBitmap.getHeight();            if (MeasureSpec.AT_MOST == mode) {                result = Math.min(size, result);            }        }        return result;    }}

好了,接下来再复习一下怎么执行帧动画

secondStepView = (SecondStepView) headerView.findViewById(R.id.second_step_view);        secondStepView.setBackgroundResource(R.drawable.second_step_animation);        secondAnimation = (AnimationDrawable) secondStepView.getBackground();

secondAnimation是一个AnimationDrawable对象,我们可以调用他的start方法开始帧动画,调用stop结束帧动画

secondAnimation.start();secondAnimation.stop();

下拉刷新的实现

这一部分我在Android自定义控件之仿美团下拉刷新 中已经详细分析过了,所以这里就不再赘述了。

完整代码

欢迎大家上我的GitHub上下载源码

12 0