Android自定义控件(七)——ShapeDrawable实现放大镜效果

简介: Android自定义控件(七)——ShapeDrawable实现放大镜效果

什么是ShapeDrawable


ShapeDrawable是一种常见的Drawable,可以理解为通过颜色来构造的图形,它既可以是纯色的图形,也可以是具有渐变效果的图形。


ShapeDrawable有两个构造函数,分别是:

ShapeDrawable()
ShapeDrawable(Shape s)


ShapeDrawable使用过程中,需要与Shape对象关联起来,所以,当我们使用第一个构造函数的时候,就需要额外的调用如下代码,关联Shape:

ShapeDrawable.setShape(Shape shape)

所以从这里我们可以看到,我们还是直接使用第二个构造函数最方便,但有一点需要注意,Shape是一个基类,并没有具体实现,并不能直接传递Shape对象,在我们使用的过程中,要么自己继承实现,要么使用它的派生类:


1.RectShape:构造一个矩形Shape

2.ArcShape:构造一个扇形Shape

3.OvalShape:构造一个椭圆Shape

4.RoundRectShape:构造一个圆角矩形Shape,可带有镂空的矩形效果

5.PathShape:构造一个可根据路径绘制的Shape


OvalShape的基本用法


今天介绍的放大镜效果的实现,使用的就是OvalShape来构造,所以我们先来写一个简单的程序,看看如何使用这个OvalShape,直接上代码:

public class OvalShapeView extends View {
    private ShapeDrawable shapeDrawable;//定义一个ShapeDrawable
    /***
     * 初始化
     */
    private void init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null);//关闭硬件加速
        this.shapeDrawable=new ShapeDrawable(new OvalShape());//绘制一个椭圆形的ShapeDrawable
        this.shapeDrawable.setBounds(new Rect(50,50,200,100));//定位椭圆位置
        this.shapeDrawable.getPaint().setColor(Color.GREEN);//显示颜色为绿色
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        this.shapeDrawable.draw(canvas);
    }
    public OvalShapeView(Context context) {
        super(context);
        this.init();
    }
    public OvalShapeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.init();
    }
    public OvalShapeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.init();
    }
}


其中setBounds()用来指定当前ShapeDrawable在当前控件中的显示位置,而getPaint()就是获取ShapeDrawable自带的画笔,然后设置画笔参数,来更改其显示效果,使用起来与直接定义的Paint类似,比如这里的setColor(),还有即将用到的setShader()。这段代码显示的效果如下:

36.png

其他几个的用法雷同,这里就不在赘述了。


放大镜效果实现


基本用法小编已经介绍的比较清楚了,下面我们来实现我们今天的终极目标,如何实现Android中的放大镜效果,首先我们还是要自定义一个View,然后定义成员变量,进行初始化,代码如下:

public class LoupeView extends View {
    private Bitmap bitmap;//获取原图片
    private ShapeDrawable shapeDrawable;//创建一个
    private final Matrix matrix=new Matrix();//矩阵运算
    private static final int RADIUS=160;//半径
    private static final int TIMES=3;//放大倍数
    private void init(){
        setLayerType(LAYER_TYPE_SOFTWARE,null);//禁止硬件加速
    }
    public LoupeView(Context context) {
        super(context);
        this.init();
    }
    public LoupeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.init();
    }
    public LoupeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.init();
    }
}


这里我们定义了一个bitmap方便后续设置背景,以及设置放大效果,还有ShapeDrawable,矩阵运算matrix进行移动ShapeDrawable,半径RADIUS,倍数TMES。


接着,我们需要绘制背景到自定义控件中,所以我们需要在OnDraw()函数中进行绘制操作,代码如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(this.bitmap==null){
            Bitmap bmp= BitmapFactory.decodeResource(getResources(),R.drawable.background);//获取原图片
            this.bitmap=Bitmap.createScaledBitmap(bmp,getWidth(),getHeight(),false);//根据原图片创建一个和屏幕宽高相等的Bitmap
            BitmapShader bitmapShader=new BitmapShader(
                    Bitmap.createScaledBitmap(this.bitmap,bitmap.getWidth()*TIMES,bitmap.getHeight()*TIMES,true),//按比例进行缩放图片
                    Shader.TileMode.CLAMP,
                    Shader.TileMode.CLAMP);
            this.shapeDrawable=new ShapeDrawable(new OvalShape());//构造一个椭圆图形
            this.shapeDrawable.getPaint().setShader(bitmapShader);//填充
            this.shapeDrawable.setBounds(0,0,RADIUS*2,RADIUS*2);//因为椭圆的宽高都是一样的,所以是个圆形
        }
        canvas.drawBitmap(this.bitmap,0,0,null);
        this.shapeDrawable.draw(canvas);
    }


