Android自定义控件开发入门与实战(13)Android画布,一次哔哩哔哩面试经历

shapeDrawable = new ShapeDrawable(new PathShape(path, 100, 200));

高度就变成了原来的一半了。

在这里插入图片描述

自定义Shape

除了系统自带的Shape,我们还可以自定义Shape啦,就是继承Shape类,重写其draw方法。

这个实例就不讲了。以后有兴趣了再来实现。

3、常用函数

(1)、setBounds():指定当前ShapeDrawable在该控件的显示位置

(2)、getPaint():获取ShapeDrawable的paint,然后我们可以实现paint的所有函数

这里有一点,关于Shader,在之前讲shader的时候我们知道Shader是从画布左上角开始绘制的,所以在ShapeDrawable中,给其设置了Shader,也会从ShapeDrawable左上角开始绘制

(3)、其他的函数包括

setAlpha(int alpha)

setColorFilter(ColorFilter colorFilter):设置ColorFilter

setIntrinsicHeight(int height):设置默认高度。当Drawable以setbackGroundDrawable及setImageDrawable方式使用时,会使用默认宽度和默认高度来计算当前Drawable的大小与位置。如果不设置默认的宽高都是-1px。

setIntrinsicWidth(int width):设置默认宽度

setPadding(Rect padding):设置边距

4、放大镜效果

首先看下onDraw函数:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (bitmap == null) {

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.bg_telescope);

bitmap = bmp.createScaledBitmap(bmp, getWidth(), getHeight(), false);

BitmapShader bmShader = new BitmapShader(Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() * FACTOR, bitmap.getHeight() * FACTOR, true),

Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

drawable = new ShapeDrawable(new OvalShape());

drawable.getPaint().setShader(bmShader);

drawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2);

}

canvas.drawBitmap(bitmap,0,0,null);

drawable.draw(canvas);

}

(1)为什么我们要把bitmap的初始化放在 onDraw方法中,因为我们需要把被放大的图片缩放到控件大小。而控件的大小只有在onLayout之后才知道,所以就在onDraw来拿,至于Bitmap.createScaledBitmap()之后再讲。

(2)创建ShapeDrawable,做的是放大镜形状,所以我们要构造OvalShape,然后大小是事先设定好的radius*2

(3)bmShader就是创建一个将原图放大三倍的图片(因为放大镜放大三倍)

接下来看下onTouchEvent代码:

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_MOVE:

case MotionEvent.ACTION_DOWN:

final int x = (int) event.getX();

final int y = (int) event.getY();

// 这个位置表示的是绘制Shader 的起始位置

matrix.setTranslate(RADIUS - x * FACTOR, RADIUS - y * FACTOR);

drawable.getPaint().getShader().setLocalMatrix(matrix);

drawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS);

invalidate();

return true;

case MotionEvent.ACTION_UP:

drawable.setBounds(0,0,0,0);

invalidate();

return super.onTouchEvent(event);

default:

return super.onTouchEvent(event);

}

}

在onTouchEvent中返回了true,表示完全拦截了所有Touch事件,因为这里完全不需要使用View默认的处理行为。

当手指有动作的时候我们应该改变当前ShapeDrawable的位置

drawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS);

即以当前手指位置为中心,画一个圆。

最关键的是Shader如何移动到我们要显示的位置,我们讲过,Shader的开始显示位置在ShapeDrawable的左上角。所以,如果我们不移动Shape,那么显示出来的永远是图片的左上角部分。

我们需要先找到当前手指放大3倍的图片上对应的点,然后以这个对应点为中心显示出半径为RADIUS的圆中的图形。

对应的点好找,当前手指的位置是(x,y),那么放大的位置是(3x,3y)。为了显示以放大3倍后的手指位置为中心的圆形区域,BitmapShader需要向左和上移动多少呢?

其实画个图就知道 是向左移动 (-3x+r) 向上移动 (-3y+r)

效果如下所示:

在这里插入图片描述

5、自定义Drawable

自定义控件时Android控件的精髓,所以Drawable系统自带的api(ShapeDrawable GradientDrawable)大部分都满足不了我们的需求,所以我们更需要自己学习做,这里来自定义一个Drawable。

让类继承自Drawable,可以看到必须要实现以下抽象函数:

public class CustomDrawable extends Drawable {

@Override

public void draw(@NonNull Canvas canvas) {

}

@Override

public void setAlpha(int alpha) {

}

@Override

public void setColorFilter(@Nullable ColorFilter colorFilter) {

}

@Override

public int getOpacity() {

return 0;

}

}

4个函数意义如下:

  • draw():使我们将会用到的,与View类似,传入的参数是一个Canvas对象,我们只需要调用Canvas的一些方法,效果就会直接显示在Drawable上

  • setAlpha()和setColorFilter()函数非常容易实现。当外层调用Drawable这两个函数时,我们只需要将对应的参数设置给Drawable的Paint即可。

  • getOpacity():当外部需要知道我们自定义的Drawable显示模式时会调用这个函数。它有4个取值:PixelFormat.UNKNOWN,TRANSLUCENT,TRANSPARENT,OPAQUE。其中PexelFormat.UNKNOWN表示当前Drawable的绘图是具有Alpha通道的,即使用Drawable后,其底部的图像仍有可能看得到。PixelFormat.TRANSPARENT表示当前Drawable是完全透明的。PexelFormat.OPAQUE表示当前图像是完全没有Alpha通道的,使用Drawable后,其底层图像将完全被覆盖。PixelFormal.UNKNOWN表示未知。一般而言,如果我们不知道怎么返回,直接返回PixelFormat.TRANSLUCENT是最靠谱的做法。

