android可扩展自己的定义,运动图像裁剪框
在实际项目中,常常要制作一个简易的图像裁剪功能,即获取一张图片。并用一个遮罩层选择目标范围并截取保存的功能。例如以下图所看到的:
在此分享下该自己定义视图的制作过程。
需求说明
整一个视图包括一个透明的遮罩层,一个透明带白色边框的矩形。要实现的功能是:
- 点击矩形框外围:无不论什么响应
- 点击矩形框内部:可随手指移动而移动
- 点击矩形框的4个顶点:可进行对角顶点坐标不变的情况下的矩形的缩放,同一时候边框变色
以下是实现该功能的完整源代码
/**
* Created by Farble on 2015/3/10.
*/
public class PhotoCropView extends View {
private static final String TAG = "PhotoCropView"; private onLocationListener locationListener;/*listen to the Rect */
private onChangeLocationlistener changeLocationlistener;/*listening position changed */ 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 static final int minWidth = 100;/*the minimum width of the rectangle*/
private static final int minHeight = 200;/*the minimum height of the rectangle*/ private static final int START_X = 200;
private static final int START_Y = 200; private static final float EDGE_WIDTH = 1.8f;
private static final int ACCURACY= 15;/*touch accuracy*/ private int pointPosition;/*vertex of a rectangle*/ private int sX;/*start X location*/
private int sY;/*start Y location*/
private int eX;/*end X location*/
private int eY;/*end Y location*/ private int pressX;/*X coordinate values while finger press*/
private int pressY;/*Y coordinate values while finger press*/ private int memonyX;/*the last time the coordinate values of X*/
private int memonyY;/*the last time the coordinate values of Y*/ private int coverWidth = 300;/*width of selection box*/
private int coverHeight = 400;/*height of selection box*/ private Paint mPaint;
private Paint mPaintLine;
private Bitmap mBitmapCover;
private Bitmap mBitmapRectBlack;
private PorterDuffXfermode xfermode;/*paint mode*/ public PhotoCropView(Context context) {
super(context);
init();
} public PhotoCropView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public PhotoCropView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} @SuppressWarnings("deprecation")
private void init() {
sX = START_X;
sY = START_Y;
WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int width = manager.getDefaultDisplay().getWidth();
int height = manager.getDefaultDisplay().getHeight();
mBitmapCover = makeBitmap(width, height, 0x5A000000, 0, 0);
mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight); eX = sX + coverWidth;
eY = sY + coverHeight;
pressX = 0;
pressY = 0; xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); mPaint = new Paint();
mPaint.setAntiAlias(true); mPaintLine = new Paint();
mPaintLine.setColor(Color.WHITE);
mPaintLine.setStrokeWidth(2.0f);
} /*生成bitmap*/
private Bitmap makeBitmap(int mwidth, int mheight, int resource, int staX, int staY) {
Bitmap bm = Bitmap.createBitmap(mwidth, mheight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(resource);
c.drawRect(staX, staY, mwidth, mheight, p);
return bm;
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setFilterBitmap(false);
int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG); canvas.drawBitmap(mBitmapCover, 0, 0, mPaint);
mPaint.setXfermode(xfermode);
canvas.drawBitmap(mBitmapRectBlack, sX, sY, mPaint);
if (locationListener != null) {
locationListener.locationRect(sX, sY, eX, eY);
}
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
canvas.drawLine((float) sX - EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) sY - EDGE_WIDTH, mPaintLine);/*up -*/
canvas.drawLine((float) sX - EDGE_WIDTH, (float) eY + EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*down -*/
canvas.drawLine((float) sX - EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) sX - EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*left |*/
canvas.drawLine((float) eX + EDGE_WIDTH, (float) sY - EDGE_WIDTH, (float) eX + EDGE_WIDTH, (float) eY + EDGE_WIDTH, mPaintLine);/*righ |*/ } @SuppressWarnings("NullableProblems")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (changeLocationlistener != null) {
changeLocationlistener.locationChange("change self");
} else {
changeLocationlistener = null;
} memonyX = (int) event.getX();
memonyY = (int) event.getY();
checkMode(memonyX, memonyY);
break;
case MotionEvent.ACTION_MOVE: {
switch (MODE) {
case MODE_ILLEGAL:
pressX = (int) event.getX();
pressY = (int) event.getY();
recoverFromIllegal(pressX, pressY);
postInvalidate();
break;
case MODE_OUTSIDE:
//do nothing;
break;
case MODE_INSIDE:
pressX = (int) event.getX();
pressY = (int) event.getY();
moveByTouch(pressX, pressY);
postInvalidate();
break;
default:
/*MODE_POINT*/
pressX = (int) event.getX();
pressY = (int) event.getY();
mPaintLine.setColor(getContext().getResources().getColor(R.color.orange));
moveByPoint(pressX, pressY);
postInvalidate();
break;
}
}
break;
case MotionEvent.ACTION_UP:
mPaintLine.setColor(Color.WHITE);
postInvalidate();
break;
default:
break;
}
return true;
} /*从非法状态恢复,这里处理的是达到最小值后能拉伸放大*/
private void recoverFromIllegal(int rx, int ry) {
if ((rx > sX && ry > sY) && (rx < eX && ry < eY)) {
MODE = MODE_ILLEGAL;
} else {
MODE = MODE_POINT;
}
} private void checkMode(int cx, int cy) {
if (cx > sX && cx < eX && cy > sY && cy < eY) {
MODE = MODE_INSIDE;
} else if (nearbyPoint(cx, cy) < 4) {
MODE = MODE_POINT;
} else {
MODE = MODE_OUTSIDE;
}
} /*推断点(inX,inY)是否靠近矩形的4个顶点*/
private int nearbyPoint(int inX, int inY) {
if ((Math.abs(sX - inX) <= ACCURACY && (Math.abs(inY - sY) <= ACCURACY))) {/*left-up angle*/
pointPosition = 0;
return 0;
}
if ((Math.abs(eX - inX) <= ACCURACY && (Math.abs(inY - sY) <= ACCURACY))) {/*right-up angle*/
pointPosition = 1;
return 1;
}
if ((Math.abs(sX - inX) <= ACCURACY && (Math.abs(inY - eY) <= ACCURACY))) {/*left-down angle*/
pointPosition = 2;
return 2;
}
if ((Math.abs(eX - inX) <= ACCURACY && (Math.abs(inY - eY) <= ACCURACY))) {/*right-down angle*/
pointPosition = 3;
return 3;
}
pointPosition = 100;
return 100;
} /*刷新矩形的坐标*/
private void refreshLocation(int isx, int isy, int iex, int iey) {
this.sX = isx;
this.sY = isy;
this.eX = iex;
this.eY = iey;
} /*矩形随手指移动*/
private void moveByTouch(int mx, int my) {/*move center point*/
int dX = mx - memonyX;
int dY = my - memonyY; sX += dX;
sY += dY; eX = sX + coverWidth;
eY = sY + coverHeight; memonyX = mx;
memonyY = my; } /*检測矩形是否达到最小值*/
private boolean checkLegalRect(int cHeight, int cWidth) {
return (cHeight > minHeight && cWidth > minWidth);
} /*点击顶点附近时的缩放处理*/
@SuppressWarnings("SuspiciousNameCombination")
private void moveByPoint(int bx, int by) {
switch (pointPosition) {
case 0:/*left-up*/
coverWidth = Math.abs(eX - bx);
coverHeight = Math.abs(eY - by);
//noinspection SuspiciousNameCombination
if (!checkLegalRect(coverWidth, coverHeight)) {
MODE = MODE_ILLEGAL;
} else {
mBitmapRectBlack = null;
mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
refreshLocation(bx, by, eX, eY);
}
break;
case 1:/*right-up*/
coverWidth = Math.abs(bx - sX);
coverHeight = Math.abs(eY - by);
if (!checkLegalRect(coverWidth, coverHeight)) {
MODE = MODE_ILLEGAL;
} else {
mBitmapRectBlack = null;
mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
refreshLocation(sX, by, bx, eY);
}
break;
case 2:/*left-down*/
coverWidth = Math.abs(eX - bx);
coverHeight = Math.abs(by - sY);
if (!checkLegalRect(coverWidth, coverHeight)) {
MODE = MODE_ILLEGAL;
} else {
mBitmapRectBlack = null;
mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
refreshLocation(bx, sY, eX, by);
}
break;
case 3:/*right-down*/
coverWidth = Math.abs(bx - sX);
coverHeight = Math.abs(by - sY);
if (!checkLegalRect(coverWidth, coverHeight)) {
MODE = MODE_ILLEGAL;
} else {
mBitmapRectBlack = null;
mBitmapRectBlack = makeBitmap(coverWidth, coverHeight, 0xff000000, coverWidth, coverHeight);
refreshLocation(sX, sY, bx, by);
}
break;
default:
break;
}
} public void setLocationListener(onLocationListener locationListener) {
this.locationListener = locationListener;
} public interface onLocationListener {
public void locationRect(int startX, int startY, int endX, int endY);
} public interface onChangeLocationlistener {
@SuppressWarnings("SameParameterValue")
public void locationChange(String msg);
}
}
简要说明
1.可移动的透明矩形框通过PorterDuffXfermode(在还有一篇博文中有介绍,可点击这里查看)来实现
2.矩形边框的移动。缩放主要由onTouch事件做处理
3.onLocationListener 用于侦听矩形的坐标(终于可通过实现内部方法确定图像须要截取的位置)
3.onLocationListener 用于侦听矩形的坐标(终于可通过实现内部方法确定图像须要截取的位置)
怎样使用PhotoCropView
在布局文件里导入
<com.xxx.PhotoCropView
android:id="@+id/test"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/>
绑定并设置侦听器:
mCropView = (PhotoCropView)mActivity.findViewById(R.id.photo_crop_photocrop);
mCropView .setLocationListener(this);
获取坐标信息:
@Override
public void locationRect(int startX, int startY, int endX, int endY) {
Log.d("[ "+startX+"--"+startY+"--"+endX+"--"+endY+" ]");
}
扩展该视图
在此基础上可进行进一步的扩展,如:
1.进一步改动边框的色值做警示之用
2.舍弃边框改为添加4个边角
等等,可自行在onDraw()方法及外部方法中进行扩展如:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setFilterBitmap(false);
int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null,
Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG); canvas.drawBitmap(mBitmapCover, 0, 0, mPaint);
mPaint.setXfermode(xfermode);
canvas.drawBitmap(mBitmapRectBlack, sX, sY, mPaint);
if (locationListener != null) {
locationListener.locationRect(sX, sY, eX, eY);
}
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
/*在此加入4个边角...*/
}
致读者
首先感谢您阅读此文,因为本人能力有限。不免出现一些疏漏,错误。假设您有发现不论什么错误。不妥,或有更好的实现方式,可私信我。
Thanks advanced!
版权声明:本文博主原创文章。博客,未经同意不得转载。
android可扩展自己的定义,运动图像裁剪框的更多相关文章
- Android UI--ViewPager扩展Tab标签指示
Android UI--ViewPager扩展Tab标签指示 2013年8月30日出来冒冒泡 ViewPager这个控件已经不算是陌生的了,各种玩Android的小伙伴们都有发表相应的文章来讲它.我看 ...
- Android开发技巧——定制仿微信图片裁剪控件
拍照--裁剪,或者是选择图片--裁剪,是我们设置头像或上传图片时经常需要的一组操作.上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现. 背景 下面的需求都来自产品. 裁剪图片要像微信那样,拖动 ...
- Android external扩展工程
Android的扩展工程包含在external文件夹中,这是一些经过修改后适应Android系统的开源工程,这些工程有些在主机上运行,有些在目标机上运行: 工程名称 工程描述 aes 高级加密标 ...
- Android 举例说明自己的定义Camera图片和预览,以及前后摄像头切换
如何调用本地图片,并调用系统拍摄的图像上一博文解释(http://blog.csdn.net/a123demi/article/details/40003695)的功能. 而本博文将通过实例实现自己定 ...
- Android 7.0+相机、相册、裁剪适配问题
Android 7.0+相机.相册.裁剪适配问题 在manifest中: <provider android:name="android.support.v4.content.File ...
- Android中怎样做到自己定义的广播仅仅能有指定的app接收
今天没吊事.又去面试了,详细哪家公司就不说了,由于我在之前的blog中注明了那些家公司的名字,结果人家给我私信说我泄露他们的题目.好吧,我错了... 事实上当我们已经在工作的时候.我们能够在空暇的时间 ...
- Android画图系列(二)——自己定义View绘制基本图形
这个系列主要是介绍下Android自己定义View和Android画图机制.自己能力有限.假设在介绍过程中有什么错误.欢迎指正 前言 在上一篇Android画图系列(一)--自己定义View基础中我们 ...
- 003android初级篇之【转】Android开发中颜色的定义方法
正好用到颜色的定义,但脑子里没有记住具体,转载一篇加强印象 1.使用Color类的常量,如: int color = Color.BLUE; // 创建一个蓝色 是使用Android提供的颜色 int ...
- android CheckBox控件的定义及事件监听
http://www.beijibear.com/index.php?aid=336 android CheckBox控件的定义及事件监听,本例实现CheckBox控件的定义及点击事件的监听并显示结果 ...
随机推荐
- EF 批量 循环删除
var list = db.T_xAppRecord.Where(u => u.Id == 1).ToList(); //2.0 遍历集合,将 要删除的 对象 的代理对象的State 设置为 D ...
- Android如何获得手机power_profile.xml文件
上的能量消耗进行最近的测试,阅读文章一个月,最后,我们发现了一些新的想法,但产生的问题.那 工作无法再进行下去. 在Android手机中,对于手机中的每一个部件(cpu.led.gps.3g等等)执行 ...
- poj 2309 BST 使用树阵lowbit
假设领悟了树阵lowbit,这个问题很简单,底部是奇数,使用lowbit(x)寻找x父亲,然后x父亲-1是的最大数量 至于lowbit问题是如何计算,寻找x父亲,事实上x+2^x二进制结束0的数量. ...
- 前端构建工具gulp
前端构建工具gulp使用 前端自动化流程工具,用来合并文件,压缩等. Gulp官网 http://gulpjs.com/ Gulp中文网 http://www.gulpjs.com.cn/ Gul ...
- centos6.4设备hadoop-2.5.1(完全分布式)
环境介绍: 在这两种装备centos6.4(32位置)的server安装Hadoop-2.5.1分布式集群(2台机器,主要试验用.哈哈). 1.改动主机名和/etc/hosts文件 1)改动主机名(非 ...
- 文章3说话 微信商城云server创建后台
一个. 应用server资源 想要进行微信开发.少不了后台server端程序的开发,那么我们首先就要申请server资源.眼下有非常多云server可选,比方新浪的sae ...
- Java 将字节数组转化为16进制的多种方案
很多时候我们需要将字节数组转化为16进制字符串来保存,尤其在很多加密的场景中,例如保存密钥等.因为字节数组,除了写入文件或者以二进制的形式写入数据库以外,无法直接转为为字符串,因为字符串结尾有\0,当 ...
- 开源NetWorkSocket通讯组件
开源NetWorkSocket通讯组件 前言 在<化茧成蝶,开源NetWorkSocket通讯组件>发表之后,收到大家很多个star,在此感谢!更可贵的是,一些网友提出了许多好建议,经 ...
- [渣译文] SignalR 2.0 系列:SignalR的高频实时通讯
原文:[渣译文] SignalR 2.0 系列:SignalR的高频实时通讯 英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.N ...
- ArcSDE SDK For Java二次开发介绍、演示样例
在一个工作中,遇到了须要java后台来查询ArcGIS 中用到的Oracle数据库空间数据,因为对ArcGIS空间数据首次接触,仅仅知道Oracle能够使用ST_GEOMETRY字段存储,例如以下图 ...