Android自定义View实现可拖拽缩放的矩形框
程序员文章站
2022-06-22 09:05:18
本文实例为大家分享了android自定义view拖拽缩放矩形框的具体代码,供大家参考,具体内容如下在开发项目中,需要一个矩形框来实现截屏功能,并且还需要可以任意拖拽和缩放,这就需要自定义view来实现...
本文实例为大家分享了android自定义view拖拽缩放矩形框的具体代码,供大家参考,具体内容如下
在开发项目中,需要一个矩形框来实现截屏功能,并且还需要可以任意拖拽和缩放,这就需要自定义view来实现了,具体功能如下:
1.自定义view
package com.xinrui.screenshot.view; import android.content.context; import android.graphics.canvas; import android.graphics.color; import android.graphics.paint; import android.graphics.rectf; import android.support.annotation.nullable; import android.util.attributeset; import android.util.log; import android.util.typedvalue; import android.view.motionevent; import android.view.view; public class croprectview extends view { // 绘制 损害框和损害名称 private paint mpaint; private rectf mrectf; // 边缘字体 // private borderedtext mborderedtext; // 标题 或 名字 private string mtitle; // 概率 private float mconfidence; // 矩形框 corner 的角度:直角、圆角 private int mcornerangle; //直角 默认 public static final int right_corner = 0; //圆角 public static final int round_corner = 1; // remove rect private int mode; private static final int mode_outside = 0x000000aa;/*170*/ private static final int mode_inside = 0x000000bb;/*187*/ private static final int mode_point = 0x000000cc;/*204*/ private static final int mode_illegal = 0x000000dd;/*221*/ private float startx;/*start x location*/ private float starty;/*start y location*/ private float endx;/*end x location*/ private float endy;/*end y location*/ private float currentx;/*x coordinate values while finger press*/ private float currenty;/*y coordinate values while finger press*/ private float memoryx;/*the last time the coordinate values of x*/ private float memoryy;/*the last time the coordinate values of y*/ private float mcoverwidth;/*width of selection box*/ private float mcoverheight;/*height of selection box*/ private static final int accuracy = 100;/*touch accuracy*/ private int pointposition;/*vertex of a rectangle*/ private static final float minwidth = 100.0f;/*the minimum width of the rectangle*/ private static final float minheight = 200.0f;/*the minimum height of the rectangle*/ private onlocationlistener mlocationlistener;/*listen to the rect */ private static final float edge_width = 1.8f; public moveandcroprectview(context context) { this(context, null); } public moveandcroprectview(context context, @nullable attributeset attrs) { this(context, attrs, 0); } public moveandcroprectview(context context, @nullable attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); initdatas(context); } private void initdatas(context context) { mpaint = new paint(); mrectf = new rectf(); //画笔设置空心 mpaint.setstyle(paint.style.stroke); mpaint.setcolor(color.white); mpaint.setstrokewidth(2); mpaint.setantialias(true); // float textsizepx = typedvalue.applydimension(typedvalue.complex_unit_dip, // 18.0f, context.getresources().getdisplaymetrics()); // mborderedtext = new borderedtext(textsizepx); currentx = 0; currenty = 0; } private boolean firstdraw = true; @override protected void ondraw(canvas canvas) { super.ondraw(canvas); // switch (mcornerangle) { // case right_corner:// 绘制 损害框(直角矩形框) // drawrect(canvas); // break; // case round_corner:// 绘制 损害框(圆角矩形框) // drawroundrect(canvas); // break; // } if (firstdraw) { firstdraw = false; startx = mrectf.left; starty = mrectf.top; endx = mrectf.right; endy = mrectf.bottom; mcoverwidth = mrectf.width(); mcoverheight = mrectf.height(); } if (mlocationlistener != null) { mlocationlistener.locationrect(startx, starty, endx, endy); } // logutils.d("ondraw -- startx: " + startx); canvas.drawline(startx - edge_width, starty - edge_width, endx + edge_width, starty - edge_width, mpaint);/*top 上边框-*/ canvas.drawline(startx - edge_width, endy + edge_width, endx + edge_width, endy + edge_width, mpaint);/*bottom -*/ canvas.drawline(startx - edge_width, starty - edge_width, startx - edge_width, endy + edge_width, mpaint);/*left |*/ canvas.drawline(endx + edge_width, starty - edge_width, endx + edge_width, endy + edge_width, mpaint);/*right |*/ // 绘制名称 和 概率 // final string labelstring = // !textutils.isempty(mtitle) // ? string.format("%s %.2f", mtitle, (100 * mconfidence)) // : string.format("%.2f", (100 * mconfidence)); // // // 在 直角矩形框 上写字 // mborderedtext.drawtext(canvas, // startx, // starty, labelstring + "%", // mpaint); } @suppresswarnings("nullableproblems") @override public boolean ontouchevent(motionevent event) { switch (event.getaction()) { case motionevent.action_down: memoryx = event.getx(); memoryy = event.gety(); checkmode(memoryx, memoryy); break; case motionevent.action_move: { currentx = event.getx(); currenty = event.gety(); switch (mode) { case mode_illegal: recoverfromillegal(currentx, currenty); postinvalidate(); break; case mode_outside: //do nothing; break; case mode_inside://拖动 movebytouch(currentx, currenty); postinvalidate(); break; default: /*mode_point*/ movebypoint(currentx, currenty); postinvalidate(); break; } } break; case motionevent.action_up: // mpaint.setcolor(getcontext().getresources().getcolor(r.color.orange)); postinvalidate(); break; default: break; } return true; } /*点击顶点附近时的缩放处理*/ @suppresswarnings("suspiciousnamecombination") private void movebypoint(float bx, float by) { // logutils.d("movebypoint"); switch (pointposition) { case 0:/*left-up*/ mcoverwidth = math.abs(endx - bx); mcoverheight = math.abs(endy - by); //noinspection suspiciousnamecombination if (!checklegalrect(mcoverwidth, mcoverheight)) { mode = mode_illegal; } else { refreshlocation(bx, by, endx, endy); } break; case 1:/*right-up*/ mcoverwidth = math.abs(bx - startx); mcoverheight = math.abs(endy - by); if (!checklegalrect(mcoverwidth, mcoverheight)) { mode = mode_illegal; } else { refreshlocation(startx, by, bx, endy); } break; case 2:/*left-down*/ mcoverwidth = math.abs(endx - bx); mcoverheight = math.abs(by - starty); if (!checklegalrect(mcoverwidth, mcoverheight)) { mode = mode_illegal; } else { refreshlocation(bx, starty, endx, by); } break; case 3:/*right-down*/ mcoverwidth = math.abs(bx - startx); mcoverheight = math.abs(by - starty); if (!checklegalrect(mcoverwidth, mcoverheight)) { mode = mode_illegal; } else { refreshlocation(startx, starty, bx, by); } break; default: break; } } /*刷新矩形的坐标*/ private void refreshlocation(float isx, float isy, float iex, float iey) { this.startx = isx; this.starty = isy; this.endx = iex; this.endy = iey; mcoverwidth = endx - startx; mcoverheight = endy - starty; } /*检测矩形是否达到最小值*/ private boolean checklegalrect(float cheight, float cwidth) { return (cheight > minheight && cwidth > minwidth); } /*从非法状态恢复,这里处理的是达到最小值后能拉伸放大*/ private void recoverfromillegal(float rx, float ry) { if ((rx > startx && ry > starty) && (rx < endx && ry < endy)) { mode = mode_illegal; } else { mode = mode_point; } } /** * 判断点在矩形的什么位置 * @param cx * @param cy */ private void checkmode(float cx, float cy) { if (cx > startx && cx < endx && cy > starty && cy < endy) { mode = mode_inside;//矩形内部 } else if (nearbypoint(cx, cy) < 4) { mode = mode_point;//矩形点上 } else { mode = mode_outside;//矩形外部 } } /*矩形随手指移动*/ private void movebytouch(float mx, float my) {/*move center point*/ float dx = mx - memoryx; float dy = my - memoryy; startx += dx; starty += dy; if(startx<=0){ startx=0; } if(starty<=0){ starty=0; } endx = startx + mcoverwidth; endy = starty + mcoverheight; if(endx>=1920){ endx=1920; startx=endx-mcoverwidth; } if(endy>=1080){ endy=1080; starty=endy-mcoverheight; } memoryx = mx; memoryy = my; } /*判断点(inx,iny)是否靠近矩形的4个顶点*/ private int nearbypoint(float floatx, float floaty) { if ((math.abs(startx - floatx) <= accuracy && (math.abs(floaty - starty) <= accuracy))) {/*left-up angle*/ pointposition = 0; return 0; } if ((math.abs(endx - floatx) <= accuracy && (math.abs(floaty - starty) <= accuracy))) {/*right-up angle*/ pointposition = 1; return 1; } if ((math.abs(startx - floatx) <= accuracy && (math.abs(floaty - endy) <= accuracy))) {/*left-down angle*/ pointposition = 2; return 2; } if ((math.abs(endx - floatx) <= accuracy && (math.abs(floaty - endy) <= accuracy))) {/*right-down angle*/ pointposition = 3; return 3; } pointposition = 100; return 100; } // 设置矩形框 public void setrectf(rectf rectf) { this.mrectf = rectf; } public void settitle(string title) { mtitle = title; } public void setconfidence(float confidence) { mconfidence = confidence; } public void setcornerangle(int cornerangle) { this.mcornerangle = cornerangle; } // 绘制 损害框(直角矩形框) private void drawrect(canvas canvas) { canvas.drawrect(mrectf, mpaint); // 绘制名称 和 概率 // final string labelstring = // !textutils.isempty(mtitle) // ? string.format("%s %.2f", mtitle, (100 * mconfidence)) // : string.format("%.2f", (100 * mconfidence)); // 在 直角矩形框 上写字 // mborderedtext.drawtext(canvas, // mrectf.left, // mrectf.top, labelstring + "%", // mpaint); } // 绘制 损害框(圆角矩形框) private void drawroundrect(canvas canvas) { float cornersize = math.min(mrectf.width(), mrectf.height()) / 8.0f; canvas.drawroundrect(mrectf, cornersize, cornersize, mpaint); // 绘制名称 和 概率 // final string labelstring = // !textutils.isempty(mtitle) // ? string.format("%s %.2f", mtitle, (100 * mconfidence)) // : string.format("%.2f", (100 * mconfidence)); // 在 圆角矩形框 上写字 // mborderedtext.drawtext(canvas, // mrectf.left + cornersize, // mrectf.top, labelstring + "%", // mpaint); } public void setlocationlistener(onlocationlistener mlocationlistener) { this.mlocationlistener = mlocationlistener; } public interface onlocationlistener { void locationrect(float startx, float starty, float endx, float endy); } }
2.activity里的应用
package com.xinrui.screenshot; import android.app.activity; import android.graphics.rectf; import android.os.bundle; import android.util.log; import android.widget.relativelayout; import com.xinrui.screenshot.view.croprectview; public class mainactivity extends activity { private relativelayout main_area; croprectview croprectview; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); } private void initview(){ main_area = (relativelayout)findviewbyid(r.id.main_area); croprectview = (croprectview)findviewbyid(r.id.main_img); rectf rectf = new rectf(660, 240, 1260, 840); croprectview.setrectf(rectf); croprectview.setlocationlistener(new croprectview.onlocationlistener() { @override public void locationrect(float startx, float starty, float endx, float endy) { log.e("mainactivity","[ startx:(" + startx + ")--starty:(" + starty + ")--endx:(" + endx + ")--endy:(" + endy + ") ]"); } }); } }
3.activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_area" android:layout_width="match_parent" android:layout_height="match_parent"> <com.xinrui.screenshot.view.croprectview android:id="@+id/main_img" android:layout_centerinparent="true" android:layout_width="match_parent" android:layout_height="match_parent"/> </relativelayout>
大功告成。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Android自定义View实现可以拖拽的GridView
-
Android自定义ViewGroup实现可滚动的横向布局(2)
-
自定义View简单实现图片的手指移动和两指缩放
-
Android进阶之自定义View(2)高仿钉钉运动步数实现可动的进度圆环(上)
-
Android自定义ListView实现仿QQ可拖拽列表功能
-
Android自定义View实现简单的圆形Progress效果
-
Android自定义ListView实现仿QQ可拖拽列表功能
-
Android自定义View实现简单的圆形Progress效果
-
轻松实现可扩展自定义的Android滚轮时间选择控件
-
Android自定义控件实现可左右滑动的导航条