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

慕课网app下拉刷新图标填充效果的实现

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




而我的也实现了一个这个图标填充的简单版。如下图





整个实现使用图形的混合模式+贝塞尔曲线,贝塞尔曲线的绘制参考自爱哥的博客 贝塞尔曲线内容

资源文件就只有下面这个图标,该图标提取自慕课网app,然后对内部的火焰进行透明处理过



既然是自定义View,那就要继承View,实现onDraw,onMeasure等方法,这里将控件的宽度高度直接设置为图片的宽度和高度加上内边距,并且去实现相应的逻辑去判断MeasureSpec的模式是哪个从而进行处理。

先贴代码

[java] view
plaincopy

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();



// 以上画贝塞尔曲线代码参考自爱哥博客

// /article/1328790.html



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);



}



}

控件的使用

[html] view
plaincopy

<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即可。

整个实现过程还是相对简单的,基本上注释都讲的很清楚了,这里也不再重复了,文章中涉及到的两个知识点(图形的混合模式和贝塞尔曲线)的相关内容参考下面两篇文章

图形混合模式 /article/1328793.html

贝塞尔曲线 /article/1328790.html

都是爱哥的文章,个人觉得写得很细。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