android 自定义控件实现qq讨论组头像功能 还没写完

先看下今天最终要实现的效果图:

android 自定义控件实现qq讨论组头像功能 还没写完_第1张图片

这效果是不是很屌,要实现这个效果有2个重要的知识点必须要懂,现在就一一分析要使用到的知识点,然后才讨论这个效果怎么实现,

一:如何获取一个canvas对象

要想获取canvas对象有二种方法

第一种方法:自定义view或者viewGroup然后重写onDraw()或disPatchDraw()方法,这二个方法区别前者是没有子view时绘制使用onDraw(),当在view中药绘制子view时这时候要使用disPatchDraw(),这二个方法都有一个形参就是Canvas对象,而这个onDraw()或者disPathDraw()都是直接通过view调用的,所以直接通过canvas就绘制在view上了,

但是这种方法 除非绘制一些简单,单一的图形使用,复杂点的就不使用这个了,

二:第二种方法,通过创建Bitmap对象

我们知道canvas可以通过自定义view中onDraw()中参数给我们提供的,也可以自己创建一个Canvas,使用如下:

Canvas canvas = new Canvas();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.headshow1);
canvas.setBitmap(bitmap);

canvas设置的bitmap可以来自drawable目录下的资源文件,也可以自己创建一个空的Bitmap,Bitmap api给我们提供了很多种方法创建一个bitmap对象:

android 自定义控件实现qq讨论组头像功能 还没写完_第2张图片

下面对createBitmap()几个重载的方法做一个简单的说明:

public static Bitmap createBitmap(Bitmap src) {} 从原位图src复制出一个新的位图,和原始位图相同
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {}这个通常用在对原始图进行截取,
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter) {}这和上面的用途差不多,只不过了多一个matrix矩阵,通过matirx可以对位图进行旋转等其他操作,
public static Bitmap createBitmap(int width, int height, Config config) {}这是创建一个空的设置了位图的宽和高并可以设置色彩模式,色彩模式一共有如下几种:这个Config类是Bitmap的内部类有四个值分别是:ALPHA_8 代表8位Alpha位图
ARGB_4444 代表16位ARGB位图
ARGB_8888 代表32位ARGB位图
RGB_565 代表8位RGB位图
位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真,但是占内存也叫大 
我们用bitmap构造了一个canvas,哪就相当于canvas这个图层上绘制的图形就保存在这个bitmap上,而不像我们直接使用onDraw()方法中自带的canvas绘制直接就绘制到view上了,但是我们自己创建的canvas如果想画在View上就必须使用OnDraw()函数中传进来的canvas画一遍bitmap才能画到view上,现在我们写个例子来说明下,光文字描述没有那么直观,

package com.example.customcircularavatar.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**  * Created by admin on 2016/5/11.  */ public class CustomView extends View {
    private Paint mPaint;
    private Canvas mCanvas;
    public CustomView(Context context) {
        this(context,null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setColor(Color.parseColor("#88000000"));
        mCanvas = new Canvas();
        Bitmap bitmap = Bitmap.createBitmap(200,200, Bitmap.Config.ALPHA_8);//创建一个宽和高200的空的位图,
        mCanvas.setBitmap(bitmap);//把这个位图放在canvas这个图层上
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setTextSize(60);
        mCanvas.drawText("杭州准备开G20峰会",2000,200,mPaint);
    }
}
效果:

android 自定义控件实现qq讨论组头像功能 还没写完_第3张图片

发现屏幕上什么都不显示,没有绘制出来任何东西,因为绘制就是在onDraw()方法中操作,现在再看下onDraw()方法都写了什么代码:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setTextSize(60);
    mCanvas.drawText("杭州准备开G20峰会",2000,200,mPaint);
}
你会发现调用drawText()方法的不是onDraw()自带的canvas对象,因为你要直接绘制到view上只有canvas跟它view有直接的关系,而使用我们自己创建的Canvas对象跟view毫无关系,所以这就是为什么在view上什么都看不见的原因,如果要在view上看见我们自己创建的canvas图层上所绘制的东西,是不是要把我们创建canvas对象通过onDraw()方法中提供的canvas对象给绘制上去呢?答案是错误的,你去查找发现canvas并没有一个方法把canvas对象当做形参给传递进去,而是通过canvas的drawBitmap()方法把创建的bitmap对象绘制上去,

