自定义控件三部曲之绘图篇(十七)——为Bitmap添加阴影并封装控件

前言:再重复一遍我很喜欢的一句话,送给大家:迷茫,本就是青春该有的样子 ,但不要让未来的你,讨厌现在的自己

上篇给大家讲解了如何控件添加阴影效果,但是在为Bitmap图片添加阴影时,却没办法添加具有指定颜色的阴影,这篇我们就来使用自定义的控件及自定义属性来初步封装下控件。前方高能预警——本篇内容涉及内容较多,难度较大,需要多加思考。

一、使用BlurMaskFilter为图片构造定色阴影效果

上面我们讲了通过setShadowLayer为图片添加阴影效果,但是图片的的阴影是用原图形的副本加上边缘发光效果组成的。我们怎么能给图片添加一个灰色的阴影呢?

我们来分析一下setShadowLayer的阴影形成过程(假定阴影画笔是灰色),对于文字和图形,它首先产生一个跟原型一样的灰色副本。然后对这个灰色副本应用BlurMaskFilter,使其内外发光;这样就形成了所谓的阴影!当然最后再偏移一段距离。

所以,我们要给图片添加灰色阴影效果,所以我们能不能仿一下这个过程:先绘制一个跟图片一样大小的灰色图形,然后给这个灰色图形应用BlurMaskFilter使其内外发光,然后偏移原图形绘制出来,不就可以了么

所以,这里涉及到三个点:

  • 绘制出一个一样大小的灰色图形
  • 对灰色图形应用BlurMaskFilter使其内外发光
  • 偏移原图形一段距离绘制出来

下面我们就通过例子来一步步看是怎么实现出来的吧

1、绘制出一个一样大小的灰色图像

首先,我们来看怎么能绘出一个指定Bitmap所对应的灰色图像。我们知道canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)中的paint的画笔颜色对画出来的bitmap是没有任何影响的,因为原来Bitmap长什么样,无论你画笔是什么颜色,画出来的图片还是跟原图片长的一样。所以如果我们需要画一张对应的灰色图像,我们需要新建一个一样大小的空白图,但是新图片的透明度要与原图片保持一致。所以如何从原图片中抽出Alpha值成为了关键。即我们只需要创建一个与原图片一样大小且Alpha相同的图片即可。

其实Bitmap中已经存在抽取出只具有Alpha值图片的函数:

public Bitmap extractAlpha();

extraAlpha()函数的功能是:新建一张空白图片,图片具有与原图片一样的Alpha值,这个新建的Bitmap做为结果返回。这个空白图片中每个像素都具有与原图片一样的Alpha值,而且具体的颜色时,只有在使用canvas.drawBitmap绘制时,由传入的paint的颜色指定。

所以总结来讲:

  • extractAlpha()新建一张仅具有Alpha值的空白图像
  • 这张图像的颜色,是由canvas.drawBitmap时的画笔指定的。

(1)、extractAlpha()使用示例

下面,我们就用个例子先来看下extractAlpha()函数的用法

我们拿一张图片来做试验,下面这张PNG图片中,一只小猫和一只小狗,其余地方都是透明色。

下面我们分别利用extractAlpha()画出它对应的红色和绿色的阴影图

对应的代码为:

public class ExtractAlphaView extends View {
    private Paint mPaint;
    private Bitmap mBitmap,mAlphaBmp;
    public ExtractAlphaView(Context context) {
        super(context);
        init();
    }

    public ExtractAlphaView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ExtractAlphaView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.blog12);
        mAlphaBmp = mBitmap.extractAlpha();
    }

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

        int width = 200;
        int height = width * mAlphaBmp.getHeight()/mAlphaBmp.getWidth();
         mPaint.setColor(Color.RED);
        canvas.drawBitmap(mAlphaBmp,null,new Rect(0,0,width,height),mPaint);

        mPaint.setColor(Color.GREEN);
        canvas.drawBitmap(mAlphaBmp,null,new Rect(0,height,width,2*height),mPaint);
    }
}

首先看init函数:

private void init(){
    setLayerType(LAYER_TYPE_SOFTWARE,null);
    mPaint = new Paint();
    mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.blog12);
    mAlphaBmp = mBitmap.extractAlpha();
}