我们来自定义一个圆角矩形Drawable:

public class CustomDrawable extends Drawable {

private Paint mPaint;

private Bitmap mBitmap;

private BitmapShader bitmapShader;

private RectF mBounds;

public CustomDrawable(Bitmap mBitmap) {

this.mBitmap = mBitmap;

mPaint = new Paint();

mPaint.setAntiAlias(true);

}

@Override

public void draw(@NonNull Canvas canvas) {

canvas.drawRoundRect(mBounds, 20, 20, mPaint);

}

@Override

public void setAlpha(int alpha) {

mPaint.setAlpha(alpha);

}

@Override

public void setColorFilter(@Nullable ColorFilter colorFilter) {

mPaint.setColorFilter(colorFilter);

}

@Override

public int getOpacity() {

return PixelFormat.TRANSLUCENT;

}

@Override

public void setBounds(int left, int top, int right, int bottom) {

super.setBounds(left, top, right, bottom);

bitmapShader = new BitmapShader(Bitmap.createScaledBitmap(mBitmap, right - left, bottom - top, true), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

mPaint.setShader(bitmapShader);

mBounds = new RectF(left, top, right, bottom);

}

@Override

public int getIntrinsicWidth() {

return mBitmap.getWidth();

}

@Override

public int getIntrinsicHeight() {

return mBitmap.getHeight();

}

}

我们在getIntrinsicWidth/Height设置了默认的宽高

setBounds()在ShapeDrawable中我们就接触了这个函数,含义是给Drawable设定边界,即这块Drawable画布的大小。在setBounds()函数中,我们根据边界创建一个与Drawable相同大小的Bitmap作为Drawable的Shader。

也就是说Bitmap会根据Drawable的大小动态拉伸,以完全覆盖这个Drawable,最后将边界保存起来,以便在绘图中使用。

draw:我们知道,Sader始终是从画布的左上角开始平铺的,而drawXXX函数只用来指定哪部分显示出来, 所以我们只需要在draw方法中调用drawRoundRect函数就能将BitmapShder以圆角矩形的方式显示出来即可。

Drawable的使用方法

一般有两种使用方法,一种是通过ImageView的setImageDrawable(drawable)函数将其设置为ImageView的源图片,另外一种是通过View的setBackgroundDrawable(drawable)函数将其作为背景

<ImageView

android:id=“@+id/iv”

android:layout_width=“200dp”

android:layout_height=“200dp”

android:background=“#ffffff”

android:scaleType=“fitXY”/>

然后代码中通过:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.avator_xizuka);

iv = findViewById(R.id.iv);

CustomDrawable drawable = new CustomDrawable(bitmap);

iv.setImageDrawable(drawable);

在这里插入图片描述

这就跟直接在ImageView中直接设置src一样。

假如我么能通过setBackgroundDrawable来设置,并且在xml文件中将ImageView改成TextView,宽高改成wrap_content,效果如下:

在这里插入图片描述

这个时候图片被拉得很长,宽度 高度是取textView和图片中长的那一个。

这是因为我们设置了默认宽高为bitmap的宽高,如果我们不去设置默认宽高,让其返回-1,效果将是下面这个样子:

在这里插入图片描述

就变成了text的高度和宽度了。

总结:

  • 当使用setImageDrawable(drawable)函数来设置ImageView数据源时,自定义Drawable的位置和大小与ImageView的scaleType有关

  • 当使用setBackgroundDrawable(drawable)函数来设置View的背景时,自定义的Drawable的宽高与控件大小一致,控件的宽、高则选取本身宽高和自定义Drawable的宽高。

自定义Drawable和自定义View差别很大,虽然像是砍掉手势交互的自定义View,但是自定义Drawable的使用场景非常明确,就是使用在有Drawable的地方中。而且也可以替代Bimap用于View中(比如放大镜效果

既然它可以替代Bitmap,那我们就来讲一下它和bitmap之间的关系吧。

6、Drawable与Bitmap的对比

(1)定义对比

  • Bitmap是位图,bmp格式,编码器很多,有RGB_565,ARGB_8888,逐像素的显示对象,执行效率很高,但是存储效率低下

  • Drawable作为Andorid下通用的图形对象,可以装载常用格式的图像,比如GIF PNG JPG BMP,还提供了高级可视化对象,比如渐变、图形

所以Bitmap是Drawable,但是Drawable不一定是Bitmap。

(2)指标对比

在这里插入图片描述

单是从占用内存这一点上,我们就有必要在使用Bitmap一定要考虑可不可以使用Drawable了,绘制速度更是如此。

所以在Android UI系统中普遍使用Drawable。

(3)绘制便利性对比:

Drawable有很多派生类,通过这些派生类可以容易地生成渐变、层叠等效果。但从这一方面而言,Drawable比Bitmap更有优势。如果仅仅用空白画布来绘图,drawable构造和使用则不如bitmap方便。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-hsYfQQYG-1711049343414)]

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值