Android自定义View实现照片裁剪框与照片裁剪功能

本文所需要实现的就是这样一种有逼格的效果:

右上角加了个图片框,按下确定可以裁剪正方形区域里的图片并显示在右上角。

实现思路:

1:首先需要自定义一个ZoomImageView来显示我们需要的图片,这个View需要让图片能够以合适的位置展现在当前布局的图片展示区域内(合适的位置值的是:如果图片长度大于屏幕,则压缩图片长度至屏幕宽度,高度等比压缩并居中显示,如果图片高度大于屏幕,则压缩图片高度至屏幕高度,长度等比压缩并居中显示。);

2:然后需要实现这个拖动的框框,该框框实现的功能有四点:拖动、扩大缩小、触摸时显示基准线、截图。

首先是布局设计image_details.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="#323441">
<ImageButton
android:id="@+id/certification_returnbtn"
android:layout_width="55dp"
android:layout_height="55dp"
android:background="@android:color/transparent"
android:padding="15dp"
android:scaleType="fitCenter"
android:src="@drawable/topbar_returnbtn"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/certification_returnbtn"
android:gravity="center_vertical"
android:text="裁剪图片"
android:textColor="@android:color/white"
android:textSize="20sp"/>
<!-- <ImageButton
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:background="@android:color/transparent"
android:padding="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_rotate_24dp"/>-->
<ImageView
android:id="@+id/testimg"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_width="55dp"
android:layout_height="55dp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.whale.nangua.pubuliuzhaopianqiang.ZoomImageView
android:id="@+id/zoom_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#303030"/>
<com.whale.nangua.pubuliuzhaopianqiang.ChoiceBorderView
android:id="@+id/zoom_choiceborder_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<Button
android:id="@+id/image_details_subbtn"
android:text="确定"
android:background="@drawable/image_details_submitbtn_shape"
android:layout_marginBottom="20dp"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</LinearLayout>

布局比较简单,两个View互相层叠。

自定义图片大小控制视图:ZoomImageView.java

代码注释很详细就不解释了。