首先是禁用硬件加速,这基本上是我们做自定义控件的标配!为了防止功能不好用,记得每次都加上这个函数!然后是利用extratAlpha()来生成仅具有透明度的空白图像。

最后看OnDraw函数:

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

    int width = 200;
    int height = width * mAlphaBmp.getHeight()/mAlphaBmp.getWidth();
    mPaint.setColor(Color.RED);
    canvas.drawBitmap(mAlphaBmp,null,new Rect(0,0,width,height),mPaint);

    mPaint.setColor(Color.GREEN);
    canvas.drawBitmap(mAlphaBmp,null,new Rect(0,height,width,2*height),mPaint);
}

这里分别将画笔的颜色设置为红色和绿色,然后两次把mAlphaBmp画出来。上面我们已经提到,在画仅具有透明度的空白图像时,图像的颜色是由画笔颜色指定的。所以从效果图中也可以看出画出来的图像分别红色的绿色的。

这就是Bitmpa.extraAlpha()的用法!

2、对灰色图形应用BlurMaskFilter使其内外发光

在第一步完成了之后,我们进行第二步,将阴影添加内外发光效果。就形成了阴影的模样。

代码很简单,只需要使用Paint.setMaskFilter函数添加发光效果即可,代码如下:

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

    int width = 200;
    int height = width * mAlphaBmp.getHeight()/mAlphaBmp.getWidth();
    mPaint.setColor(Color.RED);
    mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));
    canvas.drawBitmap(mAlphaBmp,null,new Rect(0,0,width,height),mPaint);

    mPaint.setColor(Color.GREEN);
    canvas.drawBitmap(mAlphaBmp,null,new Rect(0,height,width,2*height),mPaint);
}

明显可以看出这里只添加了一行代码:mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));就是添加内外发光效果,难度不大,不再细讲。

3、偏移原图形一段距离绘制出来

这段比较简单了,只需要先把阴影画出来,然后再把原图像盖上去,但需要注意的是,阴影需要相对原图像偏移一段距离。完整代码如下:

public class ExtractAlphaView extends View {
    private Paint mPaint;
    private Bitmap mBitmap,mAlphaBmp;
    public ExtractAlphaView(Context context) {
        super(context);
        init();
    }

    public ExtractAlphaView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ExtractAlphaView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.blog12);
        mAlphaBmp = mBitmap.extractAlpha();
    }

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

        int width = 200;
        int height = width * mAlphaBmp.getHeight()/mAlphaBmp.getWidth();

        //绘制阴影
        mPaint.setColor(Color.RED);
        mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));
        canvas.drawBitmap(mAlphaBmp,null,new Rect(10,10,width,height),mPaint);
        mPaint.setColor(Color.GREEN);
        canvas.drawBitmap(mAlphaBmp,null,new Rect(10,height+10,width,2*height),mPaint);

        //绘制原图像
        mPaint.setMaskFilter(null);
        canvas.drawBitmap(mBitmap,null,new Rect(0,0,width,height),mPaint);
        canvas.drawBitmap(mBitmap,null,new Rect(0,height,width,2*height),mPaint);
    }
}

关键部分在onDraw函数中,先画出来阴影,然后再画出来原图像,需要注意的是在画原图像时,需要利用mPaint.setMaskFilter(null);将发光效果去掉。只有阴影需要发光效果,原图像是不需要发光效果的。另一点注意的是,阴影要偏移一点位置,这里是偏移了10个像素。

效果图如下:

二、封装控件

上面我们初步实现了图片的阴影效果,但这只是本篇内容的一小部分,最最重要的,如何将它封装成一个控件,具有如下功能:

  • 让用户定义图片内容
  • 让用户定义偏移距离
  • 让用户定义阴影颜色和阴影模糊程度
  • 可以使用wrap_content属性自适应大小

1、自定义控件属性

有关自定义控件属性,大家首先需要看下这篇文章《PullScrollView详解(一)——自定义控件属性》,在这篇文章中讲解了自定义控件属性的方法与提取方法。下面将会直接用到自定义属性的内容,下面涉及到的时候就自认为大家已经学会了自定义控件属性的方法了。

在这里,我们需要自定义四个属性,分别对应: 自定义图片内容、自定义偏移距离、自定义阴影颜色、自定义阴影模糊程度 这四个需求,所以我们先利用declare-styleable标签来定义这些属性

attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="BitmapShadowView">
        <attr name="src" format="reference" />
        <attr name="shadowDx" format="integer" />
        <attr name="shadowDy" format="integer" />
        <attr name="shadowColor" format="color"/>
        <attr name="shadowRadius" format="float"/>
    </declare-styleable>
</resources>

这里定义了五个xml属性,src来引用图片资源,仿照setShadowLayer另外定义shadowDx、shadowDy、shadowColor、shadowRadius来定义阴影的边距、颜色和模糊半径。

然后在布局中使用:(main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res/com.harvic.BlogBitmapShadow"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
    <com.harvic.BlogBitmapShadow.BitmapShadowView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center_horizontal"
            app:src="@drawable/blog12"
            app:shadowDx="10"
            app:shadowDy="10"
            app:shadowRadius="10.0"
            app:shadowColor="@android:color/holo_red_dark"/>
</LinearLayout>

在布局中使用很简单,直接定义控件所使用的图片资源、阴影相关参数就可以了,难度不大就不再讲了,下面我们来看如何在代码中中提取用户传入的这些属性。

BitmapShadowView中提取属性值并绘阴影

先列出完整代码,然后再细讲:

public class BitmapShadowView extends View {
    private Paint mPaint;
    private Bitmap mBmp,mShadowBmp;
    private int mDx = 10,mDy = 10;
    private float mRadius = 0;
    private int mShadowColor;

    public BitmapShadowView(Context context, AttributeSet attrs) throws Exception{
        super(context, attrs);

        init(context,attrs);
    }

    public BitmapShadowView(Context context, AttributeSet attrs, int defStyle) throws Exception{
        super(context, attrs, defStyle);
        init(context,attrs);
    }

    private void init(Context context,AttributeSet attrs) throws Exception {
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        /**
         * 提取属性定义
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.BitmapShadowView);
        int BitmapID = typedArray.getResourceId(R.styleable.BitmapShadowView_src,-1);
        if (BitmapID == -1){
            throw new Exception("BitmapShadowView 需要定义Src属性,而且必须是图像");
        }
        mBmp = BitmapFactory.decodeResource(getResources(),BitmapID);
        mDx = typedArray.getInt(R.styleable.BitmapShadowView_shadowDx,0);
        mDy = typedArray.getInt(R.styleable.BitmapShadowView_shadowDy,0);
        mRadius = typedArray.getFloat(R.styleable.BitmapShadowView_shadowRadius,0);
        mShadowColor = typedArray.getColor(R.styleable.BitmapShadowView_shadowColor,Color.BLACK);

        typedArray.recycle();
        /**
         * 其它定义
         */
        mPaint = new Paint();
        mShadowBmp = mBmp.extractAlpha();

    }

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

        int width = getWidth()-mDx;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        //绘制阴影
        mPaint.setColor(mShadowColor);
        mPaint.setMaskFilter(new BlurMaskFilter(mRadius, BlurMaskFilter.Blur.NORMAL));
        canvas.drawBitmap(mShadowBmp,null,new Rect(mDx,mDy,width,height),mPaint);

        //绘制原图像
        mPaint.setMaskFilter(null);
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }
}

在这段代码中分两部分,首先根据属性来初始化各变量,然后再利用这些变量画出bitmap与阴影。

首先看初始化部分:

private void init(Context context,AttributeSet attrs) throws Exception {
    setLayerType(LAYER_TYPE_SOFTWARE,null);
    /**
     * 提取属性定义
     */
    TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.BitmapShadowView);
    int BitmapID = typedArray.getResourceId(R.styleable.BitmapShadowView_src,-1);
    if (BitmapID == -1){
        throw new Exception("BitmapShadowView 需要定义Src属性,而且必须是图像");
    }
    mBmp = BitmapFactory.decodeResource(getResources(),BitmapID);
    mDx = typedArray.getInt(R.styleable.BitmapShadowView_shadowDx,0);
    mDy = typedArray.getInt(R.styleable.BitmapShadowView_shadowDy,0);
    mRadius = typedArray.getFloat(R.styleable.BitmapShadowView_shadowRadius,0);
    mShadowColor = typedArray.getColor(R.styleable.BitmapShadowView_shadowColor,Color.BLACK);

    typedArray.recycle();
    /**
     * 其它定义
     */
    mPaint = new Paint();
    mShadowBmp = mBmp.extractAlpha();
}

