Android自定义View实现可拖拽缩放的矩形框
更新时间:2020年5月21日 16:23 点击:1377
本文实例为大家分享了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子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
antdesign-vue结合sortablejs实现两个table相互拖拽排序功能
这篇文章主要介绍了antdesign-vue结合sortablejs实现两个table相互拖拽排序功能,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-09Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
- 深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
- TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
android.os.BinderProxy cannot be cast to com解决办法
本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20- 这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
- 下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
- 首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
- 下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
用Intel HAXM给Android模拟器Emulator加速
Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20- 在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
- SwiftUI是一种使用Swift语言在苹果设备上构建用户界面的创新且简单的方式,下面这篇文章主要给大家介绍了关于SwiftUI图片缩放、拼图等处理的相关资料,需要的朋友可以参考下...2021-08-23
Android开发中布局中的onClick简单完成多控件时的监听的利与弊
本文章来为各位介绍一篇关于Android开发中布局中的onClick简单完成多控件时的监听的利与弊的例子,希望这个例子能够帮助到各位朋友. 首先在一个控件加上这么一句:and...2016-09-20