我正在参加 CSDN 2015博客之星评选 感恩分享活动,如果觉得文章还不错,请投个票鼓励下吧:http://vote.blog.csdn.net/blogstar2015/candidate?username=tianjian4592


尊重原创,欢迎转载,转载请注明: FROM GA_studio http://blog.csdn.net/tianjian4592


前面关于paint的方法讲解里,讲到setXfermode 就截止了,原因有两个:

1. 那篇文章已经太长了,我自己都看不下去了;

2.setXfermode 在paint 里占有至关重要的地位;

基于以上两个原因,我们一起来看看这个方法有何妙用。

首先我们还是来看看关于这个方法的说明:

    /**     * Set or clear the xfermode object. - 设置或清除xfermode对象;     * Pass null to clear any previous xfermode. - 传递null以清除任何以前的xfermode。     * As a convenience, the parameter passed is also returned. - 为方便起见,也返回传递的参数。     *     * @return         xfermode     */    public Xfermode setXfermode(Xfermode xfermode) {        int xfermodeNative = 0;        if (xfermode != null)            xfermodeNative = xfermode.native_instance;        native_setXfermode(mNativePaint, xfermodeNative);        mXfermode = xfermode;        return xfermode;    }

这个方法传进一个 Xfermode 对象,而打开 Xfermode 发现里面没有提供任何可用的构造函数或方法,ctrl +T 看到它有三个子类:




前两个子类 AvoidXfermode 和 PixelXorXfermode 大家可以看到都已经被划上了斜线,下面就简单提及一下,咱们的重点在 PorterDuffXfermode :

1.AvoidXfermode:

    /** This xfermode draws, or doesn't draw, based on the destination's     * distance from an op-color.     *     * There are two modes, and each mode interprets a tolerance value.     *     * Avoid: In this mode, drawing is allowed only on destination pixels that     * are different from the op-color.     * Tolerance near 0: avoid any colors even remotely similar to the op-color     * Tolerance near 255: avoid only colors nearly identical to the op-color     * Tolerance near 0: draw only on colors that are nearly identical to the op-color     * Tolerance near 255: draw on any colors even remotely similar to the op-color     */    public AvoidXfermode(int opColor, int tolerance, Mode mode) {        if (tolerance < 0 || tolerance > 255) {            throw new IllegalArgumentException("tolerance must be 0..255");        }        native_instance = nativeCreate(opColor, tolerance, mode.nativeInt);    }

咱们把它上面的说明看下就很清楚了:

xfermode 是否绘制,基于目标色和参数 op-color 的差距;

其中有两种模式,分别为 Avoid 和 TARGET:

Avoid模式:只会在目标像素值和 op-color "不一样" 的地方进行绘制;

Target模式:只会在目标像素值和 op-color "一样" 的地方进行绘制;

上面的"一样" 和 "不一样" 我都打上了引号,并不是指严格意义上的一样,而是只要在可容忍范围内就代表一样,这个可容忍范围,就是容差值(tolerance),0 代表最小容差,即得和 op-color 真正意义上一样才 ok ,255 则代表最大容差,只要有一点相近,则ok;

咱们一起来看个小例子:

先在网上找个图案,用PS去掉周围部分,主体改为纯色;



当我们使用PS里的魔棒创立选区的时候,发现一次只选取了其中最相近的一部分,这时候可以看到容差为5,但我们的目的是想把图形周围近似的蓝灰色都选中该怎么办呢?容差这时候就起作用了,改大容差,就相当于调大近似度,这个概念和我们里面的容差值是一样的,希望这样说便于理解,我们看下对比图,调到50 一下就可以选中外层所有蓝灰色:




好,我们现在有了一个纯色的图标,假定我们现在有一个需求,需要在某种操作下将图标变色,类似微信底部的tab图标,未选中时时白色,选中时是绿色,这个时候我们就可以用

AvoidXfermode 进行实现,我们一起看看要怎么做:

1. 给paint 设置要变换的颜色和图层混合模式为 AvoidXfermode;

