说在前面
最近在学习自定义View的实现,发现自己对自定义View的Api非常陌生,而自定义View也属于Android中非常重要的组成部分,想着边学习边记个笔记吧。
大部分的学习内容都来自Hencoder,下面跟着凯哥一起动手学学自定义view。
自定义View的关键类是Canvas和Paint,Paint作为参数传入Canvas的相应绘制方法中。我理解的Paint和Canvas的关系就像Paint是绘制风格,Canvas控制绘制细节。
让我们开始
首先我们新建了一个自定义View继承View,我们把它叫做ClassOne,放在布局文件activity_main.xml中:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.abilix.learn.algorithms.MainActivity">
<com.abilix.learn.algorithms.ClassOne
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
然后我们重写ClassOne中的onDraw方法:
public class ClassOne extends View {
//一些构造函数
Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
canvas.drawCircle(100,100,100,paint);
}
}
这样我们build一下就得到了下面这样的效果(请忽略项目的名称QAQ):
这里的
canvas.drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
cx和
cy分别表示在一个以画布
左上角为坐标轴原点,
以即将绘制的圆的圆心为零点
,
正右方向为x正半轴,
正下方向为y轴正半轴的偏移量,
radious是圆的半径,paint这里我们默认传入了无参构造函数得到的paint。
canvas的绘制方法还有很多,除了这里的drawCircle之外,还有许多以draw开头的绘制方法,部分方法还有许多种重载(天啊这也太复杂了吧)比如:
drawBitmap
drawArc
drawColor
drawLines
drawPath
等等……
嗯,好像这些谷歌爸爸提供的官方文档里都写的很清楚了,但是作为在公司上网受限的我来说,还是只能一个一个百度一下他们到底是干啥用的(心塞塞的)。
drawBitMap
drawBitMap一共有五种重载方法:
drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)
drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint)
drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)
drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, @Nullable Paint paint)
drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, @Nullable Paint paint)
其中后两种方法已经被废弃了,我们来看看第一种:
drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
恩,乍一看应该和drawCircle很相似,也是偏移量,那么究竟如何还是要亲自码一码验证一下,我们使用Android自带的图片ic_launcher作为测试bitmap,并且传入(left,top)的值为(0,0),(30,30),(-30,-30)试试:
嘛,和我们想的一样,说明咱的想象力还是可以的嘛,(#^.^#)嘻嘻
然后我们看第二个重载函数:
drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)
为了更好得展示效果,看来得换张图片了,就用这张R.drawable.pic1好了
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
rectF = new RectF(0,0,bitmap.getWidth(),bitmap.getHeight());
然后我们进行:
canvas.drawBitmap(bitmap,rect1,rectF,paint);
得到的效果如图:
然后我们改一下rect1:
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
rectF = new RectF(0,0,bitmap.getWidth(),bitmap.getHeight());
再看看效果:
看起来view的大小没变,但是里面这个bitmap被放大了,那么我就猜想一下第二个参数Rect是用于控制传入的bitmap上进行裁剪的区域的大小,而第三个参数RectF是控制绘画区域的大小(自动缩放)。
ok,验证一蛤:
(1) 只改变rectF
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
rectF = new RectF(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
(2)同时改变
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
rectF = new RectF(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
效果:
恩,稳的不行。接下来再看看第三个重载函数吧:
drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint)
诶,这两个都是Rect???而且和第二个重载的名字是一样的(src、dst),芽儿,有点猜不出了
不过初始的样子应该还是可以猜到的:
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
rect2 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
然后我们通过改变第二,第三个参数形成三种情况:
(1)只改变第二个参数:
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
rect2 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
rect2 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
rect1 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
rect2 = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
喵喵喵喵喵?这不是一样吗???查看源码后发现:
native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top, right, bottom,
dst.left, dst.top, dst.right, dst.bottom, nativePaint, mScreenDensity,
bitmap.mDensity);
又去补充了一下Rect和RectF的区别,原来最大的区别是精度不同...
下面看看第四个重载:
drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint)
矩阵,恩,八成应该是进行几何变换的了,我们来试试常用的矩阵:
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.pic1);
matrix = new Matrix();
matrix.postRotate(45f,bitmap.getWidth()/2,bitmap.getHeight()/2);
效果:
美滋滋,正好下班,溜了溜了。周六继续分享下一期Android自定义View学习之旅(二)。