自定义View学习之12/7(进度条之混合模式)
Posted silly_wy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义View学习之12/7(进度条之混合模式)相关的知识,希望对你有一定的参考价值。
今天重点内容是我们学习自定义view里面的混合模式,其实我们的画布就跟photoshop一样,是个图层关系,一层盖着一层,这样就导致有很多种覆盖模式,这就是我们今天的主题,“混合模式”。
好,现在我们来看下这个模式的说明图:
canvas原有的图片 可以理解为背景 就是dst
新画上去的图片 可以理解为前景 就是src
从上面我们可以看到PorterDuff.Mode为枚举类,一共有16个枚举值:
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色
我决定以以下2个效果来作为联系和实现下面请看效果。1、一个是进度条转完以波纹动画的方式显示实物。2、是一款进度条,当进度覆盖文字的时候,覆盖到哪里,哪里的文字的一部分就显示成白色:
第一种效果:
接下来我们就来看这个gif的代码,其实很简单主要实现方式呢就是圆形加载条是以Canvas画扇形的方式画出,只是圆心空心而已。加载完之后呢外面的大圆就是Canvas以画圆的方式画出,只是混合模式是CLEAR也是清除的意思,占用大小刚好就是加载条的大小,然后小圆的大小也是加载条的大小,刚好覆盖在大圆上面。接着就启动循环加载知道全部显示,大圆扩散显示(因为是CLEAR模式所以,覆盖到的地方全是透明的),小圆缩小显示:
package com.wyw.lodingdemo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
public class LoadingView extends View
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
public LoadingView(Context context, AttributeSet attrs)
super(context, attrs);
init();
public LoadingView(Context context)
super(context);
init();
private Paint paint;
/** 设置矩阵的坐标点 */
private RectF rectF;
/** 当前进度 */
private int current = 0;
/** 横向中心X轴 */
private float centerX = 0;
/** 竖向中心Y轴 */
private float centerY = 0;
/** 园半径 */
private float circleRadius;
/** 是否完成 */
private boolean isComplete = false;
/** 完成之后显示的图片 */
private Bitmap bitmap;
private Matrix matrix;
// 缩放比率
private float widthRate;
private float heightRate;
/** bitmap画笔 */
private Paint Bpaint;
private int size;
/** 白屏显示的画布 */
private Canvas mCanvas;
/** 消失画笔(大圆) */
private Paint Gpaint_big;
/** 消失画笔(小圆) */
private Paint Gpaint_small;
private Bitmap fgBitmap;
private Bitmap frontBitmap;
/** 消失的园半径 */
private float gone_circleRadius_big = 0;
/** 消失的园半径 */
private float gone_circleRadius_small = 0;
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom)
centerX = (right - left) / 2;
centerY = (bottom - top) / 2;
rectF = new RectF(centerX - circleRadius, centerY - circleRadius,
centerX + circleRadius, centerY + circleRadius);// 弧形
super.onLayout(changed, left, top, right, bottom);
private void init()
size = Math.min(getResources().getDisplayMetrics().widthPixels,
getResources().getDisplayMetrics().heightPixels);
paint = new Paint();// 布局xml里面引用
paint.setColor(Color.parseColor("#fe871a"));
paint.setAntiAlias(true);// 设置抗锯齿
paint.setStrokeWidth(getInt(1f, size));
paint.setStyle(Style.STROKE);// 设置圆心掏空
// 设置画笔形状 圆形,需要先设置画笔样式 SYROKE 或者 FILL_AND_STROKE
paint.setStrokeCap(Paint.Cap.ROUND);
circleRadius = getInt(7f, size);
/** 获取传入颜色,高度,宽度的Bitmap */
public Bitmap CreateBitmap(int color, int width, int height)
int[] rgb = new int[width * height];
for (int i = 0; i < rgb.length; i++)
rgb[i] = color;
return Bitmap.createBitmap(rgb, width, height, Config.ARGB_4444);
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
if (!isComplete) // 没完成
canvas.drawArc(rectF, 0, current, false, paint);
else // 已完成
if (bitmap != null && isComplete)
if (matrix == null)
initParameters();
matrix.reset();
matrix.postScale(widthRate, heightRate);
// 绘制白色背景图
mCanvas.drawBitmap(frontBitmap, 0, 0, null);
canvas.drawBitmap(bitmap, matrix, Bpaint);
// 绘制前景
canvas.drawBitmap(fgBitmap, 0, 0, null);
// mCanvas.drawArc(left, top, right, bottom, startAngle,
// sweepAngle, useCenter, Gpaint);
mCanvas.drawCircle(centerX, centerY, gone_circleRadius_big,
Gpaint_big);
// 绘制前景
canvas.drawCircle(centerX, centerY, gone_circleRadius_small,
Gpaint_small);
if (gone_circleRadius_big < centerX * 1.5f
|| gone_circleRadius_small > 0)
handler.post(drawRunnable);
private Handler handler = new Handler();
private Runnable drawRunnable = new Runnable()
@Override
public void run()
gone_circleRadius_big += centerX * 1.5f / 50f;
gone_circleRadius_small -= circleRadius / 50f;
invalidate();
;
/** 初始化matrix */
private void initParameters()
matrix = new Matrix();
Bpaint = new Paint();
Bpaint.setAntiAlias(true);
gone_circleRadius_big = circleRadius;
gone_circleRadius_small = circleRadius;
Gpaint_small = new Paint();
// 防锯齿
Gpaint_small.setAntiAlias(true);
Gpaint_small.setColor(Color.BLACK);
Gpaint_big = new Paint();
// 防锯齿
Gpaint_big.setAntiAlias(true);
// 设置混合模式为DST_IN
Gpaint_big.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
// 生成前景图Bitmap 这里拿的宽高要在onDraw里面才能拿到哦。
fgBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Config.ARGB_4444);
frontBitmap = CreateBitmap(Color.BLACK, getWidth(), getHeight());
mCanvas = new Canvas(fgBitmap);
if (bitmap != null)
float iw = bitmap.getWidth();
float ih = bitmap.getHeight();
float width = this.getWidth();
float height = this.getHeight();
// 初始放缩比率
widthRate = width / iw;
heightRate = height / ih;
/** 是否完成 */
public void setComplete(boolean isComplete, Bitmap bitmap)
this.isComplete = isComplete;
this.bitmap = bitmap;
invalidate();
/**
* 设置当前进度
*
* @param current
* 进度
*/
public void setCurrentProgress(float current, float max)
this.current = (int) ((360f / max) * current);
invalidate();
/**
* 获取占屏幕的百分比
*
* @param value
* 使用size的百分比
* @param size
* 最大值
* @return 根据百分算出的大小
*/
private int getInt(float value, int size)
try
return Math.round(value * (float) size / 100f);
catch (Exception ignore)
return 0;
第二种效果:
好,我们来看下代码,我这里的默认模式是SCREEN。SCREEN呢就是覆盖的时候覆盖部分会是白色。这里我的文字是SCREEN模式,所以呢当我的进度条覆盖到文字的时候,覆盖的部分就会变成白色。这里呢我把所有的模式和不同的颜色都加上了,具体怎么理解怎么定义可以下载demo亲自去尝试,去切换看看。那些混合模式到底都是哪些效果。
package com.wyw.loadingdemob;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.PorterDuffXfermode;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff.Mode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class LoadingViewb extends View
public LoadingViewb(Context context)
super(context);
init();
public LoadingViewb(Context context, AttributeSet attrs)
super(context, attrs);
init();
public LoadingViewb(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
// 背景圆角矩形画笔
private Paint paint_bg;
// 背景圆角矩形
private RectF rect_bg;
// 字体画笔
private Paint paint_txt;
// 前景圆角
private Paint paint_front;
// 前景圆角矩形
private RectF rect_front;
// 结束位置
private int endX;
// 起始位置
private int startX;
// 当前位置
private int currentX;
// 竖向中间位置
private int centerY;
// 横向中间位置
private int centerX;
// 要显示的文字
private String text = "0%";
// 文字竖向居中的数值
private int txt_center_y = 0;
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom)
//开始位置(因为是圆角所以会突出一部分所以开始位置得加上屏幕的百分之5)
startX = 0 + getInt(5f);
//结束位置(因为是圆角所以会突出一部分所以开始位置得减去屏幕的百分之5)
endX = right - left - getInt(5f);
//拿到y轴中心点
centerY = (bottom - top) / 2;
//拿到x轴中心点
centerX = (right - left) / 2;
super.onLayout(changed, left, top, right, bottom);
private void init()
paint_bg = new Paint();
paint_bg.setColor(Color.parseColor("#fe871a"));
// 设置抗锯齿
paint_bg.setAntiAlias(true);
paint_bg.setStrokeWidth(getInt(1f));
// 设置圆心掏空
paint_bg.setStyle(Style.STROKE);
// 设置画笔形状 圆形,需要先设置画笔样式 STROKE 或者 FILL_AND_STROKE
paint_bg.setStrokeCap(Paint.Cap.ROUND);
paint_txt = new Paint();
paint_txt.setColor(Color.parseColor("#fe871a"));
paint_txt.setTextSize(getInt(5f));
// 设置抗锯齿
paint_txt.setAntiAlias(true);
// 设置混合模式为SCREEN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SCREEN));
// 下面这行是实现字体水平居中
paint_txt.setTextAlign(Paint.Align.CENTER);
paint_front = new Paint();
paint_front.setColor(Color.parseColor("#fe871a"));
// 设置抗锯齿
paint_front.setAntiAlias(true);
// 设置混合模式为SRC_ATOP
paint_front.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
if (rect_bg == null)
rect_bg = new RectF(startX, centerY - getInt(5f), endX, centerY
+ getInt(5f));
rect_front = new RectF(startX, centerY - getInt(5f), currentX,
centerY + getInt(5f));
// 实现字体竖向居中
FontMetricsInt fontMetrics = paint_txt.getFontMetricsInt();
txt_center_y = (centerY * 2 - fontMetrics.bottom - fontMetrics.top) / 2;
canvas.drawRoundRect(rect_bg, getInt(10f), getInt(10f), paint_bg);
canvas.drawRoundRect(rect_front, getInt(10f), getInt(10f), paint_front);
canvas.drawText(text, centerX, txt_center_y, paint_txt);
// 设置当前进度
public void setCurrentProgress(float current, float max)
//因为起点不是0,所以总长度需要减去起点 (endX-startX)
currentX = (int) (((float) (endX-startX) / max) * current);
text = (int) ((float) currentX / (float) (endX-startX) * 100) + "%";
//因为起点不是0所以需要加上起点的
rect_front.right = currentX+startX;
invalidate();
// 设置字体颜色
public void setTextColor(int color)
paint_txt.setColor(color);
//重置数据
currentX = 0;
text = "0%";
if (rect_front != null)
rect_front.right = currentX;
invalidate();
// 设置重叠模式
public void setMode(String mode)
if (mode.equals("clear"))
// 设置混合模式为CLEAR
paint_txt.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
else if (mode.equals("Src"))
// 设置混合模式为SRC
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SRC));
else if (mode.equals("Dst"))
// 设置混合模式为DST
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DST));
else if (mode.equals("srcOver"))
// 设置混合模式为SRC_OVER
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SRC_OVER));
else if (mode.equals("DstOver"))
// 设置混合模式为DST_OVER
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DST_OVER));
else if (mode.equals("SrcIn"))
// 设置混合模式为SRC_IN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
else if (mode.equals("DstIn"))
// 设置混合模式为DST_IN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
else if (mode.equals("SrcOut"))
// 设置混合模式为SRC_OUT
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
else if (mode.equals("DstOutr"))
// 设置混合模式为DST_OUT
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));
else if (mode.equals("SrcATop"))
// 设置混合模式为SRC_ATOP
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
else if (mode.equals("DstATop"))
// 设置混合模式为DST_ATOP
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
else if (mode.equals("Xor"))
// 设置混合模式为XOR
paint_txt.setXfermode(new PorterDuffXfermode(Mode.XOR));
else if (mode.equals("Darken"))
// 设置混合模式为DARKEN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.DARKEN));
else if (mode.equals("Lighten"))
// 设置混合模式为LIGHTEN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.LIGHTEN));
else if (mode.equals("Multiply"))
// 设置混合模式为MULTIPLY
paint_txt.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
else if (mode.equals("Screen"))
// 设置混合模式为SCREEN
paint_txt.setXfermode(new PorterDuffXfermode(Mode.SCREEN));
//重置数据
currentX = 0;
text = "0%";
if (rect_front != null)
rect_front.right = currentX;
invalidate();
/**
* 获取占屏幕的百分比
*
* @param value
* 使用size的百分比
* @param size
* 最大值
* @return 根据百分算出的大小
*/
private int getInt(float value)
int size = Math.min(getResources().getDisplayMetrics().widthPixels,
getResources().getDisplayMetrics().heightPixels);
try
return Math.round(value * (float) size / 100f);
catch (Exception ignore)
return 0;
本篇博客就到这里,如果有有疑问的欢迎留言讨论。同时希望大家多多关注我的博客,多多支持我。
尊重原创转载请注明:(http://blog.csdn.net/u013895206) !
下面是地址传送门:
第一种效果下载地址:http://download.csdn.net/detail/u013895206/9479008
第二种效果下载地址:http://download.csdn.net/detail/u013895206/9479013
以上是关于自定义View学习之12/7(进度条之混合模式)的主要内容,如果未能解决你的问题,请参考以下文章