初始化的时候,首先是利用TypedArray来初始化各项参数,由于我们是做图片的阴影,所以图片资源必须赋值,所以我们在提取图片资源时,对其添加容错:

int BitmapID = typedArray.getResourceId(R.styleable.BitmapShadowView_src,-1);
if (BitmapID == -1){
    throw new Exception("BitmapShadowView 需要定义Src属性,而且必须是图像");
}

当提取失败时,抛出异常,终止程序,这样用户在写代码时就可以及时发现问题,而不必等上线以后才发现没有bitmap;

有关其它属性值的提取,这里就不再细讲了。

在提取完属性以后,就是定义画笔paint和根据源图像利用extractAlpha()来生成阴影图像;

在初始化以后就是利用这些属性来进行绘图了:

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

    int width = getWidth()-mDx;
    int height = width * mBmp.getHeight()/mBmp.getWidth();

    //绘制阴影
    mPaint.setColor(mShadowColor);
    mPaint.setMaskFilter(new BlurMaskFilter(mRadius, BlurMaskFilter.Blur.NORMAL));
    canvas.drawBitmap(mShadowBmp,null,new Rect(mDx,mDy,width,height),mPaint);

    //绘制原图像
    mPaint.setMaskFilter(null);
    canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}

首先,图片宽度与控件宽度操持一致(但需要把阴影的位置空出来),所以宽度为:int width = getWidth()-mDx

根据图片的宽高比换算出图片的高度:int height = width * mBmp.getHeight()/mBmp.getWidth()

我们依控件左上角(0,0)显示原图像,阴影在(mDx,mDy)处显示;

到这里自定义属性的定义与提取就结束了,最终效果图为:

从效果图中可以明显看出,明显给原图片添加了红色的阴影效果。

目前,我们初步实现了可以让用户自定义控件属性的功能,但我们在使用这个控件时,必须强制设置指定的宽高或者fill_parent来强制平屏,这样明显是不可取的,我们需要它能够让用户使用wrap_conetent时,自己计算宽高;

2、wrap_content自适应宽高

在自适应宽高时,需要了解onMeasure()、onLayout()与onDraw()的知识,以前在写FlowLayout时,已经单独写过一篇:《FlowLayout详解(一)——onMeasure()与onLayout()》,这里就不再细讲onMeasure()的原理了,如果不理解onMeasure用法的同学需要提前把这篇文章看完再回来;在第三篇中我还会重新讲解一遍onMeasure()、onLayout()与onDraw(),这里涉及内容不多,看完上一篇然后再理解以下内容应该不会有问题

在看完上面的文章,大家就应该知道,对于View控件的自适应宽高,只需要在上面的代码中重写onMeasure()方法就可以了:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);

   int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
   int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
   int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
   int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);

   int width = mBmp.getWidth();
   int height = mBmp.getHeight();
   setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth: width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight: height);
}

在onMeasure方法中,当用户指定属性是wrap_content时,就使用图片的宽高做为控件的宽高。

此时整个自定义控件的完整代码为:

public class BitmapShadowView extends View {
    private Paint mPaint;
    private Bitmap mBmp,mShadowBmp;
    private int mDx = 10,mDy = 10;
    private float mRadius = 0;
    private int mShadowColor;

    public BitmapShadowView(Context context, AttributeSet attrs) throws Exception{
        super(context, attrs);

        init(context,attrs);
    }

    public BitmapShadowView(Context context, AttributeSet attrs, int defStyle) throws Exception{
        super(context, attrs, defStyle);
        init(context,attrs);
    }

    private void init(Context context,AttributeSet attrs) throws Exception {
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        /**
         * 提取属性定义
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.BitmapShadowView);
        int BitmapID = typedArray.getResourceId(R.styleable.BitmapShadowView_src,-1);
        if (BitmapID == -1){
            throw new Exception("BitmapShadowView 需要定义Src属性,而且必须是图像");
        }
        mBmp = BitmapFactory.decodeResource(getResources(),BitmapID);
        mDx = typedArray.getInt(R.styleable.BitmapShadowView_shadowDx,0);
        mDy = typedArray.getInt(R.styleable.BitmapShadowView_shadowDy,0);
        mRadius = typedArray.getFloat(R.styleable.BitmapShadowView_shadowRadius,0);
        mShadowColor = typedArray.getColor(R.styleable.BitmapShadowView_shadowColor,Color.BLACK);

        typedArray.recycle();
        /**
         * 其它定义
         */
        mPaint = new Paint();
        mShadowBmp = mBmp.extractAlpha();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);

