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源码下载地址:点击下载

相关文章
|
7月前
|
XML 前端开发 Java
Android Studio App自定义控件中自定义视图的绘制讲解及实战(附源码 包括自定义绘制各种图形)
Android Studio App自定义控件中自定义视图的绘制讲解及实战(附源码 包括自定义绘制各种图形)
107 1
|
Android开发
flutter中实现仿Android端的onResume和onPause方法
flutter中实现仿Android端的onResume和onPause方法
|
2月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
|
15天前
|
搜索推荐 Android开发 开发者
安卓应用开发中的自定义控件实践
在安卓应用开发的广阔天地中,自定义控件如同璀璨的星辰,点亮了用户界面设计的夜空。它们不仅丰富了交互体验,更赋予了应用独特的个性。本文将带你领略自定义控件的魅力,从基础概念到实际应用,一步步揭示其背后的原理与技术细节。我们将通过一个简单的例子——打造一个具有独特动画效果的按钮,来展现自定义控件的强大功能和灵活性。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往更高阶UI设计的大门。
|
2月前
|
缓存 搜索推荐 Android开发
安卓开发中的自定义控件基础与进阶
【10月更文挑战第5天】在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将通过浅显易懂的语言和实例,引导你了解自定义控件的基本概念、创建流程以及高级应用技巧,帮助你在开发过程中更好地掌握自定义控件的使用和优化。
48 10
|
1月前
|
前端开发 Android开发 UED
安卓应用开发中的自定义控件实践
【10月更文挑战第35天】在移动应用开发中,自定义控件是提升用户体验、增强界面表现力的重要手段。本文将通过一个安卓自定义控件的创建过程,展示如何从零开始构建一个具有交互功能的自定义视图。我们将探索关键概念和步骤,包括继承View类、处理测量与布局、绘制以及事件处理。最终,我们将实现一个简单的圆形进度条,并分析其性能优化。
|
2月前
|
前端开发 搜索推荐 Android开发
安卓开发中的自定义控件实践
【10月更文挑战第4天】在安卓开发的世界里,自定义控件如同画家的画笔,能够绘制出独一无二的界面。通过掌握自定义控件的绘制技巧,开发者可以突破系统提供的界面元素限制,创造出既符合品牌形象又提供卓越用户体验的应用。本文将引导你了解自定义控件的核心概念,并通过一个简单的例子展示如何实现一个基本的自定义控件,让你的安卓应用在视觉和交互上与众不同。
|
3月前
|
缓存 前端开发 Android开发
安卓应用开发中的自定义控件
【9月更文挑战第28天】在安卓应用开发中,自定义控件是提升用户界面和交互体验的关键。本文通过介绍如何从零开始构建一个自定义控件,旨在帮助开发者理解并掌握自定义控件的创建过程。内容将涵盖设计思路、实现方法以及性能优化,确保开发者能够有效地集成或扩展现有控件功能,打造独特且高效的用户界面。
|
3月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义控件
【9月更文挑战第5天】在安卓开发的海洋中,自定义控件如同一艘精致的小船,让开发者能够乘风破浪,创造出既独特又高效的用户界面。本文将带你领略自定义控件的魅力,从基础概念到实战应用,一步步深入理解并掌握这一技术。
|
4月前
|
Android开发 UED 开发者
安卓开发中的自定义控件基础
【8月更文挑战第31天】在安卓应用开发过程中,自定义控件是提升用户界面和用户体验的重要手段。本文将通过一个简易的自定义按钮控件示例,介绍如何在安卓中创建和使用自定义控件,包括控件的绘制、事件处理以及与布局的集成。文章旨在帮助初学者理解自定义控件的基本概念,并能够动手实践,为进一步探索安卓UI开发打下坚实的基础。