package com.example.customcircularavatar.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**  * Created by admin on 2016/5/11.  */ public class CustomView extends View {
    private Paint mPaint;
    private Canvas mCanvas;
    private Bitmap mBitmap;
    public CustomView(Context context) {
        this(context,null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setColor(Color.parseColor("#88000000"));
        mCanvas = new Canvas();
        mBitmap = Bitmap.createBitmap(900,900, Bitmap.Config.ALPHA_8);//创建一个宽和高600的空的位图,
        mCanvas.setBitmap(mBitmap);//把这个位图放在canvas这个图层上
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setTextSize(60);
        mPaint.setColor(Color.RED);
        mCanvas.drawText("杭州准备开G20峰会",60,100,mPaint);//在创建的canvas图层上绘制文字
        canvas.drawBitmap(mBitmap,100,100,new Paint());
    }
}
效果:

android 自定义控件实现qq讨论组头像功能 还没写完_第4张图片

现在在屏幕上看到了我们绘制的文字信息了,为什么上面没有显示图片呢?那是因为我们创建的Bitmap是空的一个位图,现在我们在drawable资源中图片当做位图看看效果:

package com.example.customcircularavatar.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.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.example.customcircularavatar.R;

/**  * Created by admin on 2016/5/11.  */ public class CustomView extends View {
    private static final String TAG ="CustomView" ;
    private Paint mPaint;
    private Canvas mCanvas;
    private Bitmap mBitmap;
    public CustomView(Context context) {
        this(context,null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint();
        mPaint.setColor(Color.parseColor("#88000000"));
        mCanvas = new Canvas();
        Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        mBitmap = Bitmap.createBitmap(srcBitmap);
        mCanvas.setBitmap(mBitmap);//把这个位图放在canvas这个图层上
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setTextSize(60);
        mPaint.setColor(Color.RED);
        mCanvas.drawText("杭州准备开G20峰会",60,100,mPaint);//在创建的canvas图层上绘制文字
        canvas.drawBitmap(mBitmap,0,0,mPaint);
    }
}
发现一跑起来程序就挂了,日记如下:


刚开始以为是硬件加载引起的bug,后来发现不少硬件加速引起的,而是canvas.setBitmap(bitmap)这个bitmap对象要求是可变的,而我是通过BitmapFactry.decode...()这样获取的Bitmap对象是不可变的,源码解释:

/**  * Specify a bitmap for the canvas to draw into. All canvas state such as  * layers, filters, and the save/restore stack are reset with the exception  * of the current matrix and clip stack. Additionally, as a side-effect  * the canvas' target density is updated to match that of the bitmap.  *  * @param bitmap Specifies a mutable bitmap for the canvas to draw into.  * @see #setDensity(int)  * @see #getDensity()  */ public void setBitmap(@Nullable Bitmap bitmap) {
    if (isHardwareAccelerated()) {
        throw new RuntimeException("Can't set a bitmap device on a HW accelerated canvas");
    }

    if (bitmap == null) {
        native_setBitmap(mNativeCanvasWrapper, null);
        mDensity = Bitmap.DENSITY_NONE;
    } else {
        if (!bitmap.isMutable()) {
            throw new IllegalStateException();
        }
        throwIfCannotDraw(bitmap);

        native_setBitmap(mNativeCanvasWrapper, bitmap);
        mDensity = bitmap.mDensity;
    }

    mBitmap = bitmap;
}
bitmap Specifies a mutable bitmap for the canvas to draw into
意思是说通过canvas draw上去的bitmap对象要是可变的,所以通过修改后代码如下:

package com.example.customcircularavatar.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.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.example.customcircularavatar.R;

/**  * Created by admin on 2016/5/11.  */ public class CustomView extends View {
    private static final String TAG ="CustomView" ;
    private Paint mPaint;
    private Canvas mCanvas;
    private Bitmap mBitmap;
    private  Bitmap srcBitmap;
    public CustomView(Context context) {
        this(context,null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint();
        mCanvas = new Canvas();
        mBitmap = Bitmap.createBitmap(900,900, Bitmap.Config.ALPHA_8);
        srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        mCanvas.setBitmap(mBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setTextSize(60);
        mPaint.setColor(Color.RED);
        mCanvas.drawBitmap(srcBitmap,0,0,mPaint);
        mCanvas.drawText("杭州准备开G20峰会",60,100,mPaint);
        canvas.drawBitmap(mBitmap,0,0,mPaint);
    }
}
效果如下:

android 自定义控件实现qq讨论组头像功能 还没写完_第5张图片


第二个知识点就是关于Xfermode

paint有个方法setXfermode(Xfermode xfermode),这个方法能起什么功能,就是实现两张图叠加时的混合效果,

这篇博客 周末把剩下的写完!



你可能感兴趣的:(android 自定义控件实现qq讨论组头像功能 还没写完)