android下拉刷新精彩动画

之前看到一种下拉刷新的效果,与以往的下拉效果都不一样,大多数下拉刷新都是一个圆形进度条在旋转,而这个下拉刷新则是一个不断填充的效果。本以为这是个自定义View,后来反编译慕课网的app后提取资源的时候看到好多的图片,那大概慕课网app内部的实现应该是帧动画达到这种效果。而当我看到这种效果的时候,由于前段时间在学自定义控件,所以本能的反应则是自定义的。首先我们看下慕课网的效果。如下图

package cn.edu.zafu.view;  

import android.content.Context;  
import android.graphics.Bitmap;  
import android.graphics.Bitmap.Config;  
import android.graphics.BitmapFactory;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.Path;  
import android.graphics.PorterDuff;  
import android.graphics.PorterDuffXfermode;  
import android.util.AttributeSet;  
import android.view.View;  

/** * @author lizhangqu * * 2015-3-5 */  
public class CustomView extends View {  
    private PorterDuffXfermode porterDuffXfermode;// Xfermode 
    private Paint paint;// 画笔 
    private Bitmap bitmap;// 源图片 
    private int width, height;// 控件宽高 
    private Path path;// 画贝塞尔曲线需要用到 
    private Canvas mCanvas;// 在该画布上绘制目标图片 
    private Bitmap bg;// 目标图片 

    private float controlX, controlY;// 贝塞尔曲线控制点,使用三阶贝塞尔曲线曲线,需要两个控制点,两个控制点都在该变量基础上生成 
    private float waveY;// 上升的高度 

    private boolean isIncrease;// 用于控制控制点水平移动 

    private boolean isReflesh = true;// 是否刷新并产生填充效果,默认为true 

    /** * @return 是否刷新 */  
    public boolean isReflesh() {  
        return isReflesh;  
    }  

    /** * 提供接口设置刷新 * * @param isReflesh */  
    public void setReflesh(boolean isReflesh) {  
        this.isReflesh = isReflesh;  
        //重绘 
        postInvalidate();  
    }  

    /** * @param context */  
    public CustomView(Context context) {  
        this(context, null);  
    }  

    /** * @param context * @param attrs */  
    public CustomView(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  

    /** * @param context * @param attrs * @param defStyle */  
    public CustomView(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
        init();  
    }  

    /** * 初始化变量 */  
    private void init() {  
        // 初始化画笔 
        paint = new Paint();  
        paint.setAntiAlias(true);  
        paint.setDither(true);  
        paint.setStyle(Paint.Style.FILL);  
        paint.setColor(Color.parseColor("#ffc9394a"));  
        // 获得资源文件 
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mooc);  
        // 设置宽高为图片的宽高 
        width = bitmap.getWidth();  
        height = bitmap.getHeight();  

        // 初始状态值 
        waveY = 7 / 8F * height;  
        controlY = 17 / 16F * height;  

        // 初始化Xfermode 
        porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);  
        // 初始化path 
        path = new Path();  
        // 初始化画布 
        mCanvas = new Canvas();  
        // 创建bitmap 
        bg = Bitmap.createBitmap(width, height, Config.ARGB_8888);  
        // 将新建的bitmap注入画布 
        mCanvas.setBitmap(bg);  

    }  

    @Override  
    protected void onDraw(Canvas canvas) {  
        // 画目标图,存在bg上 
        drawTargetBitmap();  
        // 将目标图绘制在当前画布上,起点为左边距,上边距的交点 
        canvas.drawBitmap(bg, getPaddingLeft(), getPaddingTop(), null);  
        if (isReflesh) {  
            // 重绘,使用boolean变量isReflesh进行控制,并对外提供访问的接口,默认为true且刷新 
            invalidate();  
        }  
    }  

    private void drawTargetBitmap() {  
        // 重置path 
        path.reset();  
        // 擦除像素 
        bg.eraseColor(Color.parseColor("#00ffffff"));  

        // 当控制点的x坐标大于或等于终点x坐标时更改标识值 
        if (controlX >= width + 1 / 2 * width) {  
            isIncrease = false;  
        }  
        // 当控制点的x坐标小于或等于起点x坐标时更改标识值 
        else if (controlX <= -1 / 2 * width) {  
            isIncrease = true;  
        }  

        // 根据标识值判断当前的控制点x坐标是该加还是减 
        controlX = isIncrease ? controlX + 10 : controlX - 10;  
        if (controlY >= 0) {  
            // 波浪上移 
            controlY -= 1;  
            waveY -= 1;  
        } else {  
            // 超出则重置位置 
            waveY = 7 / 8F * height;  
            controlY = 17 / 16F * height;  
        }  

        // 贝塞尔曲线的生成 
        path.moveTo(0, waveY);  
        // 两个控制点通过controlX,controlY生成 
        path.cubicTo(controlX / 2, waveY - (controlY - waveY),  
                (controlX + width) / 2, controlY, width, waveY);  
        // 与下下边界闭合 
        path.lineTo(width, height);  
        path.lineTo(0, height);  
        // 进行闭合 
        path.close();  

        // 以上画贝塞尔曲线代码参考自爱哥博客 
        // http://blog.csdn.net/aigestudio/article/details/41960507 

        mCanvas.drawBitmap(bitmap, 0, 0, paint);// 画慕课网logo 
        paint.setXfermode(porterDuffXfermode);// 设置Xfermode 
        mCanvas.drawPath(path, paint);// 画三阶贝塞尔曲线 
        paint.setXfermode(null);// 重置Xfermode 
    }  

    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        // 获得宽高测量模式和大小 
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
        // 保存测量结果 
        int width, height;  

        if (widthMode == MeasureSpec.EXACTLY) {  
            // 宽度 
            width = widthSize;  
        } else {  
            // 宽度加左右内边距 
            width = this.width + getPaddingLeft() + getPaddingRight();  
            ;  
            if (widthMode == MeasureSpec.AT_MOST) {  
                // 取小的那个 
                width = Math.min(width, widthSize);  
            }  

        }  

        if (heightMode == MeasureSpec.EXACTLY) {  
            // 高度 
            height = heightSize;  
        } else {  
            // 高度加左右内边距 
            height = this.height + getPaddingTop() + getPaddingBottom();  
            ;  
            if (heightMode == MeasureSpec.AT_MOST) {  
                // 取小的那个 
                height = Math.min(height, heightSize);  
            }  

        }  
        // 设置高度宽度为logo宽度和高度,实际开发中应该判断MeasureSpec的模式,进行对应的逻辑处理,这里做了简单的判断测量 
        setMeasuredDimension(width, height);  

    }  

}  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/ll"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
    <cn.edu.zafu.view.CustomView   
        android:id="@+id/cv"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:padding="20dp"  
        android:layout_centerInParent="true"  
        android:background="#0000ff"  
        />  
</RelativeLayout>  

如果要停止其不断填充的效果,通过函数setReflesh设置isReflesh变量为false即可。
整个实现过程还是相对简单的,基本上注释都讲的很清楚了,这里也不再重复了,文章中涉及到的两个知识点(图形的混合模式和贝塞尔曲线)的相关内容参考下面两篇文章
图形混合模式 http://blog.csdn.net/aigestudio/article/details/41316141
贝塞尔曲线 http://blog.csdn.net/aigestudio/article/details/41960507
都是爱哥的文章,个人觉得写得很细。

你可能感兴趣的:(anmication)