android自定义水波纹,android 自定义控件之水波纹loading的实现

恩恩,需求还是没有下来,整了一天多,再次整出一个loading框,看来我对loading框是情有独钟,好了,不多bb,先上图:

3a6eeef40f75208bc55214d0dd42fb25.png

恩,就是这么个东东,较之前两个,有了点技术含量,但是其实也不是很难,之所以做了一天多,原因是又特么踩了一个坑,坑了我一个下午的时间,伤不起,至于是什么坑,下面再说;

好了,完成这个之前必要的知识储备,二阶贝塞尔曲线,也去网上看了一些文章,还有说要三阶贝塞尔曲线知识的,其实我觉得没必要,二阶就够了,下面附上一个链接,看完就知道贝塞尔曲线到底是个

知道了贝塞尔曲线,下面就可以开始说道说道了。

要完成这个loading图案,我认为难点主要有两个(不了解如何实现之前),一个是如何实现水波纹效果,一个就是如何将背景的圆和水波纹有效的组合起来(这里有坑,请注意)!

首先是水波纹效果,恩,其实就是两组正选曲线一直做平移运动(之所以所难点,是因为不揭穿之前,我是一脸懵逼的-_-!!);

然后组合的话就要用到Paint的setXfermode()接口,在里面指定一个PorterDufffermode类的枚举对象,具体有哪些,这里也附上一个链接:http://blog.csdn.net/zidan_2011/article/details/21518351

好了,具体是如何的,上代码来分析吧!

public class WaveLoading extends View {

private Paint wavePaint;

private Bitmap waveBitmap;

private Canvas waveCanvas; //画水波纹相关的东东

private Paint cirPaint;

private Bitmap cirBitmap;

private Canvas cirCanvas; //画背景园相关的东东

private Paint mPaint; //canvas画布的画笔

private Bitmap midBitmap;

private Canvas midCanvas;

private Paint midPaint; //中间画布,用来组合背景圆和水波纹

private int viewWidth; //控件的宽

private int viewHeight; //控件的高

private Path mPath; // 画水波纹用到的路径

private int offset; //水波纹达到波动效果的偏移量

private int progress = 0; //进度条

private android.os.Handler mHander = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

invalidate();

}

};

public WaveLoading(Context context) {

this(context, null);

}

public WaveLoading(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public WaveLoading(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

initSetting();

}

private void initSetting() {

wavePaint = new Paint();

wavePaint.setColor(Color.parseColor("#0404FF"));

wavePaint.setAntiAlias(true);

wavePaint.setStyle(Paint.Style.FILL);

cirPaint = new Paint();

cirPaint.setColor(Color.parseColor("#88888888"));

cirPaint.setAntiAlias(true);

mPaint = new Paint();

mPaint.setStyle(Paint.Style.FILL);

midPaint = new Paint();

midPaint.setStyle(Paint.Style.FILL);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

viewWidth = MeasureSpec.getSize(widthMeasureSpec);

viewHeight = MeasureSpec.getSize(heightMeasureSpec);

offset = -viewWidth;

initBitMap();

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

private void initBitMap() {

if (waveBitmap == null) {

waveBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);

waveCanvas = new Canvas(waveBitmap);

}

if (cirBitmap == null) {

cirBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);

cirCanvas = new Canvas(cirBitmap);

}

if (midBitmap == null) {

midBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);

midCanvas = new Canvas(midBitmap);

}

}

@Override

protected void onDraw(Canvas canvas) {

cirCanvas.drawCircle(viewWidth / 2, viewHeight / 2, 150, cirPaint);

drawSrc();

midCanvas.drawBitmap(cirBitmap, 0, 0, midPaint);

midPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));

midCanvas.drawBitmap(waveBitmap, 0, 0, midPaint);

String percent = progress <= 300 ? progress / 3 + "%" : "100%";

mPaint.setTextSize(50);

mPaint.setTextAlign(Paint.Align.CENTER);

canvas.drawText(percent, (viewWidth) / 2, (viewHeight) / 2, mPaint);