package com.whale.nangua.pubuliuzhaopianqiang;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by nangua on 2016/7/20.
*/
public class ZoomImageView extends View {
/**
* 初始化状态常量
*/
public static final int STATUS_INIT = 1;
/**
* 用于对图片进行移动和缩放变换的矩阵
*/
private Matrix matrix = new Matrix();
/**
* 待展示的Bitmap对象
*/
private Bitmap sourceBitmap;
/**
* 记录当前操作的状态,可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
*/
private int currentStatus;
/**
* ZoomImageView控件的宽度
*/
private int width;
/**
* ZoomImageView控件的高度
*/
private int height;
/**
* ZoomImageView构造函数,将当前操作状态设为STATUS_INIT。
*
* @param context
* @param attrs
*/
public ZoomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
currentStatus = STATUS_INIT;
}
/**
* 将待展示的图片设置进来。
*
* @param bitmap 待展示的Bitmap对象
*/
public void setImageBitmap(Bitmap bitmap) {
sourceBitmap = bitmap;
invalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
// 分别获取到ZoomImageView的宽度和高度
width = getWidth();
height = getHeight();
}
}
/**
* 根据currentStatus的值来决定对图片进行什么样的绘制操作。
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
initBitmap(canvas);
canvas.drawBitmap(sourceBitmap, matrix, null);
}
float translateY;//Y轴偏移量
float translateX;//X轴偏移量
/**
* @param canvas
* @autohr nangua
* 对图片进行初始化操作,包括让图片居中,以及当图片大于屏幕宽高时对图片进行压缩。
* <p>
* 1.当图片宽度大于显示宽度、图片高度小于显示宽度:
* 设置图片宽度为显示宽度,高度缩放*(图片宽度/显示宽度)
* <p>
* 2.当图片宽度小于显示宽度、图片高度大于显示宽度:
* 设置图片高度为显示高度,宽度缩放*(图片高度/显示高 度)
* <p>
* 3.当图片宽度大于显示宽度,图片高度大于显示宽度:
* 选取差度更大的一边进行压缩,另一边等比缩放
* <p>
* 4.当图片宽度小于显示宽度,图片高度小于显示宽度:
* 选取差度更大的一边进行压缩,另一边等比缩放
*/
private void initBitmap(Canvas canvas) {
if (sourceBitmap != null) {
matrix.reset(); //重置矩阵
int bitmapWidth = sourceBitmap.getWidth(); //得到源图片宽
int bitmapHeight = sourceBitmap.getHeight(); //得到源图片高
//如果原图片大小大于控件宽高
if (bitmapWidth > width || bitmapHeight > height) {
//如果宽和高都比屏幕大,选择差度大的一边缩小,另一边等比缩小
if (bitmapWidth > width && bitmapHeight > height) {
int distanceX = Math.abs(width - bitmapWidth);
int distanceY = Math.abs(height - bitmapHeight);
float ratio;
//找出差值大的一边,进行缩小
if (distanceX >= distanceY) {
ratio = width / (bitmapWidth * 1.0f);
matrix.postScale(ratio, ratio);
//此时横轴铺满,只需要对竖轴进行平移
translateY = (height - sourceBitmap.getHeight() * ratio) / 2f;
matrix.postTranslate(0, translateY);
} else {
ratio = height / (bitmapHeight * 1.0f);
matrix.postScale(ratio, ratio);
//此时竖轴铺满,只需要对横轴进行平移
translateX = (width - sourceBitmap.getWidth() * ratio) / 2f;
matrix.postTranslate(translateX, 0); //在横纵轴上进行平移
}
//当图片宽度大于显示宽度、图片高度小于显示宽度:
} else if (bitmapWidth > width) {
// 当图片宽度大于屏幕宽度时,将图片等比例压缩,使它可以完全显示出来
float ratio = width / (bitmapWidth * 1.0f); //压缩比例
matrix.postScale(ratio, ratio);
translateY = (height - (bitmapHeight * ratio)) / 2f;
// 在纵坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(0, translateY);
//当图片宽度小于显示宽度、图片高度大于显示宽度:
} else if (bitmapHeight > height) {
// 当图片高度大于屏幕高度时,将图片等比例压缩,使它可以完全显示出来
float ratio = height / (bitmapHeight * 1.0f); //压缩比例
matrix.postScale(ratio, ratio);
translateX = (width - (bitmapWidth * ratio)) / 2f;
// 在横坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(translateX, 0);
}
} else {
// 当图片的宽高都小于屏幕宽高时,选择差度小的一边铺满,另一边等比扩大
//计算长和宽的差值
int distanceX = Math.abs(width - bitmapWidth);
int distanceY = Math.abs(height - bitmapHeight);
float ratio;
//找出差值小的一边,进行扩大
if (distanceX <= distanceY) {
ratio = width / (bitmapWidth * 1.0f);
matrix.postScale(ratio, ratio);
//此时横轴铺满,只需要对竖轴进行平移
translateY = (height - sourceBitmap.getHeight() * ratio) / 2f;
matrix.postTranslate(0, translateY);
} else {
ratio = height / (bitmapHeight * 1.0f);
matrix.postScale(ratio, ratio);
//此时竖轴铺满,只需要对横轴进行平移
translateX = (width - sourceBitmap.getWidth() * ratio) / 2f;
matrix.postTranslate(translateX, 0); //在横纵轴上进行平移
}
}
//进行绘制
canvas.drawBitmap(sourceBitmap, matrix, null);
}
}
}

重点来了,相册选取框视图:ChoiceBorderView.java

