android 使用BitmapShader实现圆形以及放大镜效果

Posted zhoushenxian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 使用BitmapShader实现圆形以及放大镜效果相关的知识,希望对你有一定的参考价值。

在一些显示用户头像的时候,大多数都是显示圆形的,而不是显示一个正方形或者长方形,这样显得很呆板,视觉效果也不好看,今天就用二种方式实现图片圆形显示的效果,在先讲这个效果实现之前,讲下BitmapShader到底能干嘛,android有几个关于渲染的类,使用它能实现一些不错的效果,比如今天讲的BitmapShader的就是关于图像渲染,还有其他4种,这都会再以后都会以博客的形式写出来,今天就单独讲讲BitmapShader,进入BitmapShader源码发现这是你见过最简单的系统类源码了

package android.graphics;

public class BitmapShader extends android.graphics.Shader 
    public BitmapShader(android.graphics.Bitmap bitmap, android.graphics.Shader.TileMode tileX, android.graphics.Shader.TileMode tileY)  /* compiled code */ 
这构造函数中就三个参数,简单说明下:

第一个bitmap:就是你渲染图像对象

第二个参数tileX:就是你x轴方向渲染模式

第三个参数tileY:就是你y轴方向渲染模式

关于渲染模式有三种:

从上面的代码中知道BitmapShader是继承了Shader类,而模式就是定义在Shader中的一个枚举而已

package android.graphics;

public class Shader 
    public Shader()  /* compiled code */ 

    public boolean getLocalMatrix(android.graphics.Matrix localM)  /* compiled code */ 

    public void setLocalMatrix(android.graphics.Matrix localM)  /* compiled code */ 

    protected void finalize() throws java.lang.Throwable  /* compiled code */ 

    public static enum TileMode 
        CLAMP, MIRROR, REPEAT;

        private TileMode()  /* compiled code */ 
    
从代码中我们就清晰的看到定义的TileMode有三种

CLAMP:当view控件宽或者高大于你渲染图形的大小时,拉伸最后一个像素去铺满剩下的地方

MIRROR:当view控件宽或者高大于你渲染图形的大小时,通过镜像翻转铺满剩下的地方

REPEAT:当view控件宽或者高大于你渲染图形的大小时,重复图片平铺整个画面(电脑设置壁纸)

等下会说这个三个模式,先把第一种实现圆形的方式代码贴下:

package com.bitmapshaderdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
 * Created by admin on 2016/11/22.
 */
public class RoundHeadView extends View 
    private static final String TAG ="RoundHeadView" ;
    private Paint mPaint;
    private ShapeDrawable shapeDrawable;
    private BitmapShader bitmapShaderp;
    public RoundHeadView(Context context) 
        this(context,null);
    
    public RoundHeadView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    
    public RoundHeadView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initPaint();
        init();
        setBackgroundColor(Color.RED);
    
    private void init() 
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.gird);
        Log.e(TAG,"-"+bitmap.getWidth()+"-"+""+bitmap.getHeight());
        shapeDrawable = new ShapeDrawable(new OvalShape());
        bitmapShaderp = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        shapeDrawable.getPaint().setShader(bitmapShaderp);
    
    private void initPaint() 
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
    

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

运行起来看效果:


发现屏幕一片空白,这是为什么呢? 是因为你没给图片显示设置位置,哪可能会说我我这个自定义view的宽和高就是屏幕的宽和高,但是你必须要给shapeDrawable设置显示区域,

@Override
protected void onDraw(Canvas canvas) 
    super.onDraw(canvas);
    shapeDrawable.setBounds(200,200,400,400);
    shapeDrawable.draw(canvas);
我们在onDraw()方法中指定了显示的区域,就是一个矩形区域,现在运行看效果


ok,一个美女头像出来了,这是一种实现方式,下面不使用ShapeDrawable再实现下:

package com.bitmapshaderdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
 * Created by admin on 2016/11/22.
 */