        int width = mBmp.getWidth();
        int height = mBmp.getHeight();
        setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth: width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight: height);
    }

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

        int width = getWidth()-mDx;
        int height = width * mBmp.getHeight()/mBmp.getWidth();

        //绘制阴影
        mPaint.setColor(mShadowColor);
        mPaint.setMaskFilter(new BlurMaskFilter(mRadius, BlurMaskFilter.Blur.NORMAL));
        canvas.drawBitmap(mShadowBmp,null,new Rect(mDx,mDy,width,height),mPaint);

        //绘制原图像
        mPaint.setMaskFilter(null);
        canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
    }
}

所以当我们对这个自定义的控件使用如下布局(使用wrap_content):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res/com.harvic.BlogBitmapShadow"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
    <com.harvic.BlogBitmapShadow.BitmapShadowView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            app:src="@drawable/blog12"
            app:shadowDx="10"
            app:shadowDy="10"
            app:shadowRadius="10.0"
            app:shadowColor="@android:color/holo_red_dark"/>

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="test BTN"/>
</LinearLayout>

效果图如下:

所以,这时候如果我们需要产生灰色阴影,只需要把xml中的app:shadowColor的值改一下即可:(为了方便看阴影,我把Activiy背景改成了白色)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res/com.harvic.BlogBitmapShadow"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:background="@android:color/white">

    <com.harvic.BlogBitmapShadow.BitmapShadowView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            app:src="@drawable/blog12"
            app:shadowDx="10"
            app:shadowDy="10"
            app:shadowRadius="10.0"
            app:shadowColor="@android:color/darker_gray"/>

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="test BTN"/>
</LinearLayout>

效果图如下:

到这里,整个控件的封装就结束了,但细心的同学可以发现,BitmapShadowView的构造函数默认有三个,而我这里只写了两个具有AttributeSet attrs参数的,而下面这个构造函数却是没有实现的:

public BitmapShadowView(Context context){
    super(context);
}

因为当从XML中生成控件时,都会调用具有AttributeSet attrs参数的方法,而从代码中生成控件则会调用上面仅具有context函数的方法,所以如果需要从代码中生成需要添加上这个方法,并且需要在代码中提供接口供外部设置各种属性才好,我这里就略去了这部分内容了,大家可以自己来填充这个控件,使其更完整。

好了,整篇文章到这里就结束了,源码在文章底部给出。

如果本文有帮到你,记得加关注哦

源码下载地址:http://download.csdn.net/detail/harvic880925/9573981

转载请标明出处,http://blog.csdn.net/harvic880925/article/details/51889104谢谢

如果你喜欢我的文章,那么你将会更喜欢我的微信公众号,将定期推送博主最新文章与收集干货分享给大家(一周一次)

时间: 2024-12-28 16:02:07

自定义控件三部曲之绘图篇(十七)——为Bitmap添加阴影并封装控件的相关文章

自定义控件三部曲之绘图篇(十四)——Canvas与图层(二)

前言: 有些话说得坦白就会变成感慨有些事总想不开难免有太多无奈这一路走来梦想从未更改相信真心的付出就能够活得精彩把头抬起来拍拍身上的尘埃坚守心底的真爱勇敢相信未来 --巫启贤<相信未来> 系列文章: Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268 一.FLAG的具体意义 1.FLAG概述 有关save系列函数,在canvas中总共有如下几个: public int save() pub

自定义控件三部曲之绘图篇(二十)——RadialGradient与水波纹按钮效果

前言:每当感叹自己的失败时,那我就问你,如果让你重新来一次,你会不会成功?如果会,那说明并没有拼尽全力. 最近博主实在是太忙了,博客更新实在是太慢了,真是有愧大家. 这篇将是Shader的最后一篇,下部分,我们将讲述Canvas变换的知识.在讲完Canvas变换以后,就正式进入第三部曲啦,是不是有点小激动呢-- 今天给大家讲的效果是使用RadialGradient来实现水波纹按钮效果,水波纹效果是Android L平台上自带的效果,这里我们就看看它是如何实现的,本篇的最终效果图如下 一.Radi