package com.whale.nangua.pubuliuzhaopianqiang;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
/**
* 相册选择框的View
* Created by nangua on 2016/7/21.
*/
public class ChoiceBorderView extends View {
private int scale = (int) this.getResources().getDisplayMetrics().density; //屏幕像素密度
private float borderHeight; //总高
private float borderWith; //总宽
private float borderLength = 200 * scale; //边框长度
private int RECT_BORDER_WITH = 3 * scale; //长方形框框粗
private int RECT_CORNER_WITH = 6 * scale; //四个角的粗
private int RECT_CORNER_HEIGHT = 20 * scale; //四个角的长度
//四个点坐标
private static float[][] four_corner_coordinate_positions;
private static int NOW_MOVE_STATE = 1; //移动状态,默认为1,Y轴=1,X轴=2
private static boolean MOVE_OR_ZOOM_STATE = true; //移动或缩放状态, true 为移动
public ChoiceBorderView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
init();
}
/**
* 初始化布局
* @param changed
* @param left
* @param top
* @param right
* @param bottom
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
borderHeight = this.getHeight();
borderWith = this.getWidth();
//初始化四个点的坐标
four_corner_coordinate_positions = new float[][]{
{(borderWith - borderLength) / 2, (borderHeight - borderLength) / 2}, //左上
{(borderWith + borderLength) / 2, (borderHeight - borderLength) / 2}, //右上
{(borderWith - borderLength) / 2, (borderHeight + borderLength) / 2}, //左下
{(borderWith + borderLength) / 2, (borderHeight + borderLength) / 2}, //右上
};
}
private void init() {
}
private int temp1 = (RECT_CORNER_WITH - RECT_BORDER_WITH) / 2; //长方形的粗半距
private int temp2 = (RECT_CORNER_WITH + RECT_BORDER_WITH) / 2; //四个角的粗半距
/**
* RECT_CORNER_WITH = 6
* RECT_BORDER_WITH =3
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
Paint paintRect = new Paint(); //初始化画笔
//画边框的画笔
paintRect.setColor(getResources().getColor(R.color.bordercolor)); //颜色
paintRect.setStrokeWidth(RECT_BORDER_WITH); //宽度
paintRect.setAntiAlias(true); //抗锯齿
paintRect.setStyle(Paint.Style.STROKE); //设置空心
canvas.drawRect(four_corner_coordinate_positions[0][0],
four_corner_coordinate_positions[0][1],
four_corner_coordinate_positions[3][0],
four_corner_coordinate_positions[3][1], paintRect);
//画四个角的画笔
paintRect.setColor(Color.WHITE);
paintRect.setStrokeWidth(RECT_CORNER_WITH);
paintRect.setAntiAlias(true);
//左上角的两根
canvas.drawLine(four_corner_coordinate_positions[0][0] - temp2,
four_corner_coordinate_positions[0][1] - temp1,
four_corner_coordinate_positions[0][0] - temp1 + RECT_CORNER_HEIGHT,
four_corner_coordinate_positions[0][1] - temp1,
paintRect);
canvas.drawLine(four_corner_coordinate_positions[0][0] - temp1,
four_corner_coordinate_positions[0][1] - temp2,
four_corner_coordinate_positions[0][0] - temp1,
four_corner_coordinate_positions[0][1] - temp1 + RECT_CORNER_HEIGHT,
paintRect);
//左下角的两根
canvas.drawLine(four_corner_coordinate_positions[2][0] - temp2,
four_corner_coordinate_positions[2][1] + temp1,
four_corner_coordinate_positions[2][0] - temp1 + RECT_CORNER_HEIGHT,
four_corner_coordinate_positions[2][1] + temp1,
paintRect);
canvas.drawLine(four_corner_coordinate_positions[2][0] - temp1,
four_corner_coordinate_positions[2][1] + temp1,
four_corner_coordinate_positions[2][0] - temp1,
four_corner_coordinate_positions[2][1] + temp1 - RECT_CORNER_HEIGHT,
paintRect);
//右上角的两根
canvas.drawLine(four_corner_coordinate_positions[1][0] + temp1,
four_corner_coordinate_positions[1][1] - temp1,
four_corner_coordinate_positions[1][0] + temp1 - RECT_CORNER_HEIGHT,
four_corner_coordinate_positions[1][1] - temp1,
paintRect);
canvas.drawLine(four_corner_coordinate_positions[1][0] + temp1,
four_corner_coordinate_positions[1][1] - temp2,
four_corner_coordinate_positions[1][0] + temp1,
four_corner_coordinate_positions[1][1] - temp1 + RECT_CORNER_HEIGHT
, paintRect);
//右下角的两根
canvas.drawLine(four_corner_coordinate_positions[3][0] + temp2,
four_corner_coordinate_positions[3][1] + temp1,
four_corner_coordinate_positions[3][0] + temp1 - RECT_CORNER_HEIGHT,
four_corner_coordinate_positions[3][1] + temp1,
paintRect);
canvas.drawLine(four_corner_coordinate_positions[3][0] + temp1,
four_corner_coordinate_positions[3][1] + temp1,
four_corner_coordinate_positions[3][0] + temp1,
four_corner_coordinate_positions[3][1] + temp1 - RECT_CORNER_HEIGHT,
paintRect);
//画扫描线
if (IF_SCANNING_SHOW) {
paintRect.setColor(Color.WHITE);
paintRect.setStrokeWidth(1);
paintRect.setAntiAlias(true);
paintRect.setStyle(Paint.Style.STROKE);
//共四根线
//竖1
canvas.drawLine(four_corner_coordinate_positions[0][0] + borderLength / 3,
four_corner_coordinate_positions[0][1] + temp1,
four_corner_coordinate_positions[2][0] + borderLength / 3,
four_corner_coordinate_positions[2][1] - temp1,
paintRect);
//竖2
canvas.drawLine(four_corner_coordinate_positions[1][0] - borderLength / 3,
four_corner_coordinate_positions[1][1] + temp1,
four_corner_coordinate_positions[3][0] - borderLength / 3,
four_corner_coordinate_positions[3][1] - temp1,
paintRect);
//横1
canvas.drawLine(four_corner_coordinate_positions[0][0] + temp1,
four_corner_coordinate_positions[0][1] + borderLength / 3,
four_corner_coordinate_positions[1][0] - temp1,
four_corner_coordinate_positions[1][1] + borderLength / 3,
paintRect);
//横2
canvas.drawLine(four_corner_coordinate_positions[2][0] + temp1,
four_corner_coordinate_positions[2][1] - borderLength / 3,
four_corner_coordinate_positions[3][0] - temp1,
four_corner_coordinate_positions[3][1] - borderLength / 3,
paintRect);
}
}
private boolean IF_SCANNING_SHOW = false;
private int lastX = 0; //上次按下的X位置
private int lastY = 0; //上次按下的Y位置
private int offsetX = 0; //X轴偏移量
private int offsetY = 0; //Y轴偏移量
static int point = -1;// 用户按下的点
private int POINT_STATE = -1; //判断用户是缩小还是放大 0放大 1缩小
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
IF_SCANNING_SHOW = true;//显示扫描线
if (isInTheCornerCircle(event.getX(), event.getY()) != -1) {
//开始缩放操作
MOVE_OR_ZOOM_STATE = false; //设置false为缩放状态
point = isInTheCornerCircle(event.getX(), event.getY());
}
lastX = x;
lastY = y;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
offsetX = x - lastX;
offsetY = y - lastY;
//判断当前是扩大还是缩小操作
judgementXandY();
//限定移动范围
//移动状态:只有在移动状态下才能移动
if (MOVE_OR_ZOOM_STATE) {
getoffsetXandoffsetY();
//四个点的坐标信息也要随之改变
for (int i = 0; i < four_corner_coordinate_positions.length; i++) {
four_corner_coordinate_positions[i][0] += offsetX;
four_corner_coordinate_positions[i][1] += offsetY;
//更新回调接口
onImageDetailsSizeChanggedl.onBorderSizeChangged(
(int) four_corner_coordinate_positions[0][0],
(int) four_corner_coordinate_positions[0][1],
(int) borderLength
);
invalidate();
}
// this.scrollBy(-offsetX, -offsetY); //这里弃用,后面改用了四点坐标移动代替背景移动
}
//在缩放状态下
else {
//按住某一个点,该点的坐标改变,其他2个点坐标跟着改变,对点坐标不变
max = Math.abs(offsetX) >= Math.abs(offsetY) ? Math.abs(offsetX) : Math.abs(offsetY);
//只有在扩大操作才进行边界范围判断
if (POINT_STATE == 0) {
getoffsetXandoffsetY(); //边界范围判断
}
//缩小操作时进行边界不能太小判断
else if (POINT_STATE == 1) {
//如果边长+max太小,直接返回
if (borderLength - max <= RECT_CORNER_HEIGHT*2-temp2) {
max = 0;
}
}
//改变坐标
changgeFourCoodinatePosition(point, offsetX, offsetY);
//更新边长
notifyNowborderLength();
//更新回调接口
onImageDetailsSizeChanggedl.onBorderSizeChangged(
(int) four_corner_coordinate_positions[0][0],
(int) four_corner_coordinate_positions[0][1],
(int) borderLength
);
invalidate();
}
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_UP:
IF_SCANNING_SHOW = false; //不显示扫描线
MOVE_OR_ZOOM_STATE = true; //回归为默认的移动状态
invalidate();
break;
}
return true;
}
/**
* 更新矩形框边长的方法
*/
private void notifyNowborderLength() {
float a = four_corner_coordinate_positions[0][0];
float b = four_corner_coordinate_positions[0][1];
float c = four_corner_coordinate_positions[1][0];
float d = four_corner_coordinate_positions[1][1];
float temp1 = (float) Math.pow(a - c, 2);
float temp2 = (float) Math.pow(b - d, 2);
borderLength = (float) Math.sqrt(temp1 + temp2);
}
/**
* POINT_STATE 为0放大, 1缩小
*/
private void judgementXandY() {
switch (point) {
case 0:
if ((offsetX <= 0 && offsetY <= 0) || (offsetX <= 0 && offsetY >= 0)) {
POINT_STATE = 0;//扩大
} else {
POINT_STATE = 1;//缩小
}
break;
case 1:
if ((offsetX >= 0 && offsetY <= 0) || (offsetX >= 0 && offsetY >= 0)) {
POINT_STATE = 0;
} else {
POINT_STATE = 1;
}
break;
case 2:
if ((offsetX <= 0 && offsetY >= 0) || (offsetX <= 0 && offsetY <= 0)) {
POINT_STATE = 0;
} else {
POINT_STATE = 1;
}
break;
case 3:
if ((offsetX >= 0 && offsetY >= 0) || (offsetX >= 0 && offsetY <= 0)) {
POINT_STATE = 0;
} else {
POINT_STATE = 1;
}
break;
}
}
/**
* 防止X和Y溢出边界的算法
*/
private void getoffsetXandoffsetY() {
//如果是移动状态
if (MOVE_OR_ZOOM_STATE) {
if ((four_corner_coordinate_positions[0][0] + offsetX <= 0) ||
(four_corner_coordinate_positions[1][0] + offsetX >= borderWith)
) {
offsetX = 0;
}
if ((four_corner_coordinate_positions[0][1] + offsetY <= 0) ||
(four_corner_coordinate_positions[2][1] + offsetY >= borderHeight)
) {
offsetY = 0;
}
}
//如果是缩放状态
else {
switch (point) {
case 0:
if ((four_corner_coordinate_positions[0][0] - max <= 0) ||
(four_corner_coordinate_positions[0][1] - max <= 0)
) {
max = 0;
}
break;
case 1:
if ((four_corner_coordinate_positions[1][0] + max >= borderWith) ||
(four_corner_coordinate_positions[1][1] - max <= 0)
) {
max = 0;
}
break;
case 2:
if ((four_corner_coordinate_positions[2][0] - max <= 0) ||
(four_corner_coordinate_positions[2][1] + max >= borderHeight)
) {
max = 0;
}
break;
case 3:
if ((four_corner_coordinate_positions[3][0] + max >= borderWith) ||
(four_corner_coordinate_positions[3][1] + max >= borderHeight)
) {
max = 0;
}
break;
}
}
} 