public class RoundHeadView extends View 
    private static final String TAG ="RoundHeadView" ;
    private Paint mPaint;
    private BitmapShader bitmapShaderp;
    public RoundHeadView(Context context) 
        this(context,null);
    
    public RoundHeadView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    
    public RoundHeadView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initPaint();
        init();
    
    private void init() 
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.gird);
        Log.e(TAG,"-"+bitmap.getWidth()+"-"+""+bitmap.getHeight());
        bitmapShaderp = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        mPaint.setShader(bitmapShaderp);
    
    private void initPaint() 
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    
    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        canvas.drawCircle(200,200,160,mPaint);
    

这就是使用Paint中的setShader()方法把图片 渲染成一个圆形,因为这个渲染是作用于这个画笔上的也就是代码中的mPaint,而我们又用这个画笔去画圆,这就是为啥一个长方形的图片变成圆形的,要变成圆角矩形也很简单,就一行代码的事:

 @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
//        canvas.drawCircle(200,200,160,mPaint);
        canvas.drawRoundRect(100,100,400,300,15,15,mPaint);
    
效果:


上面二种做法都有问题,我们知道显示一个圆,其实是一个矩形的内切圆,如果这个图片不是正方形是长方形,那么这个内切圆肯定是有问题的,认真看下上面几个圆形的头像就会发现问题,在这先把上面遗留的渲染模式讲下:

我绘制圆以后再使用这个画笔绘制一个矩形

@Override
protected void onDraw(Canvas canvas) 
    super.onDraw(canvas);
    canvas.drawCircle(200,200,160,mPaint);
    canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);
设置圆形图片的资源:


这是整张图,在绘制了一个400*400的矩形后效果如下:


没绘制矩形之前效果:


现在把这二个图片放在一起对比:


还有二种模式,这样吧,把图片设置成圆形的不太好看效果,就设置成长方形吧,

@Override
protected void onDraw(Canvas canvas) 
    super.onDraw(canvas);
    canvas.drawRect(100,100,300,300,mPaint);
    canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);
bitmapShaderp = new BitmapShader(bitmap, Shader.TileMode.MIRROR,Shader.TileMode.MIRROR);
模式改成了MIRROR,效果如下:


MIRROR其实就是我们生活中照镜子一样,这个很好理解:

现在把x轴方向设置成重复的模式

bitmapShaderp = new BitmapShader(bitmap, Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);
效果:


通过上面的例子把三种模式都讲了下,现在把上面说到的如果图片不是正方形的问题解决下,解决方案有二种,一种是是使用最小的边把图片切割,一种是放大

方案一:

解决思路:以图片最小的边去切一个正方形,比如这张图:


那么现在开始按照这个思路解决刚才问题

private Bitmap getBitmap() 
    Canvas canvas = new Canvas();
    Bitmap tempBp = null;
    Paint paint = new Paint();
    if(width>height)
        tempBp = Bitmap.createBitmap(bitmap,(width-height)/2,0,height,height);
    else if(width<height)
        tempBp = Bitmap.createBitmap(bitmap,0,(height-width)/2,width,width);
    else//等于
        tempBp = Bitmap.createBitmap(bitmap,0,0,height,height);
    
    canvas.drawBitmap(tempBp,10,10,paint);
    return  tempBp;
这个就是得到在原来的图片上切一个矩形出来,使用了Bitmap的一个静态函数

public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap source, int x, int y, int width, int height)
第一个参数 source:就是在那个图片上进行切

第二个参数在以图片为坐标轴 x轴方向的值

第三个参数:同理第二个参数

第四个参数:新图片的宽度

第五个参数:新图片的高度

如图:


同理宽>高的情况下,相信也可以算出(x,y)坐标

整个代码如下:

package com.bitmapshaderdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/11/22.
 */
