在上一篇博客中,我们基本了解了Android中View的绘制流程,知道了绘制流程以后,是不是想要马上写一些自定义控件来玩一下呢?不急,在这一篇博客中,我们先来了解一下Android给我们提供的一些常见的绘图的API。绘图的API是什么呢,我就用一个比喻吧。当Android工程师在自定义view是就相当于一个画家,那么画家作画是不是需要了工具了,如画板、画布、画笔以及颜料等,而Android系统也给我们提供了很多这样的工具,就是Android中的一些绘图相关的API。这篇文章主要介绍Point/PointF、Rect/RectF、Bitmap/BitmapDrawable几个类;在下一篇文章中将主要介绍Paint类和Canvas类。
1.Point类和PointF类(讨论的是android.graphics包下面的,不是java.awt包下面的)
Point类是一个简单的类,代表一个"点",实现了Parcelable序列化接口,支付序列化和反序列化。Point类中定义了两个成员变量x和y,表示一个点的横坐标和纵坐标。在Android中的坐标与数学中的平面坐标有所不同,Android中x轴向右为正,向左为负;y轴向下为正,向上为负,坐标原点在屏幕的左上角,也就是说屏幕内的所有点不管是x坐标还是y坐标都是正数。
Point类作为最简单的类,提供的功能也很简单
① 通过Point类创建一个点的方法
// 创建一个新的点② 对Point点进行操作的方法
public Point() {}
// 根据x,y坐标创建一个新的点
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// 通过一个点创建一个新的点,就是将一个点的坐标赋值给另一个点
public Point(Point src) {
this.x = src.x;
this.y = src.y;
}
// 对点进行重新设置x和y③ 判断方法
public void set(int x, int y) {
this.x = x;
this.y = y;
}
// 将x和y取反值
public final void negate() {
x = -x;
y = -y;
}
// 对x和y的值做改变,dx、dy表示偏移量,正负号决定偏移的方向
public final void offset(int dx, int dy) {
x += dx;
y += dy;
}
// 通过x和y坐标判断是否为同一点PointF类和Point类功能和用法完全一样,唯一不同的就是Point类的成员变量x、y是int类型的;PointF类的成员变量x、y是float类型的。
public final boolean equals(int x, int y) {
return this.x == x && this.y == y;
}
// 判断一个对象是否和当前点是否是同一个点或者重合
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
if (x != point.x) return false;
if (y != point.y) return false;
return true;
}
2.Rect类和RectF类
Rect 类定义了一个矩形结构,同样实现了Parcelable序列化接口。Rect类定义了left、top、right、bottom 四个成员变量,我们需要正确理解这4个成员变量的作用:
◆ left: 矩形左边线条离 y 轴的距离
◆ top:矩形上面线条离 x 轴的距离
◆ right:矩形右边线条离 y 轴的距离
◆ bottom:矩形底部线条离 x 轴的距离
矩形是一种非常常见的图形结构,并且能衍生出更多的图形,如椭圆、扇形、弧线等等;矩形还能进行各种图形运算,如交集、并集等等,所以与之对应的 Rect 类功能也更加复杂。
① 创建一个Rect,主要有3个构造方法
// 创建一个空对象,left、topright、bottom都为0② 对Rect重新设置值的方法
public Rect() {}
// 根据指定的left、topright、bottom创建一个Rect对象
public Rect(int left, int top, int right, int bottom) {...}
// 从另外一个Rect对象复制出一个新的Rect对象
public Rect(Rect r) {...}
// 将Rect对象置空,就是将left、topright、bottom的值都设置为0
public void setEmpty() {
left = right = top = bottom = 0;
}
// 重新设置left、topright、bottom的值
public void set(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
// 将另外一个Rect对象的值设置到当前Rect对象中
public void set(Rect src) {
this.left = src.left;
this.top = src.top;
this.right = src.right;
this.bottom = src.bottom;
}
③ Rect类中的计算相关的方法
// 获取Rect的宽④ Rect类中的判断相关的方法
public final int width() {
return right - left;
}
// 获取Rect的高
public final int height() {
return bottom - top;
}
// 计算Rect中心点的x坐标,返回int类型的值
public final int centerX() {
return (left + right) >> 1; // >>1 相当于 /2
}
// 计算Rect中心点的y坐标,返回int类型的值
public final int centerY() {
return (top + bottom) >> 1;
}
// 计算Rect中心点的x坐标,返回float类型的值,结果更精确
public final float exactCenterX() {
return (left + right) * 0.5f;
}
// 计算Rect中心点的y坐标,返回float类型的值,结果更精确
public final float exactCenterY() {
return (top + bottom) * 0.5f;
}
// 判断是否为空⑤ 缩放和平移相关的方法
public final boolean isEmpty() {
return left >= right || top >= bottom;
}
// 判断是否包含点x、y
public boolean contains(int x, int y) {
return left < right && top < bottom // check for empty first
&& x >= left && x < right && y >= top && y < bottom;
}
// 判断是否包含另外一个矩形
public boolean contains(int left, int top, int right, int bottom) {
return this.left < this.right && this.top < this.bottom
&& this.left <= left && this.top <= top
&& this.right >= right && this.bottom >= bottom;
}
// 判断是否包含另外一个矩形
public boolean contains(Rect r) {
return this.left < this.right && this.top < this.bottom
&& left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
}
// 实现矩形的缩放功能,缩放中心就是矩形的中心点,dx、 dy为正数时表示缩小,负数表示放大⑥ 运算相关的方法
public void inset(int dx, int dy) {
left += dx;
top += dy;
right -= dx;
bottom -= dy;
}
// 矩形的left和right同时移动相同的距离dx,矩形的top和bottom同时移动相同的距离dy,正负决定移动方向
public void offset(int dx, int dy) {
left += dx;
top += dy;
right += dx;
bottom += dy;
}
// 也是移位, 只是offset()是绝对定位,offsetTo()是相对定位。
public void offsetTo(int newLeft, int newTop) {
right += newLeft - left;
bottom += newTop - top;
left = newLeft;
top = newTop;
}
// 取交集
public boolean intersect(int left, int top, int right, int bottom) {
if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
if (this.left < left) this.left = left;
if (this.top < top) this.top = top;
if (this.right > right) this.right = right;
if (this.bottom > bottom) this.bottom = bottom;
return true;
}
return false;
}
// 取交集
public boolean intersect(Rect r) {
return intersect(r.left, r.top, r.right, r.bottom);
}
// 取并集
public void union(int left, int top, int right, int bottom) {
if ((left < right) && (top < bottom)) {
if ((this.left < this.right) && (this.top < this.bottom)) {
if (this.left > left) this.left = left;
if (this.top > top) this.top = top;
if (this.right < right) this.right = right;
if (this.bottom < bottom) this.bottom = bottom;
} else {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
}
// 取并集
public void union(Rect r) {
union(r.left, r.top, r.right, r.bottom);
}
// 更新矩形的值,扩大范围
public void union(int x, int y) {
if (x < left) {
left = x;
} else if (x > right) {
right = x;
}
if (y < top) {
top = y;
} else if (y > bottom) {
bottom = y;
}
}
RectF类与Rect类功能和用法完全一样,不同是Rect的left、topright、 bottom四个成员变量为int类型,而RectF为float类型。另外在开发中可能会用到Rect和RectF的相互转换,在RectF类中定义了两个方法用于将RectF装换成Rect:
// 将float类型的left、top、right、 bottom都以四舍五入的方式变为int类型而Rect转换成RectF就可以直接通过构造函数就可以实现转换了
public void round(Rect dst) {
dst.set(FastMath.round(left), FastMath.round(top),
FastMath.round(right), FastMath.round(bottom));
}
// 将float类型的left、top做floor()运算:返回最大的值,该值小于等于参数,并等于某个整数(如15.3、15.8都返回15)
// 将float类型的right、 bottom做ceil()运算:返回最小的值,该值大于等于参数,并等于某个整数(如15.3、15.8都返回16)
public void roundOut(Rect dst) {
dst.set((int) Math.floor(left), (int) Math.floor(top),
(int) Math.ceil(right), (int) Math.ceil(bottom));
}
public RectF(Rect r) {...}
Bitmap表示“位图”,用于存储png、jpg、gif等格式的图片数据,在Android中对图片进行处理,需要先将图片读成Bitmap对象,然后在对Bitmap对象进行操作,图片读取操作是BitmapFactory 类完成的,该类定义了一些方法用于读取不同路径下的图片数据:
// 从指定的文件路径中读取图片当然上面的方法还有很多的重载方法,能在读取图片数据的同时设置一些参数。
public static Bitmap decodeFile(String pathName)
// 通过资源id读取res/drawable或res/mipmap目录下的图片
public static Bitmap decodeResource(Resources res, int id)
// 从InputStream输入流中读取图片数据
public static Bitmap decodeStream(InputStream is)
// 从字节数组中读取图片数据
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
如果需要创建一个图片的话,在Bitmap中提供了createBitmap()方法可以创建,这个方法同样有很多的重载版本,可以在创建图片的同时指定图片的各种参数,下面提供一种比较常见的做法做参考:
// 创建一个宽和高都是500的ARGB_8888类型的空白位图对象在Bitmap类中还有一些比较常用的方法:
Bitmap bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.ARGB_8888);
// 压缩图片 format:图片格式;quality:压缩率;stream:保存压缩后的数据在Android中Bitmap是一个比较消耗资源的对象,所以在使用完成时候要释放掉Bitmap对象,一般释放Bitmap的代码如下:
public boolean compress(CompressFormat format, int quality, OutputStream stream)
// 复制一张图片 config:图片的配置;isMutable:复制后得到的图片是否可以修改,true表示可以
public Bitmap copy(Config config, boolean isMutable)
// 获取Bitmap的宽
public final int getWidth()
// 获取Bitmap的高
public final int getHeight()
// 设置Bitmap的宽
public final int setWidth()
// 设置Bitmap的高
public final int setHeight()
// 判断图片是否已经回收
public final boolean isRecycled()
// 回收Bitmap内存
public void recycle()
// 返回Bitmap的字节数
public final int getByteCount()
// 判断bitmap是否为null或者已经释放掉BimapDrawable是Android 的一种通用位图格式,也可以理解成Bitmap的另外一种表现形式。 但比Bitmap占用资源更少、性能更高。
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle(); // 释放Bitmap内存
bitmap = null; // 将bitmap对象置为null
System.gc(); // 手动调用gc(),提醒JVM释放资源
}
在BitmapDrawable中的一些常用方法,同时也提供了BitmapDrawable和Bitmap的相互转化的方法:
// 通过构造可以将Bitmap对象转换成BitmapDrawable对象
public BitmapDrawable(Bitmap bitmap)
// 将Bitmap对象设置到BitmapDrawable对象中
protected void setBitmap(Bitmap bitmap)
// 从BitmapDrawable对象中得到Bitmap对象
public final Bitmap getBitmap()
// 获取BitmapDrawable的宽,和Bitmap有一点区别
public int getIntrinsicWidth()
// 获取BitmapDrawable的高,和Bitmap有一点区别
public int getIntrinsicHeight()
// 获取BitmapDrawable的透明度
public int getAlpha()
// 获取BitmapDrawable中的Paint对象
public final Paint getPaint()