自定义View的混合模式的使用实例

Posted LQS_Android

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义View的混合模式的使用实例相关的知识,希望对你有一定的参考价值。

自定义View的混合模式的使用实例一:图片倒影

package com.xw.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.xw.avatarview.R;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/6/9
 * @description: SRC_IN 模式 特性是根据目标图像透明度来决定如何显示源图像。源图像是小狗图像,目标图像是一张遮罩图,而这张遮罩图
 * 看不清楚,它是一个个从上到下的白色填充渐变 白色的透明度从49%到0%。
 */
public class InvertedImageView_SRCIN extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST, BmpSRC, BmpRevert;

    public InvertedImageView_SRCIN(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        //目标图像是一张遮罩图,而这张遮罩图看不清楚,它是一个从上到下的白色填充渐变,白色的透明度从49%到0%。
        BmpDST = BitmapFactory.decodeResource(getResources(), R.drawable.dog_invert_shade, null);
        //源图像是小狗图像
        BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.dog, null);

        Matrix matrix = new Matrix();
        //使用matrix.setScale(1F, -1F);方法来实现图片的垂直翻转,这样使用避免了使用旋转变换的复杂计算
        matrix.setScale(1F, -1F);
        //通过Matrix将小狗的源图像进行翻转生成倒影的源图像
        BmpRevert = Bitmap.createBitmap(BmpSRC, 0, 0, BmpSRC.getWidth(), BmpSRC.getHeight(), matrix, true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //int width = getWidth() * 2 / 3;
        int width = getWidth();
        int height = width * BmpDST.getHeight() / BmpDST.getWidth();

        //先画出正着的小狗图片
        canvas.drawBitmap(BmpSRC, null, new RectF(0, 0, width, height), mBitPaint);

        //开启离屏绘制再画出倒影
        int saveCount = canvas.save();
        //画布向下移动源图像的高度height,准备绘制倒影
        canvas.translate(0, height);
        canvas.drawBitmap(BmpDST, null, new RectF(0, 0, width, height), mBitPaint);
        //设置混合模式为PorterDuff.Mode.SRC_IN
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(BmpRevert, null, new RectF(0, 0, width, height), mBitPaint);
        //设置混合模式为空
        mBitPaint.setXfermode(null);

        canvas.restoreToCount(saveCount);
    }
}

绘图原理在代码的注释中已经备注的很清晰了,这里不再解释说明。效果图如下:

自定义View的混合模式的使用实例二:模仿刮刮乐抽奖效果

package com.xw.view;

import android.content.Context;
import android.graphics.Bitmap;
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.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.xw.avatarview.R;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/6/10
 * @description:
 */
public class EraserView_SRCOUT extends View {
    private Paint mBitPaint;
    private Bitmap BmpDST, BmpSRC, BmpText;
    private Path mPath;
    private float mPreX, mPreY;

    public EraserView_SRCOUT(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        mBitPaint = new Paint();
        mBitPaint.setColor(Color.RED);
        mBitPaint.setStyle(Paint.Style.STROKE);
        mBitPaint.setStrokeWidth(100);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;

        BmpText = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka_text, null);
        BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.dog, options);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(BmpText, null, new RectF(0, 0, BmpDST.getWidth(), BmpDST.getHeight()), mBitPaint);

        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        //先把手指轨迹画到目标Bitmap上
        Canvas c = new Canvas(BmpDST);
        c.drawPath(mPath, mBitPaint);

        //然后把目标图像画到画布上
        canvas.drawBitmap(BmpDST, 0, 0, mBitPaint);

        //计算源图像区域
        mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(BmpSRC, 0, 0, mBitPaint);

        mBitPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(), event.getY());
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            case MotionEvent.ACTION_MOVE:
                float endX = (mPreX + event.getX()) / 2;
                float endY = (mPreY + event.getY()) / 2;
                mPath.quadTo(mPreX, mPreY, endX, endY);
                mPreX = event.getX();
                mPreY = event.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        postInvalidate();
        return super.onTouchEvent(event);
    }
}

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<com.xw.view.EraserView_SRCOUT xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</com.xw.view.EraserView_SRCOUT>

MainActivity中直接加载布局就可以了:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

 效果图:

自定义View的混合模式的使用实例二:刮刮乐

package com.xw.guaguale.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;

