精华内容
下载资源
问答
  • 支持图片自动裁剪 支持图片手动不规则裁剪 2 使用 Step 1. 添加JitPack仓库 在项目的build.gradle添加JitPack仓库 allprojects { repositories { ... maven { url "https://jitpack.io" } } } Step 2. 添加依赖 ...
  • IDCardCamera 项目地址:wildma/IDCardCamera  ...简介:Android 自定义相机实现身份证拍照,并加入自动对焦与图片不规则裁剪 更多:作者 提 Bug  标签:     README of English 效果图 ...

    IDCardCamera

    项目地址:wildma/IDCardCamera 

    简介:Android 自定义相机实现身份证拍照,并加入自动对焦与图片不规则裁剪

    更多:作者   提 Bug   

    标签:

     

    jitpack  

    README of English

    效果图

    身份证来源网络虚拟构造

    展开全文
  • 支持图片自动裁剪 支持图片手动不规则裁剪 2 使用 Step 1. 添加JitPack仓库 在项目的build.gradle添加JitPack仓库 allprojects { repositories { ... maven { url "https://jitpack.io" } } } Step 2. 添加依赖...
  • <div><p>Tiny.FileCompressOptions options=...出现图片被自动裁剪的情况,按照默认的就没有裁剪,有没有大佬知道原因</p><p>该提问来源于开源项目:Sunzxyong/Tiny</p></div>
  • 当Glide访问同一个uri的时候,默认是有...前段时间,应公司要求实现一个自定义相机,需要有自动对焦和图片不规则裁剪功能,其实难点主要也是这2个功能。经Google搜索,发现并没有现成的轮子。最后通过各种查找资料...

    https://www.jianshu.com/p/5e3cb0c63cd5

    当Glide访问同一个uri的时候,默认是有缓存 的,当uri的图片改变时候,glide默认还是加载之前的图片,所以要清除glide的缓存

    1 前言

    前段时间,应公司要求实现一个自定义相机,需要有自动对焦和图片不规则裁剪功能,其实难点主要也是这2个功能。经Google搜索,发现并没有现成的轮子。最后通过各种查找资料,自己封装了一个,效果图如下:

    身份证来源网络虚拟构造

    2 使用

    Step 1. 添加JitPack仓库

    在项目的build.gradle添加JitPack仓库

    allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }
    

    Step 2. 添加依赖

    在需要使用的module中添加依赖

    dependencies {
        compile 'com.github.wildma:IDCardCamera:1.0.0'
    }
    

    或者引用本地lib

    compile project(':idcardcamera')
    

    Step 3. 调用CameraActivity类的toCameraActivity方法打开拍照界面

    CameraActivity.toCameraActivity(this, CameraActivity.TYPE_IDCARD_FRONT);
    

    Step 4. 在onActivityResult方法中获取裁剪后的图片

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == CameraActivity.REQUEST_CODE && resultCode == CameraActivity.RESULT_CODE) {
                //获取图片路径,显示图片
                final String path = CameraActivity.getImagePath(data);
                if (!TextUtils.isEmpty(path)) {
                    imageView.setImageBitmap(BitmapFactory.decodeFile(path));
                }
            }
        }
    

    3 功能特点

    自定义相机的代码我就不重复造轮子了,网上很多,我找了个UI比较不错的项目 CertificateCamera ,然后在该项目的基础上进行功能增加的,主要增加的内容如下:

    1. 解决该项目拍照不成功的问题(该项目代码有问题,博主也一直没有去修改,这里帮他改过来了)
    2. 增加自动对焦功能
    3. 增加图片不规则裁剪功能

    3.1 自动对焦

    实现自动对焦有多种方式,这里列举下:

    3.1.1 使用原生API

    1. 调用autoFocus方法,如下:
            camera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
                }
            });
    

    结论:不满足,大部分手机只对焦一次。

    1. 设置对焦模式,如下:
    camera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
    

    camera.getParameters().setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
    

    结论:不满足,不能兼容所有手机。

    3.1.2 使用定时器

    即使用一个定时器,每隔一段时间进行自动对焦。代码如下:

    package com.wildma.idcardcamera.camera;
    
    import android.annotation.SuppressLint;
    import android.hardware.Camera;
    import android.os.AsyncTask;
    import android.os.Build;
    import android.util.Log;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.concurrent.RejectedExecutionException;
    
    public class AutoFocusManager implements Camera.AutoFocusCallback {
    
        private static final String TAG = AutoFocusManager.class.getSimpleName();
    
        private static final long AUTO_FOCUS_INTERVAL_MS = 2000L;
        private static final Collection<String> FOCUS_MODES_CALLING_AF;
    
        static {
            FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
            FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
            FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
        }
    
        private final boolean            useAutoFocus;
        private final Camera             camera;
        private       boolean            stopped;
        private       boolean            focusing;
        private       AsyncTask<?, ?, ?> outstandingTask;
    
        public AutoFocusManager(Camera camera) {
            this.camera = camera;
            String currentFocusMode = camera.getParameters().getFocusMode();
            useAutoFocus = FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
            //  Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
            start();
        }
    
        @Override
        public synchronized void onAutoFocus(boolean success, Camera theCamera) {
            focusing = false;
            autoFocusAgainLater();
        }
    
        @SuppressLint("NewApi")
        private synchronized void autoFocusAgainLater() {
            if (!stopped && outstandingTask == null) {
                AutoFocusTask newTask = new AutoFocusTask();
                try {
                    if (Build.VERSION.SDK_INT >= 11) {
                        newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                    } else {
                        newTask.execute();
                    }
                    outstandingTask = newTask;
                } catch (RejectedExecutionException ree) {
                    Log.w(TAG, "Could not request auto focus", ree);
                }
            }
        }
    
        public synchronized void start() {
            if (useAutoFocus) {
                outstandingTask = null;
                if (!stopped && !focusing) {
                    try {
                        camera.autoFocus(this);
                        Log.w(TAG, "自动对焦");
                        focusing = true;
                    } catch (RuntimeException re) {
                        // Have heard RuntimeException reported in Android 4.0.x+;
                        // continue?
                        Log.w(TAG, "Unexpected exception while focusing", re);
                        // Try again later to keep cycle going
                        autoFocusAgainLater();
                    }
                }
            }
        }
    
        private synchronized void cancelOutstandingTask() {
            if (outstandingTask != null) {
                if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
                    outstandingTask.cancel(true);
                }
                outstandingTask = null;
            }
        }
    
        public synchronized void stop() {
            stopped = true;
            if (useAutoFocus) {
                cancelOutstandingTask();
                // Doesn't hurt to call this even if not focusing
                try {
                    camera.cancelAutoFocus();
                } catch (RuntimeException re) {
                    // Have heard RuntimeException reported in Android 4.0.x+;
                    // continue?
                    Log.w(TAG, "Unexpected exception while cancelling focusing", re);
                }
            }
        }
    
        private final class AutoFocusTask extends AsyncTask<Object, Object, Object> {
            @Override
            protected Object doInBackground(Object... voids) {
                try {
                    Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
                } catch (InterruptedException e) {
                }
                start();
                return null;
            }
        }
    
    }
    
    

    结论:虽然可以实现,但是你对比下手机自带的相机,发现并不是每隔一段时间进行自动对焦的,都是通过移动手机后才自动对焦的,所以这种方式是不合理的。

    3.1.3 使用传感器

    即根据传感器来判断手机的运动状态,如果手机从静止状态变成运行状态后再次进入静止状态,此时就是手机的对焦时机。代码如下:

    package com.wildma.idcardcamera.camera;
    
    import android.app.Activity;
    import android.content.Context;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    import android.util.Log;
    
    import java.util.Calendar;
    
    public class SensorControler implements SensorEventListener {
        public static final String TAG = "SensorControler";
        private SensorManager mSensorManager;
        private Sensor        mSensor;
        private int           mX, mY, mZ;
        private long lastStaticStamp = 0;
        Calendar mCalendar;
        public static final int DELEY_DURATION = 500;
        private static SensorControler mInstance;
        private int foucsing = 1;  //1 表示没有被锁定 0表示被锁定
    
        boolean isFocusing = false;
        boolean canFocusIn = false;  //内部是否能够对焦控制机制
        boolean canFocus   = false;
    
        public static final int STATUS_NONE   = 0;
        public static final int STATUS_STATIC = 1;
        public static final int STATUS_MOVE   = 2;
        private             int STATUE        = STATUS_NONE;
    
        private SensorControler(Context context) {
            mSensorManager = (SensorManager) context.getSystemService(Activity.SENSOR_SERVICE);
            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// TYPE_GRAVITY
        }
    
        public static SensorControler getInstance(Context context) {
            if (mInstance == null) {
                mInstance = new SensorControler(context);
            }
            return mInstance;
        }
    
        public void onStart() {
            restParams();
            canFocus = true;
            mSensorManager.registerListener(this, mSensor,
                    SensorManager.SENSOR_DELAY_NORMAL);
        }
    
        public void onStop() {
            mSensorManager.unregisterListener(this, mSensor);
            canFocus = false;
        }
    
        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
    
        }
    
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.sensor == null) {
                return;
            }
    
            if (isFocusing) {
                restParams();
                return;
            }
    
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                int x = (int) event.values[0];
                int y = (int) event.values[1];
                int z = (int) event.values[2];
                mCalendar = Calendar.getInstance();
                long stamp = mCalendar.getTimeInMillis();// 1393844912
    
                int second = mCalendar.get(Calendar.SECOND);// 53
    
                if (STATUE != STATUS_NONE) {
                    int px = Math.abs(mX - x);
                    int py = Math.abs(mY - y);
                    int pz = Math.abs(mZ - z);
                    //                Log.d(TAG, "pX:" + px + "  pY:" + py + "  pZ:" + pz + "    stamp:"
                    //                        + stamp + "  second:" + second);
                    double value = Math.sqrt(px * px + py * py + pz * pz);
                    if (value > 1.4) {
                        //                    textviewF.setText("检测手机在移动..");
                        //                    Log.i(TAG,"mobile moving");
                        STATUE = STATUS_MOVE;
                    } else {
                        //                    textviewF.setText("检测手机静止..");
                        //                    Log.i(TAG,"mobile static");
                        //上一次状态是move,记录静态时间点
                        if (STATUE == STATUS_MOVE) {
                            lastStaticStamp = stamp;
                            canFocusIn = true;
                        }
    
                        if (canFocusIn) {
                            if (stamp - lastStaticStamp > DELEY_DURATION) {
                                //移动后静止一段时间,可以发生对焦行为
                                if (!isFocusing) {
                                    canFocusIn = false;
                                    //                                onCameraFocus();
                                    if (mCameraFocusListener != null) {
                                        mCameraFocusListener.onFocus();
                                    }
                                    //                                Log.i(TAG,"mobile focusing");
                                }
                            }
                        }
    
                        STATUE = STATUS_STATIC;
                    }
                } else {
                    lastStaticStamp = stamp;
                    STATUE = STATUS_STATIC;
                }
    
                mX = x;
                mY = y;
                mZ = z;
            }
        }
    
        /**
         * 重置参数
         */
        private void restParams() {
            STATUE = STATUS_NONE;
            canFocusIn = false;
            mX = 0;
            mY = 0;
            mZ = 0;
        }
    
        /**
         * 对焦是否被锁定
         *
         * @return
         */
        public boolean isFocusLocked() {
            if (canFocus) {
                return foucsing <= 0;
            }
            return false;
        }
    
        /**
         * 锁定对焦
         */
        public void lockFocus() {
            isFocusing = true;
            foucsing--;
            Log.i(TAG, "lockFocus");
        }
    
        /**
         * 解锁对焦
         */
        public void unlockFocus() {
            isFocusing = false;
            foucsing++;
            Log.i(TAG, "unlockFocus");
        }
    
        public void restFoucs() {
            foucsing = 1;
        }
    
    
        private CameraFocusListener mCameraFocusListener;
    
        public interface CameraFocusListener {
            void onFocus();
        }
    
        public void setCameraFocusListener(CameraFocusListener mCameraFocusListener) {
            this.mCameraFocusListener = mCameraFocusListener;
        }
    }
    
    

    结论:完美实现,只要带传感器的手机就能兼容。

    3.2 图片不规则裁剪

    要实现的效果如下:

     

    图片来源见文末参考资料

    由效果图可得出如下结论:

    1. 需要利用drawline将四个坐标点连接起来
    2. 需要处理触摸与拖拽事件,即随着手指的移动,坐标点跟随移动。
    3. 需要处理裁剪框区域内的全透明化和区域外的半透明化的效果。
    4. 其他等等...

    也就是说需要很强的自定义View的能力和计算能力,所以想着还是找找现成的轮子吧。
    经过各种Google,发现国内很难找到这种轮子,最终还是在Github上找了一个国外的,主要代码如下:

    package com.wildma.idcardcamera.cropper;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.Point;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.Rect;
    import android.graphics.Region;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class CropOverlayView extends View {
    
        private int defaultMargin = 100;
        private int minDistance   = 100;
        private int vertexSize    = 30;
        private int gridSize      = 3;
    
        private Bitmap bitmap;
        private Point  topLeft, topRight, bottomLeft, bottomRight;
    
        private float touchDownX, touchDownY;
        private CropPosition cropPosition;
    
        private int currentWidth  = 0;
        private int currentHeight = 0;
    
        private int minX, maxX, minY, maxY;
    
        public CropOverlayView(Context context) {
            super(context);
        }
    
        public CropOverlayView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public void setBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
            resetPoints();
            invalidate();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            if (getWidth() != currentWidth || getHeight() != currentHeight) {
                currentWidth = getWidth();
                currentHeight = getHeight();
                resetPoints();
            }
            Log.e("stk", "canvasSize=" + getWidth() + "x" + getHeight());
    
            drawBackground(canvas);
            drawVertex(canvas);
            drawEdge(canvas);
            //        drawGrid(canvas);//裁剪框内部线条
        }
    
        private void resetPoints() {
    
            Log.e("stk", "resetPoints, bitmap=" + bitmap);
    
            // 1. calculate bitmap size in new canvas
            float scaleX = bitmap.getWidth() * 1.0f / getWidth();
            float scaleY = bitmap.getHeight() * 1.0f / getHeight();
            float maxScale = Math.max(scaleX, scaleY);
    
            // 2. determine minX , maxX if maxScale = scaleY | minY, maxY if maxScale = scaleX
            int minX = 0;
            int maxX = getWidth();
            int minY = 0;
            int maxY = getHeight();
    
            if (maxScale == scaleY) { // image very tall
                int bitmapInCanvasWidth = (int) (bitmap.getWidth() / maxScale);
                minX = (getWidth() - bitmapInCanvasWidth) / 2;
                maxX = getWidth() - minX;
            } else { // image very wide
                int bitmapInCanvasHeight = (int) (bitmap.getHeight() / maxScale);
                minY = (getHeight() - bitmapInCanvasHeight) / 2;
                maxY = getHeight() - minY;
            }
    
            this.minX = minX;
            this.minY = minY;
            this.maxX = maxX;
            this.maxY = maxY;
    
            if (maxX - minX < defaultMargin || maxY - minY < defaultMargin)
                defaultMargin = 0; // remove min
            else
                defaultMargin = 100;
    
            Log.e("stk", "maxX - minX=" + (maxX - minX));
            Log.e("stk", "maxY - minY=" + (maxY - minY));
    
            topLeft = new Point(minX + defaultMargin, minY + defaultMargin);
            topRight = new Point(maxX - defaultMargin, minY + defaultMargin);
            bottomLeft = new Point(minX + defaultMargin, maxY - defaultMargin);
            bottomRight = new Point(maxX - defaultMargin, maxY - defaultMargin);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        private void drawBackground(Canvas canvas) {
            Paint paint = new Paint();
            paint.setColor(Color.parseColor("#66000000"));
            paint.setStyle(Paint.Style.FILL);
    
            Path path = new Path();
            path.moveTo(topLeft.x, topLeft.y);
            path.lineTo(topRight.x, topRight.y);
            path.lineTo(bottomRight.x, bottomRight.y);
            path.lineTo(bottomLeft.x, bottomLeft.y);
            path.close();
    
            canvas.save();
            canvas.clipPath(path, Region.Op.DIFFERENCE);
            canvas.drawColor(Color.parseColor("#66000000"));
            canvas.restore();
        }
    
        private void drawVertex(Canvas canvas) {
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setStyle(Paint.Style.FILL);
    
            canvas.drawCircle(topLeft.x, topLeft.y, vertexSize, paint);
            canvas.drawCircle(topRight.x, topRight.y, vertexSize, paint);
            canvas.drawCircle(bottomLeft.x, bottomLeft.y, vertexSize, paint);
            canvas.drawCircle(bottomRight.x, bottomRight.y, vertexSize, paint);
    
            Log.e("stk",
                    "vertextPoints=" +
                            topLeft.toString() + " " + topRight.toString() + " " + bottomRight.toString() + " " + bottomLeft.toString());
    
        }
    
        private void drawEdge(Canvas canvas) {
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setStrokeWidth(3);
            paint.setAntiAlias(true);
    
            canvas.drawLine(topLeft.x, topLeft.y, topRight.x, topRight.y, paint);
            canvas.drawLine(topLeft.x, topLeft.y, bottomLeft.x, bottomLeft.y, paint);
            canvas.drawLine(bottomRight.x, bottomRight.y, topRight.x, topRight.y, paint);
            canvas.drawLine(bottomRight.x, bottomRight.y, bottomLeft.x, bottomLeft.y, paint);
        }
    
        private void drawGrid(Canvas canvas) {
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setStrokeWidth(2);
            paint.setAntiAlias(true);
    
            for (int i = 1; i <= gridSize; i++) {
                int topDistanceX = Math.abs(topLeft.x - topRight.x) / (gridSize + 1) * i;
                int topDistanceY = Math.abs((topLeft.y - topRight.y) / (gridSize + 1) * i);
    
                Point top = new Point(
                        topLeft.x < topRight.x ? topLeft.x + topDistanceX : topLeft.x - topDistanceX,
                        topLeft.y < topRight.y ? topLeft.y + topDistanceY : topLeft.y - topDistanceY);
    
                int bottomDistanceX = Math.abs((bottomLeft.x - bottomRight.x) / (gridSize + 1) * i);
                int bottomDistanceY = Math.abs((bottomLeft.y - bottomRight.y) / (gridSize + 1) * i);
                Point bottom = new Point(
                        bottomLeft.x < bottomRight.x ? bottomLeft.x + bottomDistanceX : bottomLeft.x - bottomDistanceX,
                        bottomLeft.y < bottomRight.y ? bottomLeft.y + bottomDistanceY : bottomLeft.y - bottomDistanceY);
    
                canvas.drawLine(top.x, top.y, bottom.x, bottom.y, paint);
    
                int leftDistanceX = Math.abs((topLeft.x - bottomLeft.x) / (gridSize + 1) * i);
                int leftDistanceY = Math.abs((topLeft.y - bottomLeft.y) / (gridSize + 1) * i);
    
                Point left = new Point(
                        topLeft.x < bottomLeft.x ? topLeft.x + leftDistanceX : topLeft.x - leftDistanceX,
                        topLeft.y < bottomLeft.y ? topLeft.y + leftDistanceY : topLeft.y - leftDistanceY);
    
                int rightDistanceX = Math.abs((topRight.x - bottomRight.x) / (gridSize + 1) * i);
                int rightDistanceY = Math.abs((topRight.y - bottomRight.y) / (gridSize + 1) * i);
    
                Point right = new Point(
                        topRight.x < bottomRight.x ? topRight.x + rightDistanceX : topRight.x - rightDistanceX,
                        topRight.y < bottomRight.y ? topRight.y + rightDistanceY : topRight.y - rightDistanceY);
    
                canvas.drawLine(left.x, left.y, right.x, right.y, paint);
            }
    
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
                case MotionEvent.ACTION_DOWN:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    onActionDown(event);
                    return true;
                case MotionEvent.ACTION_MOVE:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    onActionMove(event);
                    return true;
            }
            return false;
        }
    
        private void onActionDown(MotionEvent event) {
            touchDownX = event.getX();
            touchDownY = event.getY();
            Point touchPoint = new Point((int) event.getX(), (int) event.getY());
            int minDistance = distance(touchPoint, topLeft);
            cropPosition = CropPosition.TOP_LEFT;
            if (minDistance > distance(touchPoint, topRight)) {
                minDistance = distance(touchPoint, topRight);
                cropPosition = CropPosition.TOP_RIGHT;
            }
            if (minDistance > distance(touchPoint, bottomLeft)) {
                minDistance = distance(touchPoint, bottomLeft);
                cropPosition = CropPosition.BOTTOM_LEFT;
            }
            if (minDistance > distance(touchPoint, bottomRight)) {
                minDistance = distance(touchPoint, bottomRight);
                cropPosition = CropPosition.BOTTOM_RIGHT;
            }
        }
    
        private int distance(Point src, Point dst) {
            return (int) Math.sqrt(Math.pow(src.x - dst.x, 2) + Math.pow(src.y - dst.y, 2));
        }
    
        private void onActionMove(MotionEvent event) {
            int deltaX = (int) (event.getX() - touchDownX);
            int deltaY = (int) (event.getY() - touchDownY);
    
            switch (cropPosition) {
                case TOP_LEFT:
                    adjustTopLeft(deltaX, deltaY);
                    invalidate();
                    break;
                case TOP_RIGHT:
                    adjustTopRight(deltaX, deltaY);
                    invalidate();
                    break;
                case BOTTOM_LEFT:
                    adjustBottomLeft(deltaX, deltaY);
                    invalidate();
                    break;
                case BOTTOM_RIGHT:
                    adjustBottomRight(deltaX, deltaY);
                    invalidate();
                    break;
            }
            touchDownX = event.getX();
            touchDownY = event.getY();
        }
    
        private void adjustTopLeft(int deltaX, int deltaY) {
            int newX = topLeft.x + deltaX;
            if (newX < minX)
                newX = minX;
            if (newX > maxX)
                newX = maxX;
    
            int newY = topLeft.y + deltaY;
            if (newY < minY)
                newY = minY;
            if (newY > maxY)
                newY = maxY;
    
            topLeft.set(newX, newY);
        }
    
        private void adjustTopRight(int deltaX, int deltaY) {
            int newX = topRight.x + deltaX;
            if (newX > maxX)
                newX = maxX;
            if (newX < minX)
                newX = minX;
    
            int newY = topRight.y + deltaY;
            if (newY < minY)
                newY = minY;
            if (newY > maxY)
                newY = maxY;
    
            topRight.set(newX, newY);
        }
    
        private void adjustBottomLeft(int deltaX, int deltaY) {
            int newX = bottomLeft.x + deltaX;
            if (newX < minX)
                newX = minX;
            if (newX > maxX)
                newX = maxX;
    
            int newY = bottomLeft.y + deltaY;
            if (newY > maxY)
                newY = maxY;
            if (newY < minY)
                newY = minY;
    
            bottomLeft.set(newX, newY);
        }
    
        private void adjustBottomRight(int deltaX, int deltaY) {
            int newX = bottomRight.x + deltaX;
            if (newX > maxX)
                newX = maxX;
            if (newX < minX)
                newX = minX;
    
            int newY = bottomRight.y + deltaY;
            if (newY > maxY)
                newY = maxY;
            if (newY < minY)
                newY = minY;
    
            bottomRight.set(newX, newY);
        }
    
        public void crop(CropListener cropListener, boolean needStretch) {
            if (topLeft == null)
                return;
    
            // calculate bitmap size in new canvas
            float scaleX = bitmap.getWidth() * 1.0f / getWidth();
            float scaleY = bitmap.getHeight() * 1.0f / getHeight();
            float maxScale = Math.max(scaleX, scaleY);
    
            // re-calculate coordinate in original bitmap
            Log.e("stk", "maxScale=" + maxScale);
    
            Point bitmapTopLeft = new Point((int) ((topLeft.x - minX) * maxScale), (int) ((topLeft.y - minY) * maxScale));
            Point bitmapTopRight = new Point((int) ((topRight.x - minX) * maxScale), (int) ((topRight.y - minY) * maxScale));
            Point bitmapBottomLeft = new Point((int) ((bottomLeft.x - minX) * maxScale), (int) ((bottomLeft.y - minY) * maxScale));
            Point bitmapBottomRight = new Point((int) ((bottomRight.x - minX) * maxScale), (int) ((bottomRight.y - minY) * maxScale));
    
            Log.e("stk", "bitmapPoints="
                    + bitmapTopLeft.toString() + " "
                    + bitmapTopRight.toString() + " "
                    + bitmapBottomRight.toString() + " "
                    + bitmapBottomLeft.toString() + " ");
    
            Bitmap output = Bitmap.createBitmap(bitmap.getWidth() + 1, bitmap.getHeight() + 1, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(output);
    
            Paint paint = new Paint();
            // 1. draw path
            Path path = new Path();
            path.moveTo(bitmapTopLeft.x, bitmapTopLeft.y);
            path.lineTo(bitmapTopRight.x, bitmapTopRight.y);
            path.lineTo(bitmapBottomRight.x, bitmapBottomRight.y);
            path.lineTo(bitmapBottomLeft.x, bitmapBottomLeft.y);
            path.close();
            canvas.drawPath(path, paint);
    
            // 2. draw original bitmap
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(bitmap, 0, 0, paint);
    
            // 3. cut
            Rect cropRect = new Rect(
                    Math.min(bitmapTopLeft.x, bitmapBottomLeft.x),
                    Math.min(bitmapTopLeft.y, bitmapTopRight.y),
                    Math.max(bitmapBottomRight.x, bitmapTopRight.x),
                    Math.max(bitmapBottomRight.y, bitmapBottomLeft.y));
    
            Bitmap cut = Bitmap.createBitmap(
                    output,
                    cropRect.left,
                    cropRect.top,
                    cropRect.width(),
                    cropRect.height()
            );
    
            if (!needStretch) {
                cropListener.onFinish(cut);
            } else {
                // 4. re-calculate coordinate in cropRect
                Point cutTopLeft = new Point();
                Point cutTopRight = new Point();
                Point cutBottomLeft = new Point();
                Point cutBottomRight = new Point();
    
                cutTopLeft.x = bitmapTopLeft.x > bitmapBottomLeft.x ? bitmapTopLeft.x - bitmapBottomLeft.x : 0;
                cutTopLeft.y = bitmapTopLeft.y > bitmapTopRight.y ? bitmapTopLeft.y - bitmapTopRight.y : 0;
    
                cutTopRight.x = bitmapTopRight.x > bitmapBottomRight.x ? cropRect.width() : cropRect.width() - Math.abs(bitmapBottomRight.x - bitmapTopRight.x);
                cutTopRight.y = bitmapTopLeft.y > bitmapTopRight.y ? 0 : Math.abs(bitmapTopLeft.y - bitmapTopRight.y);
    
                cutBottomLeft.x = bitmapTopLeft.x > bitmapBottomLeft.x ? 0 : Math.abs(bitmapTopLeft.x - bitmapBottomLeft.x);
                cutBottomLeft.y = bitmapBottomLeft.y > bitmapBottomRight.y ? cropRect.height() : cropRect.height() - Math.abs(bitmapBottomRight.y - bitmapBottomLeft.y);
    
                cutBottomRight.x = bitmapTopRight.x > bitmapBottomRight.x ? cropRect.width() - Math.abs(bitmapBottomRight.x - bitmapTopRight.x) : cropRect.width();
                cutBottomRight.y = bitmapBottomLeft.y > bitmapBottomRight.y ? cropRect.height() - Math.abs(bitmapBottomRight.y - bitmapBottomLeft.y) : cropRect.height();
    
                Log.e("stk", cut.getWidth() + "x" + cut.getHeight());
    
                Log.e("stk", "cutPoints="
                        + cutTopLeft.toString() + " "
                        + cutTopRight.toString() + " "
                        + cutBottomRight.toString() + " "
                        + cutBottomLeft.toString() + " ");
    
                float width = cut.getWidth();
                float height = cut.getHeight();
    
                float[] src = new float[]{cutTopLeft.x, cutTopLeft.y, cutTopRight.x, cutTopRight.y, cutBottomRight.x, cutBottomRight.y, cutBottomLeft.x, cutBottomLeft.y};
                float[] dst = new float[]{0, 0, width, 0, width, height, 0, height};
    
                Matrix matrix = new Matrix();
                matrix.setPolyToPoly(src, 0, dst, 0, 4);
                Bitmap stretch = Bitmap.createBitmap(cut.getWidth(), cut.getHeight(), Bitmap.Config.ARGB_8888);
    
                Canvas stretchCanvas = new Canvas(stretch);
                //            stretchCanvas.drawBitmap(cut, matrix, null);
                stretchCanvas.concat(matrix);
                stretchCanvas.drawBitmapMesh(cut, WIDTH_BLOCK, HEIGHT_BLOCK, generateVertices(cut.getWidth(), cut.getHeight()), 0, null, 0, null);
    
                cropListener.onFinish(stretch);
            }
        }
    
        private int WIDTH_BLOCK  = 40;
        private int HEIGHT_BLOCK = 40;
    
        private float[] generateVertices(int widthBitmap, int heightBitmap) {
    
            float[] vertices = new float[(WIDTH_BLOCK + 1) * (HEIGHT_BLOCK + 1) * 2];
    
            float widthBlock = (float) widthBitmap / WIDTH_BLOCK;
            float heightBlock = (float) heightBitmap / HEIGHT_BLOCK;
    
            for (int i = 0; i <= HEIGHT_BLOCK; i++)
                for (int j = 0; j <= WIDTH_BLOCK; j++) {
                    vertices[i * ((HEIGHT_BLOCK + 1) * 2) + (j * 2)] = j * widthBlock;
                    vertices[i * ((HEIGHT_BLOCK + 1) * 2) + (j * 2) + 1] = i * heightBlock;
                }
            return vertices;
        }
    }
    

    github地址:WildmaIDCardCamera

    参考资料:



    作者:wildma
    链接:https://www.jianshu.com/p/5e3cb0c63cd5
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • 最近做了一个图片识别、以及搜索的小项目,其中有一个模块是拍照以及拍照后对图片进行剪切,开始用的系统的相机和裁剪,由于系统的相机和裁剪多出了一些不必要的步骤和启动慢等等带给用户的体验不好,故自己写了一个...

    最近做了一个图片识别、以及搜索的小项目,其中有一个模块是拍照以及拍照后对图片进行剪切,开始用的系统的相机和裁剪,由于系统的相机和裁剪多出了一些不必要的步骤和启动慢等等带给用户的体验不好,故自己写了一个,下面给大家简要介绍下:

    自定义相机:定义SurfaceView得到SurfaceHolder,然后添加回调

    <span style="white-space:pre">		</span>surface_camera = (SurfaceView) findViewById(R.id.camera_preview);
    		view_focus = findViewById(R.id.view_focus);
    		surface_holder = surface_camera.getHolder();
    		surface_holder.addCallback(this);
    在回调中将SurfaceView和camera关联在一起,然后进行初始化、连续对焦然后销毁,这里简单把代码粘一下

    @Override
    	public void surfaceCreated(SurfaceHolder holder) {
    		
    		if(camera != null){
    			try {
    				camera.setPreviewDisplay(surface_holder);
    			} catch (IOException e) {
    				System.out.println(e);
    			}
    		}
    
    		mode = MODE.FOCUSING;
    		updateCameraParameters();
    		camera.startPreview();
    	}
    
    	@Override
    	public void surfaceChanged(SurfaceHolder holder, int format, int width,
    			int height) {
    		
    		camera.autoFocus(new AutoFocusCallback() {
    
    			@Override
    			public void onAutoFocus(boolean arg0, Camera arg1) {
    				// TODO Auto-generated method stub
    				if (arg0) {
    					updateCameraParameters();// 实现相机的参数初始化
    				}
    			}
    		});
    	}
    
    	@Override
    	public void surfaceDestroyed(SurfaceHolder holder) {
    		if (camera != null && holder != null) {
    			camera.stopPreview();
    			camera.release();
    			camera = null;
    		}
    	}
    在实现连续对焦的方式我是采用的传感器实现的,通过手机位置或者说角度发生微弱变化然后调用对焦功能,这样子和实际的系统自带的相机的体验差不多

    	@Override
    		public void onSensorChanged(SensorEvent arg0) {
    			// TODO Auto-generated method stub
    			
    			float x = arg0.values[0];
    		    float y = arg0.values[1];
    		    float z = arg0.values[2];
    		    
    		    if (!mInitialized){
    		        mLastX = x;
    		        mLastY = y;
    		        mLastZ = z;
    		        mInitialized = true;
    		    }
    		    
    		    float deltaX  = Math.abs(mLastX - x);
    		    float deltaY = Math.abs(mLastY - y);
    		    float deltaZ = Math.abs(mLastZ - z);
    		    
    		    if(deltaX > .3 || deltaY > .3 || deltaZ > .3){
    		    	
    		    	if(CustomCameraView.camera != null){
    		    		cameraView.setFocus();
    		    	}
    		    }
    		 
    		    mLastX = x;
    		    mLastY = y;
    		    mLastZ = z;
    		}
    变焦的话,就不多说了就是简单的silder实现的,“点击对焦”简单调用自动对焦实现的,这里也存在了一个问题就是打开相机之后,如果进行了点击对焦之后就不能进行连续对焦了,这里我只用到了连续对焦没有使用点击对焦,所以就没有深入探究原因。

    拍照后保存

    <span style="white-space:pre">	</span>/**
    	 * 拍照
    	 */
    	public void takePicture() {
    		
    		if (camera != null) {
    			camera.takePicture(null, null, new PictureCallback() {
    
    				@Override
    				public void onPictureTaken(byte[] data, Camera camera) {
    					File folder = new File(Environment
    							.getExternalStorageDirectory().getAbsolutePath()
    							+ CUSTOMECAMERA_PHOTO_PATH);
    					if (folder.exists() == false) {
    						folder.mkdirs();
    					}
    					File pictureFile = new File(folder.getAbsoluteFile(),
    							"temp.jpg");
    					
    					try {
    						FileOutputStream fos = new FileOutputStream(pictureFile);
    						fos.write(data);
    						fos.close();
    						
    						if (onTakePictureInfo != null) {
    							onTakePictureInfo.onTakePictureInofo(true);
    						}
    
    					} catch (Exception e) {
    						if (onTakePictureInfo != null) {
    							mode = MODE.NONE;
    							onTakePictureInfo.onTakePictureInofo(false);
    						}
    					}
    				}
    			});
    			
    			mode = MODE.NONE;
    		}
    	}

    关于剪切拿到了的源码,在上面进行了简要的修改,我这里需要的是横屏,最后的结果:

    拍照界面


    裁剪界面


    下载地址:http://download.csdn.net/detail/u012350993/8285313,里面一些东西我没有去除,文件有点略大。

    展开全文
  • 知识点: 在官方7.0的以上的系统中,尝试传递 file://URI可能会触发...自动创建Android/data/你的包名/files文件 this.getExternalFilesDir(null).getAbsolutePath() file 转化 content://uri Strin...

    知识点:

    1. 在官方7.0的以上的系统中,尝试传递 file://URI可能会触发FileUriExposedException
    2. google官方FileProvider
    3. 调用系统裁剪的过程中涉及到两个 Uri 对象:inputUri 和 outputUri
    4. 零时赋权
      (1)Context.grantUriPermission(package, Uri, mode_flags)Context.grantUriPermission(package, Uri, mode_flags)
      (2)Intent.setFlags()
    5. 自动创建Android/data/你的包名/files文件
       this.getExternalFilesDir(null).getAbsolutePath()
    
    1. file 转化 content://uri
      String mTempPhotoPath = this.getExternalFilesDir(null).getAbsolutePath() + File.separator + "photo.jpeg";
      File file=new File(mTempPhotoPath);
      Uri uri=FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationContext().getPackageName() + ".fileprovider", file);
      //(重点)第三个参数为要共享的文件,并且这个文件一定位于path 文件中添加的子目录里面。
    

    文件的绝对路径与第二步指定的文件目录保持一致:(假设包名为com.ysx.fileproviderserver)。如上边的代码的文件的绝对路径为/data/data/com.ysx.fileproviderserver/files/text/hello.txt,对应paths中的内容为:。getUriForFile()的第二个参数是authority,与manifest文件中声明的authorities保持一致。这时候,我们得到的URI的串为:content://com.ysx.fileproviderserver.fileprovider/my_files/hello.txt。

    1. 在paths节点内部支持以下几个子节点,分别为:
    	<root-path/> 代表设备的根目录new File("/");
    	<files-path/> 代表context.getFilesDir()
    	<cache-path/> 代表context.getCacheDir()
    	<external-path/> 代表Environment.getExternalStorageDirectory()
    	<external-files-path>代表context.getExternalFilesDirs()
    	<external-cache-path>代表getExternalCacheDirs()
    
    1. FileProvider基本使用流程
      (1)定义一个 FileProvider
      (2)指定共享目录
      (3)为文件生成有效的 Content URI
      (4)申请临时的读写权限
      (5)发送 Content URI 至其他的 App

    主要代码

    package com.example.administrator.popupwindow;
    
    import android.Manifest;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.databinding.DataBinderMapper;
    import android.databinding.DataBindingUtil;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v4.content.FileProvider;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.KeyEvent;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    
    
    import de.hdodenhof.circleimageview.CircleImageView;
    
    import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
    import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
    
    
    public class MainActivity extends BaseActivity implements View.OnClickListener {
    
        private static final String TAG = "MainActivity";
        private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
        Uri imageUri_crop = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap
        private CircleImageView circleImageView;
        private String mTempPhotoPath;
        private String mTempPhotoPath2;
        private Uri imageUri;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            circleImageView = (CircleImageView) findViewById(R.id.profile_image);
            circleImageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    showTypeDialog();
                }
            });
        }
    
        @Override
        public void onClick(View view) {
        }
    
        private void showTypeDialog() {
            //显示对话框
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.mipmap.ic_launcher);
            builder.setTitle("选择头像");
            final String[] items = {"相册", "相机"};
            builder.setCancelable(true);
            builder.setItems(items, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    Toast.makeText(getApplicationContext(), "You clicked " + items[i], Toast.LENGTH_SHORT).show();
                    if (i == 0) {
                        Intent intent1 = new Intent(Intent.ACTION_PICK, null);
                        //打开文件
                        intent1.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                        startActivityForResult(intent1, 1);
                        dialogInterface.dismiss();
                    }
                    if (i == 1) {
                        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {   //权限还没有授予,需要在这里写申请权限的代码
                            // 第二个参数是一个字符串数组,里面是你需要申请的权限 可以设置申请多个权限
                            // 最后一个参数是标志你这次申请的权限,该常量在onRequestPermissionsResult中使用到
                            Log.e(TAG, "onClick: " + "未授权");
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                    2);
                        } else { //权限已经被授予,在这里直接写要执行的相应方法即可
                            takePhoto();
                        }
                        dialogInterface.dismiss();
                    }
                }
            });
            final AlertDialog dialog = builder.create();
            dialog.show();
        }
    
        private void takePhoto() {
            // 跳转到系统的拍照界面
            Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            // 指定照片存储位置为sd卡本目录下
            // 这里设置为固定名字 这样就只会只有一张temp图 如果要所有中间图片都保存可以通过时间或者加其他东西设置图片的名称
            // File.separator为系统自带的分隔符 是一个固定的常量
            mTempPhotoPath = this.getExternalFilesDir(null).getAbsolutePath() + File.separator + "photo1.jpeg";//android/data 自动创建目录
            mTempPhotoPath2 = Environment.getExternalStorageDirectory() + File.separator + "photo2.jpeg";
            Log.e(TAG, "takePhoto: " + mTempPhotoPath);
            Log.e(TAG, "takePhoto2: " + mTempPhotoPath2);
            // 获取图片所在位置的Uri路径
            imageUri = FileProvider.getUriForFile(MainActivity.this, MainActivity.this.getApplicationContext().getPackageName() + ".fileprovider", new File(mTempPhotoPath2));
            Log.e(TAG, "takePhoto3: " + imageUri);
            //下面这句指定调用相机拍照后的照片存储的路径,如果没有这句代码,则不储存照片
            intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    
    //        intentToTakePhoto.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
    //        intentToTakePhoto.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
            startActivityForResult(intentToTakePhoto, 2);
        }
    
        public void cropPhoto(Uri uri) {
            Intent intent = new Intent("com.android.camera.action.CROP");
            intent.setDataAndType(uri, "image/*");
            intent.putExtra("crop", "true");
            // aspectX aspectY 是宽高的比例
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);
            // outputX outputY 是裁剪图片宽高
            intent.putExtra("outputX", 250);
            intent.putExtra("outputY", 250);
            intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri_crop);
            intent.putExtra("return-data", false);
            startActivityForResult(intent, 3);
        }
    
        private Bitmap decodeUriAsBitmap(Uri uri) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
            return bitmap;
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode) {
                case 1:
                    if (resultCode == RESULT_OK) {
                        Uri uri = data.getData();
                        cropPhoto(uri);
                    }
                    break;
                case 2:
                    if (resultCode == RESULT_OK) {
                        if (imageUri != null) {
                            Log.e(TAG, "onActivityResult: " + imageUri.toString());
                            cropPhoto(imageUri);
                        }
                    }
                    break;
                case 3:
                    if (imageUri_crop != null) {
                        Bitmap bitmap = decodeUriAsBitmap(imageUri_crop);//decode bitmap
                        circleImageView.setImageBitmap(bitmap);
                    }
                    break;
            }
        }
    }
    
    
    
    

    完整Demo下载,AS直接运行

    展开全文
  • 原理文章:... 需求背景:人脸比对需要比对正面照和持证照,正面照如果是竖的话,会比对不上,因此拍照保存的照片要正向保存 功能:带拍摄区域边框、半透明遮罩层、支持点击屏幕对焦、自动裁剪出所需大小
  • 此功能: 1.上传图片自动生成缩略图, 2.显示上传进度条 3.可在线拍照
  • 支持系统拍照,及自定义Intent拍照 支持相册选取,及自定义Intent获取 支持图片压缩,默认采用鲁班压缩 支持图片裁剪,系统裁剪及自带裁剪 自动适配camera及sd权限 适配AndroidQ 链式调用 如何使用 UTakePhoto.with...
  • IV、自定义相机: 加一个长方形的框框并裁剪身份证照片 需求: 拍身份证的时候加一个长方形的框框 功能目的:人脸比对,需要比对正面照和持证照,正面照如果是竖的话,会比对不上,因此拍照保存的照片要正向保存。 ...
  • 要求上传正方形,在裁剪的时候发现imageOrientation自动改变了. 但是从相册选不会,拍照会. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)...
  • 今天开发一个拍照获取照片的功能的时候, 发现上传之后图片会自动旋转90. 测试发现, 只要是图片大于2M, 系统就会自动翻转照片  相机拍照后直接取出来的UIimage(用UIImagePickerControllerOriginalImage取出),它...
  • 支持图片自动裁剪 支持图片手动不规则裁剪 使用 步骤1.添加JitPack仓库 在项目的build.gradle添加JitPack仓库 allprojects { repositories { ... maven { url "https://jitpack.io" } } } 步骤2.添加依赖 在需要...
  • 功能介绍 支持系统拍照,及自定义Intent拍照 支持相册选取,及自定义Intent获取 支持图片压缩,默认采用鲁班压缩...此处只说明选择相册照片裁剪,若要拍照裁剪,请看使用文档说明 UTakePhoto.with(MainActivity.this)
  • 仿QQ头像裁剪功能

    2015-06-02 16:40:51
    调用系统自带相机或者系统图库进行拍照图片的选择(完美解决大图内存溢出问题,亲测小米华为拍出的十几M图片均可使用,完美解决三星等部分手机拍照后图片自动旋转问题),裁剪后可自定义压缩保存质量,可自定义裁剪...
  • Android自定义相机实现身份证拍照,并加入自动对焦与图片不规则裁剪
  • 最近做了一个图片识别、以及搜索的小项目,其中有一个模块是拍照以及拍照后对图片进行剪切,开始用的系统的相机和裁剪,由于系统的相机和裁剪多出了一些不必要的步骤和启动慢等等带给用户的体验不好,故自己写了一个...
  • TakePhoto是一款用于在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库,目前最新版本3.0.2。 支持通过相机拍照获取图片 支持从相册选择图片 支持从文件选择图片 支持批量...
  • IOS 相机 或者 相册 获取图片裁剪

    千次阅读 2018-03-28 19:51:59
    功能比较简单,就是 点击按钮 拍照或者相册 选择照片 然后弹出系统 自动裁剪框 裁切图片 储存本地代码下载地址
  • 一款用于在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库 效果展示: 图片拍照 图片裁剪 TakePhoto 包括以下功能: 支持通过相机拍照获取图片 支持从相册选择图片 支持从...
  • A5下载站向大家推荐一款专业的图片编辑软件——图片批量水印裁剪器...新增文档资料扫描拍照件批量智能自动整理功能; 4.新增批量叠加同一背景的同时打印文字和图片水印功能; 5.重制安装包。 图片批量水印裁剪器截图
  • jQueryphotoClip-图片上传并裁剪

    千次阅读 2019-02-25 14:28:23
    公司年前要推一个制作新年贺卡的H5活动页,开发过程中踩了几个坑,今天总结分享...出现的问题:ios多个型号的手机出现拍照或者相册上传的图片会自动旋转90度,用户不可自己选择裁剪区域。 最终方案:使用jQueryph...
  • 支持通过相机拍照获取图片 支持从相册选择图片 支持从文件选择图片 支持批量图片选取 支持图片压缩以及批量图片压缩 ...支持因拍照Activity被回收后的自动恢复 支持Android8.1 +支持多种压缩工具 +支持多种图片选择工具
  • 最近在项目中遇到一个很奇怪的问题,在拍照后获取图片,有时候会莫名其妙的自动旋转90度,导致项目中的裁剪图片总是出问题,后来在度娘上查了一下,才明白是什么鬼!原来是API在作怪,他会在把图片大于2M的图片给你...
  • 3.2.1 新增裁剪用scaleAspectFillCrop属性,设置为YES后,照片尺寸小于裁剪框时会自动放大撑满 3.2.0 加入用NSOperationQueue控制获取原图并发数降低内存的示例 3.1.8 批量获取图片时加入队列控制,尝试优化大批量...
  • 更新裁剪框最小属性, 限制最小区域,可传1以上的数字和字符串,限制长宽都是这么大 也可以传数组[90,90] limitMinSize: [Number, Array, String] 0.50 支持图片清空 修复 ie11 ie10 不能使用问题 修复 URL....

空空如也

空空如也

1 2 3 4
收藏数 73
精华内容 29
关键字:

拍照自动裁剪