这里我们通过判断bitmap是否为空,才知道是否设置了背景,如果没有背景,我们获取到原图像,然后通过createScaledBitmap()将源图像缩放到控件大小,这里小编设置的控件大小为match_parent,其实就是屏幕的宽高。


同时,通过BitmapShader设置图片的放大效果,然后就是上面介绍的OvalShape来构造一个圆形,因为这里设置的椭圆形宽高都是RADIUS*2,所以他是一个圆形。最后,将图片画到控制之中,然后把放大后的该图片圆形显示区域的ShapeDrawable也画上去。

@Override
public boolean onTouchEvent(MotionEvent event) {
    final int x=(int)event.getX();//获取手指X坐标
    final int y=(int)event.getY();//获取手指Y坐标
    //将ShapeDrawable移动到手指所显示人区域
    this.matrix.setTranslate(RADIUS-x*TIMES,RADIUS-y*TIMES);
    this.shapeDrawable.getPaint().getShader().setLocalMatrix(this.matrix);
  //设置ShapeDrawable位置
    this.shapeDrawable.setBounds(x-RADIUS,y-RADIUS,x+RADIUS,y+RADIUS);
    invalidate();//重绘
    return true;
}


这段代码是放大镜的核心代码,我们先来理解一个概念,我们都知道上面我们设置了原图像的大小为手机的整个屏幕,假设小编的手指按住了了屏幕上的某个点(x,y),那么图片放大三倍后,这个点在什么位置?

37.png

当然是(3x,3y),因为放大是均匀放大的,所以放大后的点在(3x,3y)是毫无问题的,但是ShapeDrawable是从左上角开始平铺的,那么没放大之前,肯定从(0,0)开始,所以我们需要将整个bitmapShader向左上角移动3x,3y的距离,左上X,Y轴的移动方向都是负值,所以左上移动的距离就是(-3x,-3y),所以移动后的ShapeDrawable的左上角坐标就是(-3x,-3y)。

38.png

但是我们画圆形都是从中心点开始画的,所以我们需要在X,Y轴各加上一个半径,就变成了(-3x+RADIUS,-3y+RADIUS),也就是上面的this.matrix.setTranslate代码,然后通过setBounds()根据手指位置为中心,画一个圆形,这样我们的放大镜效果就完全实现了。


如果写的还可以,欢迎大家给个小小的赞支持一下,非常感谢!


本文Github源码下载地址:点击下载

相关文章
|
4月前
|
XML 前端开发 Java
Android Studio App自定义控件中自定义视图的绘制讲解及实战(附源码 包括自定义绘制各种图形)
Android Studio App自定义控件中自定义视图的绘制讲解及实战(附源码 包括自定义绘制各种图形)
34 1
|
Android开发
flutter中实现仿Android端的onResume和onPause方法
flutter中实现仿Android端的onResume和onPause方法
|
4月前
|
XML Java Android开发
Android Studio App自定义控件中视图的构造和测量方法讲解及实战(附源码 实现下拉刷新功能 超详细必看)
Android Studio App自定义控件中视图的构造和测量方法讲解及实战(附源码 实现下拉刷新功能 超详细必看)
41 1
|
11月前
|
缓存 Android开发 Kotlin
Android 弹幕的两种实现及性能对比 | 自定义控件
Android 弹幕的两种实现及性能对比 | 自定义控件
199 0
|
11月前
|
调度 Android开发
Android空间架构与自定义控件详解-更新中
Android空间架构与自定义控件详解-更新中
62 0
|
Android开发 容器
Android实现面包屑效果,支持Fragment联动
Android实现面包屑效果,支持Fragment联动
|
Android开发
Android实现连线题效果
Android实现连线题效果
|
Android开发
Android实现调用系统相机录像及实现录音
Android实现调用系统相机录像及实现录音
581 0
|
移动开发 JavaScript Android开发
通过howler.js实现在Android下的微信浏览器自动播放音频
通过howler.js实现在Android下的微信浏览器自动播放音频
399 0
通过howler.js实现在Android下的微信浏览器自动播放音频
|
存储 Dart Java
【Flutter】packages思维以及使用Java添加Android平台特定的实现在Flutter框架里的体现和运用
【Flutter】packages思维以及使用Java添加Android平台特定的实现在Flutter框架里的体现和运用