static int max; 

/**
* 扩大缩放方法
* 根据用户传来的点改变其他点的坐标
* 按住某一个点,该点的坐标改变,其他2个点坐标跟着改变,对点坐标不变
* 点阵示意:
* 0 1
* 2 3
*
* @param point 用户按的点
* @param offsetX X轴偏移量
* @param offsetY Y轴偏移量
*/
private void changgeFourCoodinatePosition(int point, int offsetX, int offsetY) {
switch (point) {
case 0:
if (offsetX > 0 && offsetY < 0) {
//变化0点的位置 suoxiao
four_corner_coordinate_positions[0][0] += max;
four_corner_coordinate_positions[0][1] += max;
//变化1点的Y轴
four_corner_coordinate_positions[1][1] += max;
//变化2点的X轴
four_corner_coordinate_positions[2][0] += max;
} else if (offsetX < 0 && offsetY > 0) {
//变化0点的位置 kuoda
four_corner_coordinate_positions[0][0] -= max;
four_corner_coordinate_positions[0][1] -= max;
//变化1点的Y轴
four_corner_coordinate_positions[1][1] -= max;
//变化2点的X轴
four_corner_coordinate_positions[2][0] -= max;
} else if (offsetX < 0 && offsetY < 0) {
//变化0点的位置 kuoda
four_corner_coordinate_positions[0][0] -= max;
four_corner_coordinate_positions[0][1] -= max;
//变化1点的Y轴
four_corner_coordinate_positions[1][1] -= max;
//变化2点的X轴
four_corner_coordinate_positions[2][0] -= max;
} else if (offsetX > 0 && offsetY > 0) {
//变化0点的位置 suoxiao
four_corner_coordinate_positions[0][0] += max;
four_corner_coordinate_positions[0][1] += max;
//变化1点的Y轴
four_corner_coordinate_positions[1][1] += max;
//变化2点的X轴
four_corner_coordinate_positions[2][0] += max;
}
break;
case 1:
if (offsetX > 0 && offsetY < 0) {
//变化1点的位置
four_corner_coordinate_positions[1][0] += max;
four_corner_coordinate_positions[1][1] -= max;
//变化0点的Y轴
four_corner_coordinate_positions[0][1] -= max;
//变化3点的X轴
four_corner_coordinate_positions[3][0] += max;
} else if (offsetX < 0 && offsetY > 0) {
//变化1点的位置
four_corner_coordinate_positions[1][0] -= max;
four_corner_coordinate_positions[1][1] += max;
//变化0点的Y轴
four_corner_coordinate_positions[0][1] += max;
//变化3点的X轴
four_corner_coordinate_positions[3][0] -= max;
} else if (offsetX < 0 && offsetY < 0) {
//变化1点的位置
four_corner_coordinate_positions[1][0] -= max;
four_corner_coordinate_positions[1][1] += max;
//变化0点的Y轴
four_corner_coordinate_positions[0][1] += max;
//变化3点的X轴
four_corner_coordinate_positions[3][0] -= max;
} else if (offsetX > 0 && offsetY > 0) {
//变化1点的位置
four_corner_coordinate_positions[1][0] += max;
four_corner_coordinate_positions[1][1] -= max;
//变化0点的Y轴
four_corner_coordinate_positions[0][1] -= max;
//变化3点的X轴
four_corner_coordinate_positions[3][0] += max;
}
break;
case 2:
if (offsetX > 0 && offsetY < 0) {
//变化2点的位置 suoxiao
four_corner_coordinate_positions[2][0] += max;
four_corner_coordinate_positions[2][1] -= max;
//变化0点的X轴
four_corner_coordinate_positions[0][0] += max;
//变化3点的Y轴
four_corner_coordinate_positions[3][1] -= max;
} else if (offsetX < 0 && offsetY > 0) {
//变化2点的位置 kuoda
four_corner_coordinate_positions[2][0] -= max;
four_corner_coordinate_positions[2][1] += max;
//变化0点的X轴
four_corner_coordinate_positions[0][0] -= max;
//变化3点的Y轴
four_corner_coordinate_positions[3][1] += max;
} else if (offsetX < 0 && offsetY < 0) {
//变化2点的位置 kuoda
four_corner_coordinate_positions[2][0] -= max;
four_corner_coordinate_positions[2][1] += max;
//变化0点的X轴
four_corner_coordinate_positions[0][0] -= max;
//变化3点的Y轴
four_corner_coordinate_positions[3][1] += max;
} else if (offsetX > 0 && offsetY > 0) {
//变化2点的位置 suoxiao
four_corner_coordinate_positions[2][0] += max;
four_corner_coordinate_positions[2][1] -= max;
//变化0点的X轴
four_corner_coordinate_positions[0][0] += max;
//变化3点的Y轴
four_corner_coordinate_positions[3][1] -= max;
}
break;
case 3:
if (offsetX > 0 && offsetY < 0) {
//变化3点的位置 kuoda
four_corner_coordinate_positions[3][0] += max;
four_corner_coordinate_positions[3][1] += max;
//变化1点的X轴
four_corner_coordinate_positions[1][0] += max;
//变化2点的Y轴
four_corner_coordinate_positions[2][1] += max;
} else if (offsetX < 0 && offsetY > 0) {
//变化3点的位置 suoxiao
four_corner_coordinate_positions[3][0] -= max;
four_corner_coordinate_positions[3][1] -= max;
//变化1点的X轴
four_corner_coordinate_positions[1][0] -= max;
//变化2点的Y轴
four_corner_coordinate_positions[2][1] -= max;
} else if (offsetX < 0 && offsetY < 0) {
//变化3点的位置 suoxiao
four_corner_coordinate_positions[3][0] -= max;
four_corner_coordinate_positions[3][1] -= max;
//变化1点的X轴
four_corner_coordinate_positions[1][0] -= max;
//变化2点的Y轴
four_corner_coordinate_positions[2][1] -= max;
} else if (offsetX > 0 && offsetY > 0) {
//变化3点的位置 kuoda
four_corner_coordinate_positions[3][0] += max;
four_corner_coordinate_positions[3][1] += max;
//变化1点的X轴
four_corner_coordinate_positions[1][0] += max;
//变化2点的Y轴
four_corner_coordinate_positions[2][1] += max;
}
break;
}
}
/**
* 判断按下的点在圆圈内
*
* @param x 按下的X坐标
* @param y 按下的Y坐标
* @return 返回按到的是哪个点, 没有则返回-1
* 点阵示意:
* 0 1
* 2 3
*/
private int isInTheCornerCircle(float x, float y) {
for (int i = 0; i < four_corner_coordinate_positions.length; i++) {
float a = four_corner_coordinate_positions[i][0];
float b = four_corner_coordinate_positions[i][1];
float temp1 = (float) Math.pow((x - a), 2);
float temp2 = (float) Math.pow((y - b), 2);
if (((float) RECT_CORNER_HEIGHT) >= Math.sqrt(temp1 + temp2)) {
return i;
}
}
return -1;
}
public interface onImageDetailsSizeChangged {
void onBorderSizeChangged(int x, int y, int length);
}
public onImageDetailsSizeChangged onImageDetailsSizeChanggedl;
public void setonImageDetailsSizeChangged(onImageDetailsSizeChangged onImageDetailsSizeChangged) {
this.onImageDetailsSizeChanggedl = onImageDetailsSizeChangged;
}
} 