canvas.drawBitmap(midBitmap, 0, 0, mPaint);

super.onDraw(canvas);

}

private void drawSrc() {

waveBitmap.eraseColor(Color.parseColor("#00000000"));

mPath = new Path();

wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));

for (int i = 0; i < 4; i++) {

if (i % 2 == 0) {

mPath.reset();

mPath.moveTo(offset + i * viewWidth / 2, viewHeight - progress);

mPath.quadTo(offset + (1 + 2 * i) * viewWidth / 4, viewHeight - progress - 40,

offset + (1 + i) * viewWidth / 2, viewHeight - progress);

waveCanvas.drawPath(mPath, wavePaint);

} else {

mPath.reset();

mPath.moveTo(offset + i * viewWidth / 2, viewHeight - progress);

mPath.quadTo(offset + (1 + 2 * i) * viewWidth / 4, viewHeight - progress + 40,

offset + (1 + i) * viewWidth / 2, viewHeight - progress);

waveCanvas.drawPath(mPath, wavePaint);

}

}

mPath.reset();

waveCanvas.drawRect(0, viewHeight - progress, viewWidth, viewHeight, wavePaint);

}

public void startLoading() {

Timer timer = new Timer();

TimerTask task = new TimerTask() {

@Override

public void run() {

progress++;

offset += 3;

if (offset == 0) offset = -viewWidth;

if (progress == viewHeight + 20) progress = 0;

mHander.obtainMessage(1).sendToTarget();

}

};

timer.schedule(task, 10, 10);

}

}

onMeasure和那些初始化方法就不再分析,直接说重点:

1.水波纹代码的实现

private void drawSrc() {

waveBitmap.eraseColor(Color.parseColor("#00000000"));

mPath = new Path();

wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));

for (int i = 0; i < 4; i++) {

if (i % 2 == 0) {

mPath.reset();

mPath.moveTo(offset + i * viewWidth / 2, viewHeight - progress);

mPath.quadTo(offset + (1 + 2 * i) * viewWidth / 4, viewHeight - progress - 40,

offset + (1 + i) * viewWidth / 2, viewHeight - progress);

waveCanvas.drawPath(mPath, wavePaint);

} else {

mPath.reset();

mPath.moveTo(offset + i * viewWidth / 2, viewHeight - progress);

mPath.quadTo(offset + (1 + 2 * i) * viewWidth / 4, viewHeight - progress + 40,

offset + (1 + i) * viewWidth / 2, viewHeight - progress);

waveCanvas.drawPath(mPath, wavePaint);

}

}

mPath.reset();

waveCanvas.drawRect(0, viewHeight - progress, viewWidth, viewHeight, wavePaint);

}

实现后的效果大致是这个样子的

45436e904d8dce8458619e93eb1585e3.png

可以看到,是个很明显的正弦波,拔高是通过改变progress,波动是通过改变offset实现,具体的代码在startLoading()方法中,这里使用了PorterDuffXferMode中的Mode.XOR 抑或,根据名称就知道,显示不相交的部分,目的在将水波纹的凹陷处去掉;

2.组合水波纹和原型背景

这里用到了PorterDuffXferMode中的Mode.SRC_ATOP,显示的是下方图层的全部和上方图层与下方图层相交的部分(蓝色的水),其实,你们一定会奇怪,你画就画吧,直接用canvas在控件上画不就好了么,干嘛要多此一举的使用一个midCanvas?

这就得说道我所说的那个坑了,你直接在canvas上面画的话,设置的PorterDuffXferMode就不起作用,或者说,作用完全是混乱的(我在使用的时候,完全没有发现什么规律,有知道的朋友可以下方留言我),所以必须使用一块中间的画布,来将水波纹和圆组合后再次添加到canvas上,总结一下就是,要在onDraw(Canvas canvas)的canvas上话bitmap的话,最好直接跟它有关的bitmap只有一个;

今天的分享就到这里,记录生活,共同进步!

原文:http://www.cnblogs.com/hugystudy/p/7098042.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值