自定义控件三部曲之绘图篇(十二)——Paint之setXfermode(三)

2024-02-28 09:08

本文主要是介绍自定义控件三部曲之绘图篇(十二)——Paint之setXfermode(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一篇给大家讲解了有关setXfermode的几种模式,还剩最后一系列DST模式没讲,这篇文章就给大家讲讲这个模式的用法及实战

一、DST相关模式
在讲完了SRC相关的模式以后,我们知道SRC相关的模式,都是在相交区域优先显示源图像为主。 
与之相对应的有DST相关的模式,在DST相关的模式中,在处理相交区域时,优先以目标图像显示为主。 
这部分所涉及的模式有:Mode.DST、Mode.DST_IN、Mode.DST_OUT、Mode.DST_OVER、Mode.DST_ATOP
1、Mode.DST
计算公式为:[Da, Dc] 
从公式中也可以看出,在处理源图像所在区域的相交问题时,正好与Mode.SRC相反,全部以目标图像显示 
所以示例图像为:


2、Mode.DST_IN
计算公式为:[Da * Sa,Dc * Sa] 
我们与Mode.SRC_IN的公式对比一下:SRC_IN:[Sa * Da, Sc * Da] 
正好与SRC_IN相反,Mode.DST_IN是在相交时利用源图像的透明度来改变目标图像的透明度和饱和度。当源图像透明度为0时,目标图像就完全不显示。 
示例图像为:


由于Mode.DST_IN的模式与SRC_IN正好是相反,所以我们利用Mode.SRC_IN实现的示例,只需要将源图像与目标图像对调就可以使用Mode.DST_IN来实现了。 
所以大家自己来改造实现下《自定义控件三部曲之绘图篇(十一)——Paint之setXfermode(二)》中的图形圆角效果和图片倒影效果,这里就不再贴代码了,有困难的同学可以参考文章底部源码,源码中有实现。 
这里我们就再来实现几个不一样的效果,这些效果也可以通过Mode.SRC_IN模式实现哦,大家自己可以试试 

示例1、区域波纹
我们在《自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果 》中讲解了水波纹效果,但这个水波纹效果却只能是一固定在一个矩形区域,本例我们就利用xfermode来实现在不规则区域中显示水波纹效果,效果图如下:


这里使用到一张图片(text_shade.png):

在这张图片中,只有文字部分是纯白色的,其它区域都是透明像素。 
所以再加上我们需要自己绘制的水波纹效果的图片,这里就有两张图片了,一张是水波纹效果图,另一张是text_shade.png 
那么问题来了,如果我们使用Mode.DST_IN模式的话,谁当目标图像,谁当源图像呢? 
这就需要分析Mode.DST_IN模式的成像原理了,在Mode.DST_IN中,源图像所占区域计算结果图像时,相交区域显示的是DST目标图像; 
所以我们要最终显示的被裁剪后的波纹图,所以DST目标图像就应该是波纹图。 
所以源码如下:

public class CircleWave_DSTIN extends View {
    private Paint mPaint;
    private Path mPath;
    private int mItemWaveLength = 1000;
    private int dx;
 
    private Bitmap BmpSRC,BmpDST;
    public CircleWave_DSTIN(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.text_shade,null);
        BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
 
        startAnim();
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
 
        generageWavePath();
 
        //先清空bitmap上的图像,然后再画上Path
        Canvas c = new Canvas(BmpDST);
        c.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
        c.drawPath(mPath,mPaint);
 
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(BmpDST,0,0,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
 
    /**
     * 生成此时的Path
     */
    private void generageWavePath(){
        mPath.reset();
        int originY = BmpSRC.getHeight()/2;
        int halfWaveLen = mItemWaveLength/2;
        mPath.moveTo(-mItemWaveLength+dx,originY);
        for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){
            mPath.rQuadTo(halfWaveLen/2,-50,halfWaveLen,0);
            mPath.rQuadTo(halfWaveLen/2,50,halfWaveLen,0);
        }
        mPath.lineTo(BmpSRC.getWidth(),BmpSRC.getHeight());
        mPath.lineTo(0,BmpSRC.getHeight());
        mPath.close();
    }
 
    public void startAnim(){
        ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int)animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}
这里涉及到利用贝赛尔曲线生成波浪线的知识,请参考《自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果 》,有关波浪线的代码就一概而过了,核心放在xfermode上。 
首先看初始化:
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.text_shade,null);
BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
在初始化时,生成了两张图像,正如我们所说BmpDST是要显示的波浪线,所以我们新建一个空白图像,用以画即将生成的波浪线;而BmpSRC则用来显示源图。 
然后再看onDraw()部分:
generageWavePath();
 