自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果

前言:好想义无反顾地追逐梦想 相关文章:<Android自定义控件三部曲文章索引> 从这篇开始,我将延续androidGraphics系列文章把图片相关的知识给大家讲完,这一篇先稍微进阶一下,给大家把<android Graphics(二):路径及文字>略去的quadTo(二阶贝塞尔)函数,给大家补充一下. 本篇最终将以两个例子给大家演示贝塞尔曲线的强大用途: 1.手势轨迹 利用贝塞尔曲线,我们能实现平滑的手势轨迹效果 2.水波纹效果 电池充电时,有些手机会显示水波纹效果,就是这样

自定义控件三部曲之绘图篇(十六)——给控件添加阴影效果与发光效果

前言:要么出击,要么出局,命运女神总会眷顾拼劲全力的一方 相关文章: <Android自定义控件三部曲文章索引>:http://blog.csdn.net/harvic880925/article/details/50995268 这节我们将学到如下内容: 传统地给按钮添加阴影的方法 如何给已有控件添加阴影 如何给图片添加阴影 一.layerlist给按钮添加阴影效果 给控件添加阴影有很多方法,但平常我们给按钮添加阴影最常用的就是使用layerlist多层绘图来添加阴影效果,我们先来看一下给按

自定义控件三部曲之绘图篇(十三)——Canvas与图层(一)

前言:猛然知道姥姥79了,我好怕,好想哭 系列文章: Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268 在给大家讲解了paint的几个方法之后,我觉得有必要插一篇有关Canvas画布的知识,在开始paint之前,我们讲解了canvas绘图的几篇文章和cavas的save().store()的知识,这篇是对Canvas的一个系统的补充,前几篇文章链接如下:<自定义控件之绘图篇(一):概述及基

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

前言: 不应该一路失望 又一路等待 时间它说 世界还有不同的海 但不要告诉我 现实它很坏 我想看看 自己的能耐 --莫文蔚<境外> 系列文章: Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268 一.GPU硬件加速 1.概述 GPU英文全称Graphic Processing Unit,中文翻译为"图形处理器".与CPU不同,GPU是专门为处理图形任务而产生的芯片. 在

自定义控件三部曲之绘图篇(八)——Paint之ColorMatrix

前言:虽然梦想为了现实暂时会妥协,但终有一天,它将会实现 相关文章: <Android自定义控件三部曲文章索引>: http://blog.csdn.net/harvic880925/article/details/50995268 这篇主要讲解ColorMatrix的相关知识,这里将涉及到矩阵乘法的相关知识.所以这篇是比较有难度的. 一.矩阵概述 1.定义 称为m*n矩阵 2.矩阵乘法 矩阵乘法其实并不难,它的意思就是将第一个矩阵A的第一行,与第二个矩阵B的第一列的数字分别相乘,得到的结果相

自定义控件三部曲之动画篇(九)——联合动画的代码实现

前言:为了梦想,行色匆匆,是否会错过眼前的风景?有时也会懊悔,为何当时没能好好享受时光,但如果当时真的跟他人一样,是否现在也会跟他人一样羡慕现在的自己? 相关博客: <Android自定义控件三部曲文章索引> 上几篇给大家分别讲了ValueAnimator和ObjectAnimator,相比而言ObjectAnimator更为方便而且由于set函数是在控件类内部实现,所以封装性更好.而且在现实使用中一般而言都是使用ObjectAnimator的机率比较大. 但ValueAnimator和Obj

自定义控件三部曲之动画篇(一)——alpha、scale、translate、rotate、set的xml属性及用法

前言:这几天做客户回访,感触很大,用户只要是留反馈信息,总是一种恨铁不成钢的心态,想用你的app,却是因为你的技术问题,让他们不得不放弃,而你一个回访电话却让他们尽释前嫌,当最后把手机号留给他们以便随时沟通的时候,总会发来一条条的鼓励短信,让我不自主的开始内疚.哎,多么可爱的用户,多么无耐的现实. 相关文章: <Android自定义控件三部曲文章索引>:http://blog.csdn.net/harvic880925/article/details/50995268 一.概述 Android