public class RoundHeadView extends View 
    private static final String TAG ="RoundHeadView" ;
    private Paint mPaint;
    private BitmapShader bitmapShaderp;
    private int width;//图片的宽度
    private int height;//图片的高度
    private Bitmap bitmap;
    public RoundHeadView(Context context) 
        this(context,null);
    
    public RoundHeadView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    
    public RoundHeadView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initPaint();
        init();
    
    private void init() 
        bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.gird);
        width = bitmap.getWidth();
        height = bitmap.getHeight();
        bitmapShaderp = new BitmapShader(getBitmap(), Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);
        mPaint.setShader(bitmapShaderp);
    
    private void initPaint() 
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    
    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        canvas.drawCircle(width/2,width/2,width/2,mPaint);
    

    private Bitmap getBitmap() 
        Canvas canvas = new Canvas();
        Bitmap tempBp = null;
        Paint paint = new Paint();
        if(width>height)
            tempBp = Bitmap.createBitmap(bitmap,(width-height)/2,0,height,height);
            width = height;
        else if(width<height)
            tempBp = Bitmap.createBitmap(bitmap,0,(height-width)/2,width,width);
            height = width;
        else//等于
            height = width;
            tempBp = Bitmap.createBitmap(bitmap,0,0,height,height);
        
        canvas.drawBitmap(tempBp,0,0,paint);
        return  tempBp;
    

 效果: 

但是这个做法也有缺点就是如果用户想看图片上面的内容这个就个做法就不太好了,这也算是个需求

方案二:

周末了虽然这么晚还是想把这博客写完,不然感觉总欠什么,好了,废话不多说,之前解决方案是缩小,而这方案是放大,是通过矩阵的方式放大,那么放大比例是多少呢,是根据最小的边框来计算,就是最大的边框/最小的边框=缩放比例,代码如下:

package com.bitmapshaderdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
 * Created by admin on 2016/11/22.
 */
public class RoundHeadView extends View 
    private static final String TAG ="RoundHeadView" ;
    private Paint mPaint;
    private BitmapShader bitmapShaderp;
    private int width;//图片的宽度
    private int height;//图片的高度
    private Bitmap bitmap;
    public RoundHeadView(Context context) 
        this(context,null);
    
    public RoundHeadView(Context context, AttributeSet attrs) 
        this(context, attrs,0);
    
    public RoundHeadView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        initPaint();
        init();
    
    private void init() 
        bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.gird);
        width = bitmap.getWidth();
        height = bitmap.getHeight();
        bitmapShaderp = new BitmapShader(bitmap, Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);
        mPaint.setShader(bitmapShaderp);
    
    private void initPaint() 
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    
    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
        //设置像素矩阵,来调整大小,为了解决宽高不一致的问题。
      float scale = Math.max(width, height)*1.0f/Math.min(width, height);
      Matrix matrix = new Matrix();
      matrix.setScale(scale, scale);//缩放比例
        bitmapShaderp.setLocalMatrix(matrix);
      canvas.drawCircle(Math.max(width, height)/2f, scale*Math.max(width, height)/2f, Math.max(width, height)/2f, mPaint);
    

效果:



现在实现放大镜的功能

思路:其实将一张图片放大,然后截图这张大图中的部分图片,我先显示一个普通的图片

package com.bitmapshaderdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
 * Created by admin on 2016/11/28.
 */
public class MirrorImageView extends View 
    private Bitmap bitmap;
    private Paint paint;
    public MirrorImageView(Context context, AttributeSet attrs) 
        super(context, attrs);
        paint = new Paint();
        paint.setStrokeWidth(8);
        bitmap = BitmapFactory.decodeResource(getResources(),

以上是关于android 使用BitmapShader实现圆形以及放大镜效果的主要内容,如果未能解决你的问题,请参考以下文章

Android学习笔记进阶16之BitmapShader

Android 颜色渲染 BitmapShader位图渲染

Android 控件 RoundedBitmapDrawable

自定义控件三部曲之绘图篇(十八)——BitmapShader与望远镜效果

Android 自定义Drawable

Android Shader渲染以及实现水波纹霓虹文字雷达等效果