以上所述是小编给大家介绍的Android自定义View实现照片裁剪框与照片裁剪功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android自定义View之自定义评价打分控件RatingBar实现自定义星星大小和间距

    在Android开发中,我们经常会用到对商家或者商品的评价,运用星星进行打分.然而在Android系统中自带的打分控件,RatingBar特别不好用,间距和大小无法改变.所以,我就自定义了一个特别好用的打分控件.在项目中可以直接使用,特别简单.下面直接上图: 效果图 实现原理 其实就是自定义View继承LinearLayout ,然后里面动态加了五个ImageView. 实现代码,有详细的注释 在attrs中声明的可以在xml中设置的变量 <declare-styleable name="

  • Android自定义View之酷炫数字圆环

    先看下最终的效果 一.开始实现 新建一个DoughnutView继承View public class DoughnutView extends View { } 先重写onMeasure方法. /** * 当布局为wrap_content时设置默认长宽 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int hei

  • Android自定义View实现拖动选择按钮

    本文为大家分享了Android实现拖动选择按钮的具体代码,供大家参考,具体内容如下 效果图 View代码 第一步:自定义属性 <declare-styleable name="DragView"> <attr name="icon_drag" format="reference"/> <attr name="color_circle" format="color"/> &

  • Android实现在xml文件中引用自定义View的方法分析

    本文实例讲述了Android实现在xml文件中引用自定义View的方法.分享给大家供大家参考,具体如下: 在xml中引用自定义view 方法一: <view class="com.test.copytext.CopyText" android:layout_width="fill_parent" android:layout_height="wrap_content" /> 方法二: <view class="com.

  • Android自定义View之继承TextView绘制背景

    本文实例为大家分享了TextView绘制背景的方法,供大家参考,具体内容如下 效果: 实现流程: 1.初始化:对画笔进行设置 mPaintIn = new Paint(); mPaintIn.setAntiAlias(true); mPaintIn.setDither(true); mPaintIn.setStyle(Paint.Style.FILL); mPaintIn.setColor(getResources().getColor(R.color.colorPrimary)); mPain

  • Android自定义View实现带数字的进度条实例代码

    第一步.效果展示 图1.蓝色的进度条 图2.红色的进度条 图3.多条颜色不同的进度条 图4.多条颜色不同的进度条 第二步.自定义ProgressBar实现带数字的进度条 0.项目结构 如上图所示:library项目为自定义的带数字的进度条NumberProgressBar的具体实现,demo项目为示例项目以工程依赖的方式引用library项目,然后使用自定义的带数字的进度条NumberProgressBar来做展示 如上图所示:自定义的带数字的进度条的library项目的结构图 如上图所示:de

  • Android自定义View实现广告信息上下滚动效果

    先看看效果: 实现代码: public class ScrollBanner extends LinearLayout { private TextView mBannerTV1; private TextView mBannerTV2; private Handler handler; private boolean isShow; private int startY1, endY1, startY2, endY2; private Runnable runnable; private Li

  • Android自定义View实现折线图效果

    下面就是结果图(每种状态用一个表情图片表示): 一.主页面的布局文件如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height=&quo

  • Android自定义view制作绚丽的验证码

    废话不多说了,先给大家展示下自定义view效果图,如果大家觉得还不错的话,请继续往下阅读. 怎么样,这种验证码是不是很常见呢,下面我们就自己动手实现这种效果,自己动手,丰衣足食,哈哈~ 一. 自定义view的步骤 自定义view一直被认为android进阶通向高手的必经之路,其实自定义view好简单,自定义view真正难的是如何绘制出高难度的图形,这需要有好的数学功底(后悔没有好好学数学了~),因为绘制图形经常要计算坐标点及类似的几何变换等等.自定义view通常只需要以下几个步骤: 写一个类继承

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android 自定义View实现单击和双击事件的方法

    自定义View, 1. 自定义一个Runnable线程TouchEventCountThread ,  用来统计500ms内的点击次数 2. 在MyView中的 onTouchEvent 中调用 上面的线程 3. 自定义一个Handler, 在TouchEventHandler 中 处理 统计到的点击事件, 单击, 双击, 三击, 都可以处理 核心代码如下: public class MyView extends View { ...... // 统计500ms内的点击次数 TouchEvent

  • Android 自定义View的使用介绍

    在项目开发中,可能系统自带的一些widget不能满足我们的需求,这时就需要自定义View. 通过查看系统中的常用widget如Button,TextView,EditText,他们都继承自View,所以我们在继承自定义View的时候也自然的需要继承View.1.首先新建一个类LView继承自View 复制代码 代码如下: public class LView extends View { private Paint paint; public LView(Context context) {  

随机推荐