import com.xw.bean.LotteryInfo;
import com.xw.guaguale.R;
import com.xw.guaguale.utils.LotteryManage;

/**
 * Mode.SRC_OUT 模式刚好能够实现当目标图像为不透明时,不显示相交区域的源图像;
 */
public class GuaGuaLeView extends TextView {
    //控件宽高
    private int widget, height;
    //上下文
    private Context mContext;
    //坐标点
    private Paint mPaint;
    //画布
    private Canvas tempCanvas;
    //图片类
    private Bitmap mBitmap;
    private float x, y, ox, oy;
    //画笔类
    private Path mPath;
    //实体类
    LotteryInfo info;
    int messageCount;
    //覆盖层颜色
    int color = 0xFFD6D6D6;
    //声明控件方法
    public GuaGuaLeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init(attrs);
    }
    //在次抽奖方法
    public void againLotter() {
        messageCount = 0;
        //获取奖励信息
        info = LotteryManage.getRandomLottery();
        //设置覆盖层颜色
        tempCanvas.drawColor(color);
        //中奖信息文字
        setText(info.getText());
        //设置中奖信息文字颜色
        setTextColor(Color.BLACK);
        //设置字体加粗
        getPaint().setFakeBoldText(true);
        //拓展可设置奖区图片
//		Drawable nav_up=getResources().getDrawable(R.drawable.ic_launcher);
//		nav_up.setBounds(0, 0, nav_up.getMinimumWidth(), nav_up.getMinimumHeight());
//		setCompoundDrawables(null, null, nav_up, null);
        //设置背景为白色
        setBackgroundResource(R.color.white);
    }
    //
    private void init(AttributeSet attrs) {
        // 获取控件大小值
        TypedArray a = mContext.obtainStyledAttributes(attrs,
                R.styleable.lotter);
        //设置控件大小
        widget = (int) a.getDimension(R.styleable.lotter_widget, 300);
        height = (int) a.getDimension(R.styleable.lotter_height, 100);
        //销毁对象
        a.recycle();
        // 初始化路径
        mPath = new Path();

        // 初始化画笔
        mPaint = new Paint();
        //设置画笔颜色
        mPaint.setColor(mContext.getResources().getColor(R.color.view_color));
        //设置透明度
        mPaint.setAlpha(0);
        //设置相交模式
        mPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
        //设置防止锯齿
        mPaint.setAntiAlias(true);
        //设置画笔空心画笔
        mPaint.setStyle(Style.STROKE);
        //画笔宽度
        mPaint.setStrokeWidth(50);
        // 初始化Bitmap并且锁定到临时画布上
        mBitmap = Bitmap.createBitmap(widget, height, Bitmap.Config.ARGB_8888);
        tempCanvas = new Canvas();
        //绘制图片
        tempCanvas.setBitmap(mBitmap);
        //获取新的彩票
        againLotter();

    }
    //绘制控件
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 将处理过的bitmap画上去
        canvas.drawBitmap(mBitmap, 0, 0, null);
    }

    //手势判断
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN://按下事件
                touchDown(event);
                break;
            case MotionEvent.ACTION_MOVE://手指移动事件
                touchMove(event);
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

    // 移动的时候
    private void touchMove(MotionEvent event) {
        //手指x坐标
        x = event.getX();
        //手机Y坐标
        y = event.getY();
        // 二阶贝塞尔曲线,实现平滑曲线;oX, oY为操作点 x,y为终点
        mPath.quadTo((x + ox) / 2, (y + oy) / 2, x, y);
        //绘制mPath进行绘制
        tempCanvas.drawPath(mPath, mPaint);
        ox = x;
        oy = y;
        //刷新控件
        invalidate();
    }
    // 按下事件
    private void touchDown(MotionEvent event) {
        ox = x = event.getX();
        oy = y = event.getY();
        //逆时针
        mPath.reset();
        //移动画笔
        mPath.moveTo(ox, oy);
    }
}

效果图如下:

欢迎留言讨论!需要源码请留言!

以上是关于自定义View的混合模式的使用实例的主要内容,如果未能解决你的问题,请参考以下文章

自定义View之混合模式学习总结

自定义View学习之12/7(进度条之混合模式)

Android:在片段内膨胀自定义视图

安卓自定义View基础-颜色

安卓自定义View基础 - 颜色

安卓自定义View基础 - 颜色