摘要:
Rect是“Rectangle”简写的英文单词,中文意思“矩形或长方形”,Rect对象持有一个矩形的四个integer坐标值,RectF对象持有一个矩形的四个float坐标值,这是两者最大的区别。从实现的方式上看,Rect是一个final类实现Parcelable接口,RectF是一个普通类实现Parcelable接口,Rect和RectF除了记录的坐标数据类型不一样外,两个类提供的方法大体上都是一样的,比如:equals()比较两个矩形的坐标是否一样、isEmpty()判断矩形是否为空、width()获取矩形的宽、height()获取矩形的高、centerX()获取矩形水平中心点坐标、centerY()获取矩形垂直中心点坐标等等
封装Rect和RectF类的目的,使用Rect或RectF对象保存坐标数据,使用封装的方法方便计算或比较两个矩形的关系。
每一个View子类或者控件在屏幕中占据一块矩形区域,用于绘制和事件处理,Rect或RectF通常被用于记录这样的一块矩形区域,TeachCourse习惯将left、top、right和bottom划分为左上角坐标(left,top)和右下角坐标(right,bottom)
一、Rect或RectF基础总结
RectF和Rect类提供的方法大部分是一样的,这里着重详细介绍Rect类的使用,在布局文件放置一个TextView,然后获取控件的矩形局域的坐标,使用Rect对象保存坐标的数据,最后调用Rect提供的一些方法计算矩形的宽、高,演示Rect类的简单使用。
- final TextView textView = new TextView(this);
- textView.setText("显示Rect存储坐标数据");
- /**
- * 设置TextView的宽度和高度,最后计算TextView的左上角和右下角的坐标
- */
- textView.setLayoutParams(new ViewGroup.LayoutParams(400, 400));
- textView.setBackgroundColor(Color.parseColor("#00BFFF"));
- textView.setGravity(Gravity.CENTER);
- textView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- int top = v.getTop();
- int left = v.getLeft();
- int right = v.getRight();
- int bottom = v.getBottom();
- /**
- * 将TextView相对父控件的坐标保存在Rect对象
- */
- mRect.left = left;
- mRect.right = right;
- mRect.top = top;
- mRect.bottom = bottom;
- textView.setText(mRect.toShortString());
- }
- });
- final Button button = new Button(this);
- /**
- * 设置button的宽度和高度,最后计算矩形局域的宽和高
- */
- ViewGroup.MarginLayoutParams params=new ViewGroup.MarginLayoutParams(800, 300);
- /**
- * 设置button的margin属性值
- */
- params.setMargins(100,DensityUtil.dip2px(this,100),100,100);
- button.setLayoutParams(params);
- button.setText("计算Rect坐标");
- button.setBackgroundColor(Color.parseColor("#7FFFAA"));
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- int top = v.getTop();
- int left = v.getLeft();
- int right = v.getRight();
- int bottom = v.getBottom();
- /**
- * 将TextView相对父控件的坐标保存在Rect对象
- */
- mRect.left = left;
- mRect.right = right;
- mRect.top = top;
- mRect.bottom = bottom;
- button.setText("宽度:"+mRect.width()+"\n"+"高度:"+mRect.height());
- }
- });
- final Button anim_btn =new Button(this);
- /**
- * 设置button的宽度和高度
- */
- params=new ViewGroup.MarginLayoutParams(800, 300);
- /**
- * 设置button的margin属性值,计算矩形局域的中心点坐标
- */
- params.setMargins(100,DensityUtil.dip2px(this,100),100,100);
- anim_btn.setLayoutParams(params);
- anim_btn.setText("计算Rect坐标");
- anim_btn.setBackgroundColor(Color.parseColor("#DDA0DD"));
- anim_btn.setGravity(Gravity.RIGHT);
- anim_btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- int top = v.getTop();
- int left = v.getLeft();
- int right = v.getRight();
- int bottom = v.getBottom();
- /**
- * 将TextView相对父控件的坐标保存在Rect对象
- */
- mRect.left = left;
- mRect.right = right;
- mRect.top = top;
- mRect.bottom = bottom;
- anim_btn.setText("水平中心点:"+mRect.centerX()+"\n垂直中心点:"+mRect.centerY());
- }
- });
正是因为每一个矩形局域包含着left、top、right和bottom四个顶点坐标,getLeft()、getTop()、getRight()和getBottom()属于View声明的方法,因此每一个View子类或者控件继承上述方法,Rect或RectF类似一个工具类,封装四个顶点坐标的计算关系,使用getLeft()、getTop()、getRight()和getBottom()需要注意两个问题:
第一个问题:getLeft()、getTop()、getRight()和getBottom()计算相对其父容器的位置
第二个问题:getLeft()、getTop()、getRight()和getBottom()计算结果为0,是因为当前View子类或控件没有绘制完成。解决办法,onClick方法点击的时候计算或者使用线程的延时计算
- /**
- * 延时获取控件相对父容器的left、top、right、bottom坐标,否则为0
- */
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- saveCoordinateToRect();
- }
- }).start();
二、Rect或RectF方法深入理解
Rect是一个final类,不属于被继承,实现Parcelable接口执行序列化,声明public作用域的四个整型属性:left、top、right和bottom,用来记录View矩形局域的四个顶点坐标。
- public Rect() {}
1、创建一个空的Rect对象,left、top、right和bottom的默认值为0
- public Rect(int left, int top, int right, int bottom) {
- this.left = left;
- this.top = top;
- this.right = right;
- this.bottom = bottom;
- }
2、创建一个指定坐标值的Rect对象,left、top、right和bottom为指定值
- public Rect(Rect r) {
- if (r == null) {
- left = top = right = bottom = 0;
- } else {
- left = r.left;
- top = r.top;
- right = r.right;
- bottom = r.bottom;
- }
- }
3、使用已知的Rect,创建一个新的Rect对象,left、top、right和bottom为已知的Rect包含的值
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Rect r = (Rect) o;
- return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
- }
4、判断当前Rect与指定的o是否同一个,相同的条件:属于同一个对象或者两者left、top、right或bottom属性值一样
- @Override
- public int hashCode() {
- int result = left;
- result = 31 * result + top;
- result = 31 * result + right;
- result = 31 * result + bottom;
- return result;
- }
5、计算Rect属性值的散列码
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(32);
- sb.append("Rect("); sb.append(left); sb.append(", ");
- sb.append(top); sb.append(" - "); sb.append(right);
- sb.append(", "); sb.append(bottom); sb.append(")");
- return sb.toString();
- }
6、以Rect(left,top-right,bottom)的格式返回矩形四个坐标值
- public String toShortString(StringBuilder sb) {
- sb.setLength(0);
- sb.append('['); sb.append(left); sb.append(',');
- sb.append(top); sb.append("]["); sb.append(right);
- sb.append(','); sb.append(bottom); sb.append(']');
- return sb.toString();
- }
7、以[left,top] [right,bottom]的格式返回矩形四个坐标值,即矩形区域左上角和右下角坐标
- public String toShortString() {
- return toShortString(new StringBuilder(32));
- }
8、以[left,top] [right,bottom]的格式返回矩形四个坐标值,即矩形区域左上角和右下角坐标,和上述方法一样
- public String flattenToString() {
- StringBuilder sb = new StringBuilder(32);
- // WARNING: Do not change the format of this string, it must be
- // preserved because Rects are saved in this flattened format.
- sb.append(left);
- sb.append(' ');
- sb.append(top);
- sb.append(' ');
- sb.append(right);
- sb.append(' ');
- sb.append(bottom);
- return sb.toString();
- }
9、以left top right bottom的格式返回矩形四个坐标值,即平铺的格式,比如:0 0 400 400或 100 100 800 300
- public static Rect unflattenFromString(String str) {
- Matcher matcher = UnflattenHelper.getMatcher(str);
- if (!matcher.matches()) {
- return null;
- }
- return new Rect(Integer.parseInt(matcher.group(1)),
- Integer.parseInt(matcher.group(2)),
- Integer.parseInt(matcher.group(3)),
- Integer.parseInt(matcher.group(4)));
- }
10、给定一个平铺格式的字符串,比如:0 0 400 400,判断是否合法,然后转换为一个Rect对象
- public void printShortString(PrintWriter pw) {
- pw.print('['); pw.print(left); pw.print(',');
- pw.print(top); pw.print("]["); pw.print(right);
- pw.print(','); pw.print(bottom); pw.print(']');
- }
11、将Rect包含的属性值以[left,top] [right,bottom]的格式写入给定的PrintWriter流中
- public final boolean isEmpty() {
- return left >= right || top >= bottom;
- }
12、判断Rect是否一个空对象,即包含的属性值是否不为0
- public final int width() {
- return right - left;
- }
13、计算矩形区域的宽度
- public final int height() {
- return bottom - top;
- }
14、计算矩形区域的高度
- public final int centerX() {
- return (left + right) >> 1;
- }
15、计算矩形区域的水平中心点,计算结果为分数则返回最接近的整型数,例如:水平中心点400
- public final int centerY() {
- return (top + bottom) >> 1;
- }
16、计算矩形区域的垂直中心点,计算结果为分数则返回最接近的整型数,例如:垂直中心点850
- public final float exactCenterX() {
- return (left + right) * 0.5f;
- }
17、计算矩形区域的水平中心点,返回结果float类型,例如:水平中心点400.0
- public final float exactCenterY() {
- return (top + bottom) * 0.5f;
- }
18、计算矩形区域的垂直中心点,返回结果float类型,例如:垂直中心点850.0
- public void setEmpty() {
- left = right = top = bottom = 0;
- }
19、将Rect对象包含的属性值设置为0
- public void set(int left, int top, int right, int bottom) {
- this.left = left;
- this.top = top;
- this.right = right;
- this.bottom = bottom;
- }
20、将Rect的属性值设置为指定的值
- public void set(Rect src) {
- this.left = src.left;
- this.top = src.top;
- this.right = src.right;
- this.bottom = src.bottom;
- }
21、复制指定的Rect对象包含的属性值
- public void offset(int dx, int dy) {
- left += dx;
- top += dy;
- right += dx;
- bottom += dy;
- }
22、在当前矩形区域的水平方向、垂直方向分别增加dx、dy距离,即扩展
- public void offsetTo(int newLeft, int newTop) {
- right += newLeft - left;
- bottom += newTop - top;
- left = newLeft;
- top = newTop;
- }
23、在当前矩形区域的水平方向、垂直方向分别偏移dx、dy距离,即水平平移dx、垂直平移dy
- public void inset(int dx, int dy) {
- left += dx;
- top += dy;
- right -= dx;
- bottom -= dy;
- }
24、在当前矩形区域的水平方向、垂直方向分别减少dx、dy距离,即缩小
- public boolean contains(int x, int y) {
- return left < right && top < bottom // check for empty first
- && x >= left && x < right && y >= top && y < bottom;
- }
25、计算指定的坐标(x,y)是否包含在矩形区域范围内,包含返回true,否则返回false
- public boolean contains(int left, int top, int right, int bottom) {
- // check for empty first
- return this.left < this.right && this.top < this.bottom
- // now check for containment
- && this.left <= left && this.top <= top
- && this.right >= right && this.bottom >= bottom;
- }
26、计算指定的left、top、right、bottom顶点是否包含在矩形区域范围内,包含返回true,否则返回false
- public boolean contains(Rect r) {
- // check for empty first
- return this.left < this.right && this.top < this.bottom
- // now check for containment
- && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
- }
27、计算指定的Rect是否包含在矩形区域范围内,包含返回true,否则返回false
- 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;
- }
28、计算当前Rect与指定的left、top、right、bottom顶点是否存在交集区域,存在返回true并且返回指定坐标,否则返回false
- public boolean intersect(Rect r) {
- return intersect(r.left, r.top, r.right, r.bottom);
- }
29、计算当前Rect与指定的Rect是否存在交集区域,存在返回true并且返回指定坐标,否则返回false
- public boolean setIntersect(Rect a, Rect b) {
- if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
- left = Math.max(a.left, b.left);
- top = Math.max(a.top, b.top);
- right = Math.min(a.right, b.right);
- bottom = Math.min(a.bottom, b.bottom);
- return true;
- }
- return false;
- }
30、计算指定的a、b是否存在交集区域,存在返回true并且返回最大坐标,否则返回false
- public boolean intersects(int left, int top, int right, int bottom) {
- return this.left < right && left < this.right && this.top < bottom && top < this.bottom;
- }
31、计算当前Rect与指定的left、top、right、bottom顶点是否存在交集区域,存在返回true并且不返回指定坐标,否则返回false
- public static boolean intersects(Rect a, Rect b) {
- return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
- }
32、计算指定的a、b是否存在交集区域,存在返回true并且不返回最大坐标,否则返回false
- 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;
- }
- }
- }
33、计算当前Rect与指定的left、top、right、bottom顶点是否存在并集区域,存在更新当前矩形区域,否则不更新
- public void union(Rect r) {
- union(r.left, r.top, r.right, r.bottom);
- }
34、计算当前Rect与指定的Rect是否存在并集区域,存在更新当前矩形区域,否则不更新
- 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;
- }
- }
35、计算当前Rect与指定的坐标(x,y)是否存在并集区域,存在更新当前矩形区域,否则不更新
- public void sort() {
- if (left > right) {
- int temp = left;
- left = right;
- right = temp;
- }
- if (top > bottom) {
- int temp = top;
- top = bottom;
- bottom = temp;
- }
- }
36、排序当前矩形区域,符合:left 37、按照指定的值缩放当前矩形区域 38、按照指定的值缩放当前矩形区域 查看View子类或控件的源码,发现基本都运用到Rect或RectF,它的作用记录View或控件的矩形区域,最后重新绘制视图。这里以TextView控件为例,首先查看Rect或RectF在源码中的使用。 打开TextView控件源码,创建一个RectF对象TEMP_RECTF,Ctrl+F快速遍历当前源码使用到TEMP_RECTF的地方: TEMP_RECTF对象拥有矩形区域的四个顶点,调用Path提供的computeBounds()计算路径控制点的边界,然后将计算的结果写入TEMP_RECTF中,调用invalidate()重新绘制。关于invalidate()方法的使用,查看View源码,中文意思“是无效,使作废”,也就是使原来绘制的视图作废,根据新left、top、right、bottom的值重新绘制视图。 在TeachCourse文章开头提供的例子中,将第三个Button点击执行的代码修改一下,点击一次就改变当前Button的矩形区域的大小,然后调用invalidate重绘,呈现一种类似动画的效果。 这里的作用同理,调用Path提供的computeBounds()计算路径控制点的边界,然后将计算的结果写入TEMP_RECTF中,然后将将TEMP_RECTF左边界减1、右边界加1赋值给另一个Rect。重点理解Rect或RectF声明的各个方法的作用后,在分析源码中使用到的Rect或RectF,发现已经很容易理解。
三、从源码分析Rect或RectF得使用
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2268.html ,谢谢支持!