自定义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

时间: 2024-08-02 01:10:58

自定义View学习之12/7(进度条之混合模式)的相关文章

我的Android进阶之旅------&gt;Android自定义View实现带数字的进度条(NumberProgressBar)

今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢daimajia的开源奉献! 第一步.效果展示 图1.蓝色的进度条 图2.红色的进度条 图3.多条颜色不同的进度条 图4.多条颜色不同的进度条 版权声明:本文为[欧阳鹏]原创文章,欢迎转载,转载请注明出处! [http://blog.csdn.net/ouyang_peng/article/deta

自定义View学习之12/1

感谢AigeStudio提供的自定义view讲解(地址http://blog.csdn.net/aigestudio)下面是我看了Aige的讲解之后自己的理解以及demo,有说错的地方欢迎大家指出. 在这里自定义一个圆形等级条的view来加强自己对自定义的理解. 思路: 1.需要画一个背景圆,再需要一个覆盖在背景圆上面的进度圆. 2.使用线程让进度圆产生动画. 3.在进度圆达到圆满的时候回到原点,给个回调. 现在我们先画出一个空心圆. 代码块 /** 背景圆的画笔 */ private Pain

android 自定义view+属性动画实现充电进度条

近期项目中需要使用到一种类似手机电池充电进度的动画效果,以前没学属性动画的时候,是用图片+定时器的方式来完成的,最近一直在学习动画这一块,再加上复习一下自定义view的相关知识点,所以打算用属性动画和自定义view的方式来完成这个功能,将它开源出来,供有需要的人了解一下相关的内容. 本次实现的功能类似下面的效果: 接下来便详细解析一下如何完成这个功能,了解其中的原理,这样就能举一反三,实现其他类似的动画效果了. 详细代码请看大屏幕 https://github.com/crazyandcoder

自定义View,又一种进度条的呈现,CircleProgressView使用解析

转载请注明出处王亟亟的大牛之路 话不多说,先上效果图 循环旋转的状态 项目结构 一个Sample包,一个Lib包.Lib包里面其实只有一个累,很多内容都在素材文件里,比较建议把内容复制出来,贴到自己的项目中 主类: public class MainActivity extends ActionBarActivity { CircleProgressView mCircleView; Switch mSwitchSpin; Switch mSwitchShowUnit; SeekBar mSee

CodePush自定义更新弹框及下载进度条

CodePush 热更新之自定义更新弹框及下载进度 先来几张弹框效果图 非强制更新场景 image 强制更新场景 image 更新包下载进度效果 image 核心代码 这里的热更新Modal框,是封装成一个功能独立的组件来使用的,需不需要更新以及是否为强制更新等逻辑均在组件内实现 image UpdateComp 热更新组件核心代码如下: /** * Created by guangqiang on 2018/3/29. */ import React, {Component} from 're

Android自定义View学习笔记03

Android自定义View学习笔记03 预备知识 BitMap类 BitMap位图类,其中有一个嵌套类叫Bitmap.Config,内部有四个枚举值.这个类的作用是定义位图存储质量,即存储一个像素的位数,以及是否能显示透明.半透明颜色(Possible bitmap configurations. A bitmap configuration describes how pixels are stored. This affects the quality (color depth) as w

Android 自定义View学习(2)

上一篇学习了基本用法,今天学一下稍微复杂一点的,先看一下效果图 为了完成上面的效果还是要用到上一期开头的四步 1,属性应该要有颜色,要有速度 <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="speed" format="integer" /> <attr name="circleColor"

Android自定义View学习笔记04

Android自定义View学习笔记04 好长时间没有写相关的博客了,前几周在帮学姐做毕设,所以博客方面有些耽误.过程中写了一个类似wp的磁贴的view,想再写个配套的layout,所以昨天看了一下自定义viewGroup的相关知识-晚上睡觉想了一下可行性不是很高-代码量还不如直接自己在xml上写来得快,速度上也是个问题.今天看了一下张鸿洋老师的Android 自定义View (三) 圆环交替 等待效果这篇博文,再加上前一段时间看到的一幅图,结合之前写的一个圆形imageView的实现博文And

Android自定义view学习笔记02

Android自定义view学习笔记02 本文代码来自于张鸿洋老师的博客之Android 自定义View (二) 进阶 学习笔记,对代码进行些许修改,并补充一些在coding过程中遇到的问题.学习的新东西. 相关代码 //CustomImageView.java package mmrx.com.myuserdefinedview.textview; import android.content.Context; import android.content.res.TypedArray; im