//先清空bitmap上的图像,然后再画上Path
Canvas c = new Canvas(BmpDST);
c.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
c.drawPath(mPath,mPaint);
这一段是将波浪线的Path画到BmpDST图像上。 
首先是利用generageWavePath()生成当前的波浪线path,然后是利用PorterDuff.Mode.CLEAR模式将BmpDST图像清空:
c.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
有关使用PorterDuff.Mode.CLEAR来清空图像的知识,后面在讲解PorterDuff.Mode.CLEAR时会细讲,这里知道即可。 
为什么我们要首先清空BmpDST的图像呢,因为在这个图像之前已经画上了上一次的波浪线的图像了,我们如果不将图像清空,那这两幅波纹线不就重叠了么。 
在清空BmpDST之后,再在上面画上path:
c.drawPath(mPath,mPaint);
在生成BmpDST之后,我们就可以使用xfermode了:
//先画上SRC图像来显示完整的文字
canvas.drawBitmap(BmpSRC,0,0,mPaint);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(BmpDST,0,0,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(BmpSRC,0,0,mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
有些同学可能会问,为什么在saveLayer前,先要画一遍BmpSRC呢,这是因为我们在使用Mode.DST_IN时,除了相交区域以外,其它区域都会因为有空白像素而消失不见了,如果我们不加canvas.drawBitmap(BmpSRC,0,0,mPaint);效果图将是这样的:

看到问题所在了吧,如果仅仅这样显示,用户根本看不出来这是个啥字,所以我们需要提前把完整的字母绘制上去,来显示完整的文字样式; 
源码在文章底部给出 
2、心电图
下面我们再来实现一个心电图的动画,效果图如下:


很明显,正规的心电图应该是利用Path把当前的实时的点连接起来,我这里只是一张图片(hearmap.png)通过使用动画来实现的

中间是一条心电图线,其余位置都是透明像素;大家先想想我们要怎么利用这张图片实现上面的动画呢? 
利用Mode.DST_IN模式,由于在这个模式中,相交区域优先显示目标图像,所以我们这里需要显示心电图,所以心电图就是目标图像。 
那么问题来了,源图像是啥? 
由于我们需要从右向左逐渐显示心电图图像,所以我们源图像就是自建的空白图像,在这个图像中,绘制一个矩形,逐渐增大矩形的区域,即相交区域也会跟着增大,由于相交区域会显示出目标图像,显示出来的结果就是心电图的动画,代码如下:

public class HeartMap extends View {
 
    private Paint mPaint;
    private int mItemWaveLength = 0;
    private int dx=0;
 
    private Bitmap BmpSRC,BmpDST;
    public HeartMap(Context context, AttributeSet attrs) {
        super(context, attrs);
 
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
 
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.heartmap,null);
        BmpSRC = Bitmap.createBitmap(BmpDST.getWidth(), BmpDST.getHeight(), Bitmap.Config.ARGB_8888);
 
        mItemWaveLength = BmpDST.getWidth();
        startAnim();
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
 
        Canvas c = new Canvas(BmpSRC);
        //清空bitmap
        c.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
        //画上矩形
        c.drawRect(BmpDST.getWidth() - dx,0,BmpDST.getWidth(),BmpDST.getHeight(),mPaint);
 
        //模式合成
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(BmpDST,0,0,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
 
 
    public void startAnim(){
        ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
        animator.setDuration(6000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int)animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}
这段代码难度不大,就不再讲了,我们再来举个例子。
3、不规则波纹
上面我们实现的波纹效果都是规则的,如果我们想实现如下图这样的不规则波纹要怎么办呢?

在这里我们需要用到两张图: 
一张圆形遮罩(circle_shape.png)


一张不规则的波浪图

想必到这里,可能很多同学都知道要怎么做了 
就是在圆形遮罩上绘制不断移动的不规则的波浪图。 
代码如下:

public class IrregularWaveView extends View {
 
    private Paint mPaint;
    private int mItemWaveLength = 0;
    private int dx=0;
 
    private Bitmap BmpSRC,BmpDST;
 
    public IrregularWaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
 
        BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.wave_bg,null);
        BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.circle_shape,null);
        mItemWaveLength = BmpDST.getWidth();
 
        startAnim();
    }
 
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
 
        //先画上圆形
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        //再画上结果
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(BmpDST,new Rect(dx,0,dx+BmpSRC.getWidth(),BmpSRC.getHeight()),new Rect(0,0,BmpSRC.getWidth(),BmpSRC.getHeight()),mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(BmpSRC,0,0,mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }
 
 
    public void startAnim(){
        ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
        animator.setDuration(4000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int)animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}
首先看动画部分:
public void startAnim(){
    ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
    animator.setDuration(4000);
    animator.setRepeatCount(ValueAnimator.INFINITE);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            dx = (int)animation.getAnimatedValue();
            postInvalidate();
        }
    });
    animator.start();
}
其中:
mItemWaveLength = BmpDST.getWidth();
在这里生成了一个ValueAnimator动画,动画的值从0到波浪图的总长,实时的值保存在dx变量中。 
再看onDraw函数
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
 
    //先画上圆形
    canvas.drawBitmap(BmpSRC,0,0,mPaint);
    //再画上计算结果
    int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
    canvas.drawBitmap(BmpDST,new Rect(dx,0,dx+BmpSRC.getWidth(),BmpSRC.getHeight()),new Rect(0,0,BmpSRC.getWidth(),BmpSRC.getHeight()),mPaint);
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawBitmap(BmpSRC,0,0,mPaint);
    mPaint.setXfermode(null);
    canvas.restoreToCount(layerId);
}
首先,是画了个BmpSRC所表示的原形图:
canvas.drawBitmap(BmpSRC,0,0,mPaint);
与“示例1、区域波纹”的原因一样,我们需要先画上圆形图,不然就看不出来整体的样式是什么样的。 
然后在使用xfermode时,最难的应该是画BmpDST的这句:
canvas.drawBitmap(BmpDST,new Rect(dx,0,dx+BmpSRC.getWidth(),BmpSRC.getHeight()),new Rect(0,0,BmpSRC.getWidth(),BmpSRC.getHeight()),mPaint);
它的意思就是截取波浪图上new Rect(dx,0,dx+BmpSRC.getWidth(),BmpSRC.getHeight())这个矩形位置,将其画在BmpSRC的位置:new Rect(0,0,BmpSRC.getWidth(),BmpSRC.getHeight())
好了,这部分也就到这了 
源码在文章底部给出
3、Mode.DST_OUT
计算公式为:[Da * (1 - Sa), Dc * (1 - Sa)] 
同样,我们拿这个公式与Mode.SRC_OUT对比一下,Mode.SRC_OUT:[Sa * (1 - Da), Sc * (1 - Da)] 
可以看出Mode.SRC_OUT是利用目标图像的透明度的补值来改变源图像的透明度和饱和度。而Mode.DST_OUT反过来,是通过源图像的透明度补值来改变目标图像的透明度和饱和度。 
简单来说,在Mode.DST_OUT模式下,就是相交区域显示的是目标图像,目标图像的透明度和饱和度与源图像的透明度相反,当源图像透明底是100%时,则相交区域为空值。当源图像透明度为0时,则完全显示目标图像。非相交区域完全显示目标图像。 
示例图像为:

有些同学对这个结果可能感觉很奇怪,我们来分析一下,上篇中我们提到在xfermode的示例图像中,我们主要需要关注两点:

图中编号1的相交区域:在DST_OUT模式下,由于源图像的透明度是100%,所以计算后的结果图像在这个区域是空像素。 
图中编号2的非相交区域:在DST_OUT模式下,这个区域的源图像透明度仍为100%,所以计算后的结果图像在这个区域仍是空像素。 
所以我们做下简单的总结,当源图像区域透明度为100%时,所在区域计算结果为透明像素,当源图像的区域透明时,计算结果就是目标图像; 
这与SRC_OUT模式的结果正好相反,在SRC_OUT模式下,当目标图像区域透明度为100%时,所在区域计算结果为透明像素,当目标图像的区域透明时,计算结果就是源图像; 
所以,在上篇中,使用SRC_OUT模式实现的橡皮擦效果和刮刮卡效果都是可以使用DST_OUT模式实现的,只需要将SRC和DST所对应的图像翻转一下就可以了;
这里就不再实现了,大家自己来试试吧。
4、Mode.DST_OVER
计算公式为:[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] 
同样先写Mode.SRC_OVER对比一下,SRC_OVER:[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] 
所以它们的效果就是在SRC模式中以显示SRC图像为主变成了以显示DST图像为主。从SRC模式中的使用目标图像控制结果图像的透明度和饱和度变成了由源图像控件结果图像的透明度和饱和度。 
示例图像为:

5、Mode.DST_ATOP
计算公式为:[Sa, Sa * Dc + Sc * (1 - Da)] 
示例图像为:


由于在SRC中,我们知道了Mode.SRC_ATOP与MODE.SRC_IN的区别: 
一般而言SRC_ATOP是可以和SRC_IN通用的,但SRC_ATOP所产生的效果图在目标图的透明度不是0或100%的时候,会比SRC_IN模式产生的图像更亮些; 
我们再来对比下DST中的两个模式与SRC中的这两个模式中公式中区别: 
SRC_IN: [Sa * Da, Sc * Da] 
SRC_ATOP:[Da, Sc * Da + (1 - Sa) * Dc] 
DST_IN:[Da * Sa , Dc * Sa ] 
DST_ATOP:[Sa, Sa * Dc + Sc * (1 - Da)] 
从公式中可以看到,在SRC模式中,以显示源图像为主,透明度和饱和度利用Da来调节 
而在DST模式中,以显示目标图像为主,透明度和饱和度利用Sa来调节 
所以Mode.DST_ATOP与Mode.DST_IN的关系也是: 
一般而言DST_ATOP是可以和DST_IN通用的,但DST_ATOP所产生的效果图在源图像的透明度不是0或100%的时候,会比DST_IN模式产生的图像更亮些; 
同样,大家也可以使用Mode.DST_ATOP实现上篇文章中Mode.SRC_ATOP的两个示例:圆角效果和图片倒影,这里就不再讲了

到这里有关DST相关模式都讲完了,我们总结一下:
1、DST相关模式是完全可以使用SRC对应的模式来实现的,只不过需要将目标图像和源图像对调一下即可。 
2、在SRC模式中,是以显示源图像为主,通过目标图像的透明度来调节计算结果的透明度和饱和度,而在DST模式中,是以显示目标图像为主,通过源图像的透明度来调节计算结果的透明度和饱和度。

二、其它模式
除了颜色叠加系列模式,SRC系列模式和DST系列模式以外,另外还有两个模式Mode.CLEAR和Mode.XOR,下面我们就来看看它们的用法
1、Mode.CLEAR
计算公式:[0, 0] 
示例图像:

前面我们做清空图像的时候用过这个方法,从公式中可以看到,计算结果直接就是[0,0]即空像素。也就是说,源图像所在区域都会变成空像素! 
这样就起到了清空源图像所在区域图像的功能了。上面示例中已经存在这个Mode的用法,这里就不再举例了。