2. 绘制图标;

3. 再绘制对应色块;

由于是对对应颜色进行替换,所以也就形成了图标变色的效果,一起来看看代码:

public class AvoidXfermodeView extends View {    private Paint mBitmapPaint, mAvoidPaint;    private int mTotalWidth, mTotalHeight;    private Bitmap mBitmap;    private int mBitWidth, mBitHeight;    private Rect mOriginSrcRect, mOriginDestRect;    private Rect mAvoidSrcRect, mAvoidDestRect;    private AvoidXfermode mAvoidXfermode;    public AvoidXfermodeView(Context context) {        super(context);        initPaint();        initBitmap();        // 对蓝色相近的颜色进行替换        mAvoidXfermode = new AvoidXfermode(Color.BLUE, 150, Mode.TARGET);    }    private void initBitmap() {        mBitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.bluelogo)).getBitmap();        mBitWidth = mBitmap.getWidth();        mBitHeight = mBitmap.getHeight();    }    private void initPaint() {        mBitmapPaint = new Paint();        // 去锯齿        mBitmapPaint.setAntiAlias(true);        // 防抖动        mBitmapPaint.setDither(true);        // 图像过滤        mBitmapPaint.setFilterBitmap(true);        // 使用上面属性创建一个新paint        mAvoidPaint = new Paint(mBitmapPaint);        // 颜色设置为红色        mAvoidPaint.setColor(Color.RED);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制原图        canvas.drawBitmap(mBitmap, mOriginSrcRect, mOriginDestRect, mBitmapPaint);        // 绘制用于变色图        canvas.drawBitmap(mBitmap, mAvoidSrcRect, mAvoidDestRect, mAvoidPaint);        // 设置图层混合模式        mAvoidPaint.setXfermode(mAvoidXfermode);        // 绘制色块进行混合,得到最终效果        canvas.drawRect(mAvoidDestRect, mAvoidPaint);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mTotalWidth = w;        mTotalHeight = h;        mOriginSrcRect = new Rect(0, 0, mBitWidth, mBitHeight);        // 为了让图水平居中        int left = (mTotalWidth - mBitWidth) / 2;        mOriginDestRect = new Rect(left, 0, left + mBitWidth, mBitHeight);        mAvoidSrcRect = new Rect(mOriginSrcRect);        // 两张图得间距        int distance = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,                getResources().getDisplayMetrics());        mAvoidDestRect = new Rect(left, mBitHeight + distance, left + mBitWidth, mBitHeight * 2                + distance);    }}


一起来看看效果:



我擦,什么情况,逗我玩儿呢?这时候大家别忘了,前面说过这方法已经在API 16过时了,要在高版本用的话需要关掉硬件加速,什么?硬件加速怎么关...

硬件加速分全局(Application)、Activity、Window、View 四个层级,也简单提及一下:

1.在AndroidManifest.xml文件为application标签添加如下的属性即可为整个应用程序开启硬件加速:

<application android:hardwareAccelerated="true" ...>

2.在Activity 标签下使用 hardwareAccelerated 属性开启或关闭硬件加速:

<activity android:hardwareAccelerated="false" />
3. 在Window 层级使用如下代码开启硬件加速:

getWindow().setFlags(    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
4.View 级别如下关闭硬件加速,view 层级上没法单独开启硬件加速:

setLayerType(View.LAYER_TYPE_SOFTWARE, null);
好了,原来如此,咱们直接在全局application 关闭硬件加速:

<application        android:allowBackup="true"        android:hardwareAccelerated="false"---

再看下效果:


我们可以看到,现在蓝花已经变成红花了,已经实现了前面说的图标变色小需求,这时候有人可能会说了,这东西还有点用,如果我要在进行某些操作的时候让图标进行颜色的平滑过渡,该怎么办呢?比如viewPager从一页滑到另一页的时候,图标颜色渐变过渡,其实这样的需求只需要在上面的基础上稍加改动即可,提供下思路:

eg:view层级对外提供个接口,传入渐变比例,view里根据比例计算出当前色值,然后实时的把paint的色值更新,重新绘制即可!

好了,AvoidXfermode 就讲这些!

2.PixelXorXfermode

/** * PixelXorXfermode implements a simple pixel xor (op ^ src ^ dst). * This transformation does not follow premultiplied conventions, therefore * this mode *always* returns an opaque color (alpha == 255). Thus it is * not really usefull for operating on blended colors. */@Deprecatedpublic class PixelXorXfermode extends Xfermode {    public PixelXorXfermode(int opColor) {        native_instance = nativeCreate(opColor);    }    private static native int nativeCreate(int opColor);}

从上面的介绍上讲,这只是一个简单异或运算,这种变换不满足预乘公约,因此会总是返回一个不透明的颜色,所以对操作颜色混合不是特别的有效;

基于以上说明和这个类已经过时,讲解到此结束;

3.PorterDuffXfermode

这个类是setXfermode 方法的核心,也是图层混合模式里的核武器,通过它再加上我们的想象力,就能解决图形图像绘制里的很多问题,首先,还是看下方法说明:

    /**     * Create an xfermode that uses the specified porter-duff mode.     *     * @param mode           The porter-duff mode that is applied     */    public PorterDuffXfermode(PorterDuff.Mode mode) {        this.mode = mode;        native_instance = nativeCreateXfermode(mode.nativeInt);    }

使用 PorterDuff 模式创建一个图层混合模式,如此说来,我们的重心就转移到了 PorterDuff 身上;

PorterDuff 是啥,居然没法翻译,我靠,百思不得其姐,如果对图形图像学有所了解,肯定会知道,PorterDuff 实则是两个人名的的组合Thomas Porter 和 Tom Duff ,PorterDuff则是用于描述数字图像合成的基本手法,通过组合使用 Porter-Duff 操作,可完成任意 2D图像的合成;

听起来好像很屌炸天的样子,接下来我们一起来逐个看下这些模式:

  public class PorterDuff {    // these value must match their native equivalents. See SkPorterDuff.h    public enum Mode {        /** [0, 0] */        CLEAR       (0),        /** [Sa, Sc] */        SRC         (1),        /** [Da, Dc] */        DST         (2),        /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */        SRC_OVER    (3),        /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */        DST_OVER    (4),        /** [Sa * Da, Sc * Da] */        SRC_IN      (5),        /** [Sa * Da, Sa * Dc] */        DST_IN      (6),        /** [Sa * (1 - Da), Sc * (1 - Da)] */        SRC_OUT     (7),        /** [Da * (1 - Sa), Dc * (1 - Sa)] */        DST_OUT     (8),        /** [Da, Sc * Da + (1 - Sa) * Dc] */        SRC_ATOP    (9),        /** [Sa, Sa * Dc + Sc * (1 - Da)] */        DST_ATOP    (10),        /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */        XOR         (11),        /** [Sa + Da - Sa*Da,             Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */        DARKEN      (12),        /** [Sa + Da - Sa*Da,             Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */        LIGHTEN     (13),        /** [Sa * Da, Sc * Dc] */        MULTIPLY    (14),        /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */        SCREEN      (15),        /** Saturate(S + D) */        ADD         (16),        OVERLAY     (17);        Mode(int nativeInt) {            this.nativeInt = nativeInt;        }        /**         * @hide         */        public final int nativeInt;    }


可以看到里面通过枚举enum定义了18种混合模式,并且每种模式都写出了对应的计算方法:

其中 Sa 代表source alpha ,即源 alpha 值 ,Da 代表 Destination alpha ,即 目标alpha值 ,Sc 代表 source color ,即源色值 ,Dc 代表 Destination color ,即目标色值,并且这所有的计算都以像素为单位,在某一种混合模式下,对每一个像素的alpha 和 color 通过对应算法进行运算,得出新的像素值,进行展示;

接下来写个view 逐个对以上模式进行测试:

public class PorterDuffXfermodeView extends View {    private Paint mPaint;    private Bitmap mBottomBitmap, mTopBitmap;    private Rect mBottomSrcRect, mBottomDestRect;    private Rect mTopSrcRect, mTopDestRect;    private Xfermode mPorterDuffXfermode;    // 图层混合模式    private PorterDuff.Mode mPorterDuffMode;    // 总宽高    private int mTotalWidth, mTotalHeight;    private Resources mResources;    public PorterDuffXfermodeView(Context context) {        super(context);        mResources = getResources();        initBitmap();        initPaint();        initXfermode();    }    // 初始化bitmap    private void initBitmap() {        mBottomBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.blue)).getBitmap();        mTopBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.red)).getBitmap();    }    // 初始化混合模式    private void initXfermode() {        mPorterDuffMode = PorterDuff.Mode.DST;        mPorterDuffXfermode = new PorterDuffXfermode(mPorterDuffMode);    }    private void initPaint() {        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 背景铺白        canvas.drawColor(Color.WHITE);        // 保存为单独的层        int saveCount = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, mPaint,                Canvas.ALL_SAVE_FLAG);        // 绘制目标图        canvas.drawBitmap(mBottomBitmap, mBottomSrcRect, mBottomDestRect, mPaint);        // 设置混合模式        mPaint.setXfermode(mPorterDuffXfermode);        // 绘制源图        canvas.drawBitmap(mTopBitmap, mTopSrcRect, mTopDestRect, mPaint);        mPaint.setXfermode(null);        canvas.restoreToCount(saveCount);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mTotalWidth = w;        mTotalHeight = h;        int halfHeight = h / 2;        mBottomSrcRect = new Rect(0, 0, mBottomBitmap.getWidth(), mBottomBitmap.getHeight());        // 矩形只画屏幕一半        mBottomDestRect = new Rect(0, 0, mTotalWidth, halfHeight);        mTopSrcRect = new Rect(0, 0, mTopBitmap.getWidth(), mTopBitmap.getHeight());        mTopDestRect = new Rect(0, 0, mTotalWidth, mTotalHeight);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }}

原图如下:



蓝色上下分别为完全不透明和半透区,红色左右为半透和完全不透明区,这样绘制时可以让所有的情况进行交叉,便于分析,先看下默认绘制效果:



(1). CLEAR

清除模式[0,0],即最终所有点的像素的alpha 和color 都为 0,所以画出来的效果只有白色背景:



(2). SRC -[Sa , Sc]

只保留源图像的 alpha 和 color ,所以绘制出来只有源图,有时候会感觉分不清先绘制的是源图还是后绘制的是源图,这个时候可以这么记,先绘制的是目标图,不管任何时候,一定要做一个有目标的人,目标在前!

这个时候绘制的情形是只有屏幕上的红色圆;


(3). DST -[ Da, Dc ]

同上类比,只保留目标图像的 alpha 和 color,所以绘制出来的只有目标图,也就是屏幕上的的蓝色图;



(4).SRC_OVER - [ Sa +(1-Sa) * Da , Rc = Sc +( 1- Sa ) * Dc]

在目标图片顶部绘制源图像,从命名上也可以看出来就是把源图像绘制在上方;



(5). DST_OVER - [Sa + ( 1 - Sa ) * Da ,Rc = Dc + ( 1 - Da ) * Sc ]

可以和 SRC_OVER 进行类比,将目标图像绘制在上方,这时候就会看到先绘制的蓝色盖在了红色圆的上面;


(6). SRC_IN - [ Sa * Da , Sc * Da ]

在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响;


(7). DST_IN - [ Sa * Da , Sa * Dc ]

可以和SRC_IN 进行类比,在两者相交的地方绘制目标图像,并且绘制的效果会受到源图像对应地方透明度的影响;


(8). SRC_OUT - [ Sa * ( 1 - Da ) , Sc * ( 1 - Da ) ]

从字面上可以理解为 在不相交的地方绘制 源图像,那么我们来看看效果是不是这样;


这个效果似乎和上面说的不大一样,这个时候我们回归本源[ Sa * ( 1 - Da ) , Sc * ( 1 - Da ) ] ,从公式里可以看到 对应处的 color 是 Sc * ( 1 - Da ) ,如果相交处的目标色的alpha是完全不透明的,这时候源图像会完全被过滤掉,否则会受到相交处目标色 alpha 影响,呈现出对应色值,如果还有问题,大家可以对比上图和普通叠加图,再参考下上面公式理解;

所以该模式总结一下应该是:在不相交的地方绘制源图像,相交处根据目标alpha进行过滤,目标色完全不透明时则完全过滤,完全透明则不过滤;

(9). DST_OUT - [ Da * ( 1 - Sa ) , Dc * ( 1 - Sa ) ]

同样,可以类比SRC_OUT , 在不相交的地方绘制目标图像,相交处根据源图像alpha进行过滤,完全不透明处则完全过滤,完全透明则不过滤;


(10). SRC_ATOP - [ Da , Sc * Da + ( 1 - Sa ) * Dc ]

源图像和目标图像相交处绘制源图像,不相交的地方绘制目标图像,并且相交处的效果会受到源图像和目标图像alpha的影响;



(11). DST_ATOP - [ Sa , Sa * Dc + Sc * ( 1 - Da ) ]

源图像和目标图像相交处绘制目标图像,不相交的地方绘制源图像,并且相交处的效果会受到源图像和目标图像alpha的影响;




(12). XOR - [ Sa + Da - 2 * Sa * Da, Sc * ( 1 - Da ) + ( 1 - Sa ) * Dc ]

在不相交的地方按原样绘制源图像和目标图像,相交的地方受到对应alpha和色值影响,按上面公式进行计算,如果都完全不透明则相交处完全不绘制;




(13). DARKEN - [ Sa + Da - Sa * Da , Sc * ( 1 - Da ) + Dc * ( 1 - Sa ) + min(Sc , Dc) ]

该模式处理过后,会感觉效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合;

从算法上看,alpha值变大,色值上如果都不透明则取较暗值,非完全不透明情况下使用上面算法进行计算,受到源图和目标图对应色值和alpha值影响;




同样的,这样的混合效果可以直接在PS里进行简单模拟,创建三个一样的图层,选择对应的混合模式,对于效果表示是一致的:




(14). LIGHTEN - [ Sa + Da - Sa * Da , Sc * ( 1 -Da ) + Dc * ( 1 - Sa ) + max ( Sc , Dc ) ]

可以和 DARKEN 对比起来看,DARKEN 的目的是变暗,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下 ,色值取源色值和目标色值中的较大值,否则按上面算法进行计算;



(15). MULTIPLY - [ Sa * Da , Sc * Dc ]

正片叠底,即查看每个通道中的颜色信息,并将基色与混合色复合。结果色总是较暗的颜色。任何颜色与黑色复合产生黑色。任何颜色与白色复合保持不变。当用黑色或白色以外的颜色绘画时,绘画工具绘制的连续描边产生逐渐变暗的颜色。




(16). SCREEN - [ Sa + Da - Sa * Da , Sc + Dc - Sc * Dc ]

滤色,滤色模式与我们所用的显示屏原理相同,所以也有版本把它翻译成“屏幕”;

简单的说就是保留两个图层中较白的部分,较暗的部分被遮盖;

当一层使用了滤色(屏幕)模式时,图层中纯黑的部分变成完全透明,纯白部分完全不透明,其他的颜色根据颜色级别产生半透明的效果;


(17). ADD - [ Saturate( S+ D ) ]

饱和度叠加
(18). OVERLAY - 叠加
像素是进行 Multiply (正片叠底)混合还是 Screen (屏幕)混合,取决于底层颜色,但底层颜色的高光与阴影部分的亮度细节会被保留;
以上就是android 提供的所有的混合模式,有了这,只要是图像与图像任何形式混合能创造的效果,我们就都能够进行实现,这时候我们再看看混合模式的示意图


前面我们用AvoidXfermode实现的图标变色小栗子也同样可以使用PorterDuffXfermode进行实现,大家可以自己尝试;


最后,我们利用混合模式完成两个小栗子:

一、还算比较流行的一种 loading 形式:

先看下效果:


这个效果如果不知道图像的混合模式,做的话可能会采用2张图,对上面的图进行裁切,但自从有了混合模式,这一切都将变的简单;

我们只需要如下几步:

1.绘制目标图;

2.设置混合模式;

3.绘制带颜色的Rect;

4.不断改变Rect的区域;

代码如下:

public class PoterDuffLoadingView extends View {    private Resources mResources;    private Paint mBitPaint;    private Bitmap mBitmap;    private int mTotalWidth, mTotalHeight;    private int mBitWidth, mBitHeight;    private Rect mSrcRect, mDestRect;    private PorterDuffXfermode mXfermode;    private Rect mDynamicRect;    private int mCurrentTop;    private int mStart, mEnd;    public PoterDuffLoadingView(Context context) {        super(context);        mResources = getResources();        initBitmap();        initPaint();        initXfermode();    }    private void initXfermode() {        // 叠加处绘制源图        mXfermode = new PorterDuffXfermode(Mode.SRC_IN);    }    private void initPaint() {        // 初始化paint        mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mBitPaint.setFilterBitmap(true);        mBitPaint.setDither(true);        mBitPaint.setColor(Color.RED);    }    private void initBitmap() {        // 初始化bitmap        mBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.ga_studio))                .getBitmap();        mBitWidth = mBitmap.getWidth();        mBitHeight = mBitmap.getHeight();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 存为新图层        int saveLayerCount = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, mBitPaint,                Canvas.ALL_SAVE_FLAG);        // 绘制目标图        canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitPaint);        // 设置混合模式        mBitPaint.setXfermode(mXfermode);        // 绘制源图形        canvas.drawRect(mDynamicRect, mBitPaint);        // 清除混合模式        mBitPaint.setXfermode(null);        // 恢复保存的图层;        canvas.restoreToCount(saveLayerCount);        // 改变Rect区域,真实情况下时提供接口传入进度,计算高度        mCurrentTop -= 8;        if (mCurrentTop <= mEnd) {            mCurrentTop = mStart;        }        mDynamicRect.top = mCurrentTop;        postInvalidate();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mTotalWidth = w;        mTotalHeight = h;        mSrcRect = new Rect(0, 0, mBitWidth, mBitHeight);        // 让左边和上边有些距离        int left = (int) TypedValue.complexToDimension(20, mResources.getDisplayMetrics());        mDestRect = new Rect(left, left, left + mBitWidth, left + mBitHeight);        mStart = left + mBitHeight;        mCurrentTop = mStart;        mEnd = left;        mDynamicRect = new Rect(left, mStart, left + mBitWidth, left + mBitHeight);    }}

二、采用混合模式过滤形状,实现水波纹:



文章链接 -自定义view实现水波纹效果

CSDN 源码下载地址:http://download.csdn.net/detail/tianjian4592/8571355









更多相关文章

  1. Android(安卓)平滑和立体翻页效果2
  2. Android(安卓)SDK sample 之 SoftKeyboard 详解
  3. Android绘制流程窗口启动流程分析(中)
  4. RecyclerView Divider完美解决方案
  5. 【Android(安卓)Studio】ParseError at [row,col]:[2,6] Message
  6. Android的UI显示原理之Surface的渲染
  7. Android(安卓)自定义View实现带进度的下载按钮
  8. Android通过自定义View实现心形(贝塞尔曲线)
  9. 一步一步学android OpenGL ES2.0编程(1)

随机推荐

  1. android LinearLayout布局子空间没有填充
  2. android SDK与ADT版本更新问题
  3. android String
  4. android 获取控件真实高度
  5. Android使用XPush配置极光推送
  6. android启动画面
  7. Android预定义样式
  8. 1.8 Android(安卓)SQLite数据库
  9. [置顶] Android(安卓)View系统学习文章汇
  10. Android(安卓)build system