[置顶] 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;
    }

}
大哭为了实现这个相框我写了557行,是的其中有大量写的很蠢很重复的代码,大可以花点时间进行优化(逃)。

实现的思路是这样的:首先要确定四个点的位置,然后根据这四个点的位置在onDraw方法中画出相框的边框,四个角框,再在onTouchListner中通过计算按下的坐标偏移量改变四个点的位置从而改变onDraw中整个相框视图的位置。

但是难点在于手势的判断以及不同情况下缩放与扩大的边界值溢出问题,这个问题大概搞了一下午。。。很烧脑

但是总的来说加强了对自定义视图的理解,发现了一些解决问题的新思路。

嗯,继续努力。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

注解使用入门(一) - 2016-07-25 18:07:18

注解使用入门(一) 本篇博客要讲解主要分为以下几个问题 注解的相关知识点 基于运行时的注解的例子解析说明 至于关于编译时的注解,待下篇博客的时候会结合例子讲解一下,目前我也正在学习当中 注解的相关知识点 提到注解,大多数人应该都不默认,在我们程序中见到的@Override,@Deprected,@SupressWarnings等等,这些都是注解,只不过是系统自己封装好的,而我们平时比较少去深入理解是怎样实现的? 1)什么是注解(Annotation): Annotation(注解)就是Java提供了一种元

android-----我眼中的Binder - 2016-07-25 18:07:17

        Binder作为进程间通信方式(IPC)的一种,算Android中比较难理解的部分了,今天计划以自己所认识的framework层的Binder原理来做个总结,好了,我们开始吧!         Android中利用Binder通信,首先肯定需要获得Binder对象了,但是系统服务和我们自定义服务Binder对象的获取方式是不一样的,原因就在于系统服务是在系统启动的时候被注册到ServiceManegr的,我们只需要通过ServiceManager.getService(String nam
使用xml作为数据交互的载体是Android中非常重要的功能,比如天气预报数据、短信备份数据、通讯录数据都可以以xml的格式通过网络传输。 为了演示Xml数据的操作,我模拟了一个短信备份的案例。 需求:界面如图1-10所示。上面是三个Button,前两个分别对应两种不同方式生成xml,第三个Button点击后解析备份的xml文件,然后将数据展现在下面的ScrollView中。短信数据是模拟的假数据。 生成的xml格式如文件1-10。 【文件1-10】 xml文件格式 1. ?xml version="1.
这篇博客我们来介绍一下责任链模式(Chain-of-responsibility Pattern),责任联模式又称为职责链模式,是 行为型设计模式 之一。顾名思义,责任链模式中存在一个链式结构,多个节点首尾相连,每个节点都可以被拆开再连接,因此,链式结构具有很好的灵活性。将这样一种结构应用于编程领域,将每一个节点看作是一个对象,每一个对象拥有不同的处理逻辑,将一个请求从链式的首段发出,沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,这就是责任链或者职责链的通俗定义。 转载请注明出处: h

GeekBand第十一周笔记 - 2016-07-25 17:07:19

本周的主要内容介绍Gradle,NDK,管理依赖和Git等 一、Gradle Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。 面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。 Coding只是软件开发中的一个部分 编译源代码 运行单元测试和集成测试 执行静态代码分析,生成分析报告 创建分布版本 部署到目标环
在安卓开发中,谷歌已经为我们提供了许多原生控件,基本上能够满足我们日常的开发需求,但是某些项目中原生控件可能达不到产品所要求的各式各样的酷炫效果或功能效果,这个时候我们只能自己自定义控件来满足项目需求,我们知道自定义控件包括三种方式: 1继承控件,即继承谷歌提供的原生控件,在此基础上提供一些原生控件不具备的功能,如github上各种酷炫效果的开源组件基本上都是采用的这种方式. 2组合控件:即组合多个原生控件来达到某些单个原生控件原本不具备的功能,这个在日常开发中应该是使用的比较多的,如基本上每个App都存

Android多点触摸交互处理 - 2016-07-25 17:07:15

安卓手机中,多点触摸是是最基本的操作,下面就使用程序进行演示多点触摸操作 一、获取触摸事件中的点击,移动和抬起事件,创建新的安卓项目,如下面代码所示,分别为MainActivity类代码和Layout布局文件,用于实现获取点击事件 1.1、MainActivity类中代码,代码中为布局对象root添加监听事件,使用switch进行对屏幕时间进行判断,用于判断事件的按下还是移动,在代码中      root=(RelativeLayout) findViewById(R.id.content);用于绑定布局
内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。 前言:ListView——列表,它作为一个非常重要的显示方式,不管是在Web中还是移动平台中,都是一个非常好的、不开或缺的展示信息的工具。在Android中,ListView控件接管了这一重担,在大量的场合下,我们都需要使用这个控件。虽然在Android 5.X时代,RecyclerView在很多地方都在逐渐取代ListView,但ListView的使用范围依然非常的广泛,它这万年老大哥的地位也不是轻易就能撼动的。下面就介绍
Android ListView使用简介 ListView 是 Android 软件开发中十分常用也十分重要的一个 UI 控件。 ListView 的每一个子项可以是一个简单的字符串,也可以是一组 View 的组合,开发者完全可以根据自己的需求来定义显示的形式。 如何使用一个 ListView 实现对数据的显示呢 ? 1. 创建 ListView 控件,已备数据显示 2. 准备要显示的数据 3. 为 ListView 构建一个数据适配器 (Adapter) 4. 绑定适配器 5. 处理 ListView
转载请标明出处: 一片枫叶的专栏 文本我们将讲解android studio打包apk,aar,jar包的相关知识。apk包就是android系统的安装包,这里没什么好说的,aar包是android中独有的类库包,而jar包是java中特有的类库包,在具体的介绍打包之前,我们先来aar包和jar包的区别。 jar包与aar包的区别 jar是java字节码文件(class文件)的归档文件,其不包含android中的资源文件等信息; aar是android中特有的归档文件,既包含字节码文件也包含android