2、Mode.XOR
计算公式为:[Sa + Da - Sa*Da,Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] 
示例图像为:


单从示例图像中,好像是异或的功能,即将源图像中除了相交区域以外的部分做为结果。但仔细看看公式,其实并没有这么简单。 
首先看公式中透明度部分:Sa + Da - Sa*Da,就是将目标图像和源图像的透明度相加,然后减去它们的乘积,所以计算结果的透明度会增大(即比目标图像和源图像都大,当其中一个图像的透明度为1时,那结果图像的透明度肯定是1) 
然后再看颜色值部分:Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc);表示源图像和目标图像分别以自己的透明度的补值乘以对方的颜色值,然后相加得到结果。最后再加上Sc, Dc中的最小值。 
这个模式太过复杂,在实际应用中应用也比较少,目前没想到有哪些示例,大家有用到的,可以跟我说哦。

在实际应用中,我们可以从下面三个方面来决定使用哪一个模式:
1、首先,目标图像和源图像混合,需不需要生成颜色的叠加特效,如果需要叠加特效则从颜色叠加相关模式中选择,有Mode.ADD(饱和度相加)、Mode.DARKEN(变暗),Mode.LIGHTEN(变亮)、Mode.MULTIPLY(正片叠底)、Mode.OVERLAY(叠加),Mode.SCREEN(滤色) 
2、当不需要特效,而需要根据某一张图像的透明像素来裁剪时,就需要使用SRC相关模式或DST相关模式了。由于SRC相关模式与DST相关模式是相通的,唯一不同的是决定当前哪个是目标图像和源图像; 
3、当需要清空图像时,使用Mode.CLEAR
这篇文章到这里就结束啦,有关xfermode的知识比较有难度,大家需要仔细揣摩下

 

 

这篇关于自定义控件三部曲之绘图篇(十二)——Paint之setXfermode(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755034

相关文章

AI绘图怎么变现?想做点副业的小白必看!

在科技飞速发展的今天,AI绘图作为一种新兴技术,不仅改变了艺术创作的方式,也为创作者提供了多种变现途径。本文将详细探讨几种常见的AI绘图变现方式,帮助创作者更好地利用这一技术实现经济收益。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 定制服务:个性化的创意商机 个性化定制 AI绘图技术能够根据用户需求生成个性化的头像、壁纸、插画等作品。例如,姓氏头像在电商平台上非常受欢迎,

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

Oracle type (自定义类型的使用)

oracle - type   type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要特殊的格式, 如将name定义为(firstname,lastname)的形式,我们想把这个作为一个表的一列看待,这时候就要我们自己定义一个数据类型 格式 :create or repla

lvgl8.3.6 控件垂直布局 label控件在image控件的下方显示

在使用 LVGL 8.3.6 创建一个垂直布局,其中 label 控件位于 image 控件下方,你可以使用 lv_obj_set_flex_flow 来设置布局为垂直,并确保 label 控件在 image 控件后添加。这里是如何步骤性地实现它的一个基本示例: 创建父容器:首先创建一个容器对象,该对象将作为布局的基础。设置容器为垂直布局:使用 lv_obj_set_flex_flow 设置容器

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

一步一步将PlantUML类图导出为自定义格式的XMI文件

一步一步将PlantUML类图导出为自定义格式的XMI文件 说明: 首次发表日期:2024-09-08PlantUML官网: https://plantuml.com/zh/PlantUML命令行文档: https://plantuml.com/zh/command-line#6a26f548831e6a8cPlantUML XMI文档: https://plantuml.com/zh/xmi

argodb自定义函数读取hdfs文件的注意点,避免FileSystem已关闭异常

一、问题描述 一位同学反馈,他写的argo存过中调用了一个自定义函数,函数会加载hdfs上的一个文件,但有些节点会报FileSystem closed异常,同时有时任务会成功,有时会失败。 二、问题分析 argodb的计算引擎是基于spark的定制化引擎,对于自定义函数的调用跟hive on spark的是一致的。udf要通过反射生成实例,然后迭代调用evaluate。通过代码分析,udf在