什么是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()。这段代码显示的效果如下:
其他几个的用法雷同,这里就不在赘述了。
放大镜效果实现
基本用法小编已经介绍的比较清楚了,下面我们来实现我们今天的终极目标,如何实现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),那么图片放大三倍后,这个点在什么位置?
当然是(3x,3y),因为放大是均匀放大的,所以放大后的点在(3x,3y)是毫无问题的,但是ShapeDrawable是从左上角开始平铺的,那么没放大之前,肯定从(0,0)开始,所以我们需要将整个bitmapShader向左上角移动3x,3y的距离,左上X,Y轴的移动方向都是负值,所以左上移动的距离就是(-3x,-3y),所以移动后的ShapeDrawable的左上角坐标就是(-3x,-3y)。
但是我们画圆形都是从中心点开始画的,所以我们需要在X,Y轴各加上一个半径,就变成了(-3x+RADIUS,-3y+RADIUS),也就是上面的this.matrix.setTranslate代码,然后通过setBounds()根据手指位置为中心,画一个圆形,这样我们的放大镜效果就完全实现了。
如果写的还可以,欢迎大家给个小小的赞支持一下,非常感谢!
本文Github源码下载地址:点击下载