소스 검색

2.修改Android自定义相机图像方向,增加图像预览和裁剪。

Pen Li 7 년 전
부모
커밋
8b792014ed
21개의 변경된 파일1166개의 추가작업 그리고 29개의 파일을 삭제
  1. 7 3
      ApexDrivers/racameralib/src/main/AndroidManifest.xml
  2. 61 5
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/CameraActivity.java
  3. 10 21
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/CameraSurfaceView.java
  4. 143 0
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/PicturePreviewActivity.java
  5. 113 0
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/PictureUtils.java
  6. 137 0
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/CropImageActivity.java
  7. 425 0
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/CropImageView.java
  8. 123 0
      ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/FloatDrawable.java
  9. BIN
      ApexDrivers/racameralib/src/main/res/drawable-hdpi/btn_photo_assign.png
  10. BIN
      ApexDrivers/racameralib/src/main/res/drawable-hdpi/btn_photo_retake.png
  11. BIN
      ApexDrivers/racameralib/src/main/res/drawable-ldpi/btn_photo_assign.png
  12. BIN
      ApexDrivers/racameralib/src/main/res/drawable-ldpi/btn_photo_retake.png
  13. BIN
      ApexDrivers/racameralib/src/main/res/drawable-mdpi/btn_photo_assign.png
  14. BIN
      ApexDrivers/racameralib/src/main/res/drawable-mdpi/btn_photo_retake.png
  15. BIN
      ApexDrivers/racameralib/src/main/res/drawable-xhdpi/btn_photo_assign.png
  16. BIN
      ApexDrivers/racameralib/src/main/res/drawable-xhdpi/btn_photo_retake.png
  17. BIN
      ApexDrivers/racameralib/src/main/res/drawable-xxhdpi/btn_photo_assign.png
  18. BIN
      ApexDrivers/racameralib/src/main/res/drawable-xxhdpi/btn_photo_retake.png
  19. 68 0
      ApexDrivers/racameralib/src/main/res/layout/activity_crop_image.xml
  20. 68 0
      ApexDrivers/racameralib/src/main/res/layout/picture_preview_activity.xml
  21. 11 0
      ApexDrivers/racameralib/src/main/res/menu/preview_picture_menu.xml

+ 7 - 3
ApexDrivers/racameralib/src/main/AndroidManifest.xml

@@ -12,12 +12,16 @@
 
     <application>
 
+        <activity android:name=".crop.CropImageActivity">
+        </activity>
+
+        <activity android:name=".camera.PicturePreviewActivity">
+        </activity>
+
         <activity
             android:name=".camera.CameraActivity"
             android:screenOrientation="portrait"
-            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-            >
-
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
         </activity>
 
     </application>

+ 61 - 5
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/CameraActivity.java

@@ -9,6 +9,7 @@ import android.view.View;
 import android.widget.ImageButton;
 import android.widget.TextView;
 
+import java.io.File;
 import java.util.Date;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -22,6 +23,8 @@ public class CameraActivity extends Activity {
     private static final String CAMERA_PICTURE_DIR_KEY = "CAMERA_PICTURE_DIR_KEY";
     private static final String CAMERA_VIDEO_DIR_KEY = "CAMERA_VIDEO_DIR_KEY";
 
+    private static final int REQUEST_PICTURE_PREVIEW_CODE = 1011;
+
     private static final String SDCARD = "/sdcard";
 
     public static Intent buildCameraActivity(Activity activity, String pictureDir, String videoDir) {
@@ -106,6 +109,15 @@ public class CameraActivity extends Activity {
         setup(savedInstanceState);
     }
 
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == REQUEST_PICTURE_PREVIEW_CODE) {
+            onPicturePreviewResult(resultCode, data);
+        }
+    }
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -151,7 +163,7 @@ public class CameraActivity extends Activity {
         surfaceView.startPreview();
     }
 
-    // end region
+    // endregion
 
     // region Setup
 
@@ -303,10 +315,11 @@ public class CameraActivity extends Activity {
 
     private void onPictureTaken(String path) {
 
-        Intent intent = new Intent();
-        intent.putExtra("picturePath", path);
-        setResult(RESULT_OK, intent);
-        finish();
+        Intent intent = PicturePreviewActivity.buildPicturePreviewActivityIntent(this, path);
+        if (intent != null) {
+
+            startActivityForResult(intent, REQUEST_PICTURE_PREVIEW_CODE);
+        }
     }
 
     private void onVideoRecorded(String path) {
@@ -314,4 +327,47 @@ public class CameraActivity extends Activity {
     }
 
     // endregion
+
+    // region Activity Result
+
+    private void onPicturePreviewResult(int resultCode, Intent data) {
+
+        String picturePath = PicturePreviewActivity.getPicturePath(data);
+        if (resultCode == RESULT_OK) {
+
+            if (picturePath != null) {
+                takenPicture(picturePath);
+            }
+
+        } else {
+
+            Log.d(TAG, "onPicturePreviewResult: Cancel");
+            if (picturePath != null) {
+                File picF = new File(picturePath);
+                if (picF.exists()) {
+                    picF.delete();
+                }
+            }
+        }
+    }
+
+    private static final String PICTURE_PATH_KEY = "PICTURE_PATH_KEY";
+
+    public static String getPicturePath(Intent data) {
+        if (data != null) {
+            return data.getStringExtra(PICTURE_PATH_KEY);
+        }
+        return null;
+    }
+
+    private void takenPicture(String path) {
+
+        Intent intent = new Intent();
+        intent.putExtra(PICTURE_PATH_KEY, path);
+
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+
+    // endregion
 }

+ 10 - 21
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/CameraSurfaceView.java

@@ -1,10 +1,10 @@
 package redant.usai.com.racameralib.camera;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.ImageFormat;
 import android.hardware.Camera;
 import android.media.CamcorderProfile;
-import android.media.ExifInterface;
 import android.media.MediaRecorder;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -517,7 +517,7 @@ public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Call
     }
 
     @Override
-    public void onAutoFocus(boolean success, android.hardware.Camera camera) {
+    public void onAutoFocus(boolean success, Camera camera) {
         if (success) {
             Log.d("CameraSurfaceView", "onAutoFocus: success" + success);
         }
@@ -607,7 +607,14 @@ public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Call
                 fos.flush();
                 fos.close();
 
-                setPictureOrientation(picF.getAbsolutePath(), rotate);// 旋转图片,调整方向
+                // 设置图像方向
+                PictureUtils.setPictureOrientation(picF.getAbsolutePath(), rotate);
+
+                // 调正
+                Bitmap bitmap = PictureUtils.adjustPictureOrientation(picF.getAbsolutePath());
+
+                // 保存
+                PictureUtils.writePictureToFile(bitmap, picF.getAbsoluteFile());
 
                 if (callback != null) {
                     callback.captureImageCompletion(data, picF.getAbsolutePath());
@@ -641,24 +648,6 @@ public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Call
     }
 
 
-    private static void setPictureOrientation(String path, int orientation) {
-        try {
-            ExifInterface exifInterface = new ExifInterface(path);
-            // 修正图片的旋转角度,设置其不旋转。这里也可以设置其旋转的角度,可以传值过去,
-            // 例如旋转90度,传值ExifInterface.ORIENTATION_ROTATE_90,需要将这个值转换为String类型的
-            int rotate = ExifInterface.ORIENTATION_ROTATE_90;
-            if (orientation == 90) {
-                rotate = ExifInterface.ORIENTATION_ROTATE_90;
-            } else if (orientation == 270) {
-                rotate = ExifInterface.ORIENTATION_ROTATE_270;
-            }
-            exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(rotate));
-            exifInterface.saveAttributes();
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
     // endregion
 
 

+ 143 - 0
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/PicturePreviewActivity.java

@@ -0,0 +1,143 @@
+package redant.usai.com.racameralib.camera;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+import java.io.File;
+
+import redant.usai.com.racameralib.R;
+import redant.usai.com.racameralib.crop.CropImageActivity;
+
+public class PicturePreviewActivity extends AppCompatActivity {
+
+    private final static String PICTURE_PATH_KEY = "PICTURE_PATH_KEY";
+
+    private final static int REQUEST_EDIT_PICTURE_CODE = 2001;
+
+    public static Intent buildPicturePreviewActivityIntent(Activity activity, String picturePath) {
+
+        if (activity == null || picturePath == null) {
+            return null;
+        }
+
+        Intent intent = new Intent(activity, PicturePreviewActivity.class);
+        intent.putExtra(PICTURE_PATH_KEY, picturePath);
+        return intent;
+    }
+
+    public static String getPicturePath(Intent intent) {
+        if (intent != null) {
+            return intent.getStringExtra(PICTURE_PATH_KEY);
+        }
+        return null;
+    }
+
+    private String picturePath;
+    private ImageView picView;
+    private ImageButton retakeBtn, assignBtn;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.picture_preview_activity);
+
+        setupView();
+
+        init(savedInstanceState);
+
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+       getMenuInflater().inflate(R.menu.preview_picture_menu, menu);
+       return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.edit_item) {
+
+            File file = new File(picturePath);
+            CropImageActivity.Builder builder = new CropImageActivity.Builder(1024,1024, file, file);
+            Intent intent = builder.getIntent(this);
+
+            startActivityForResult(intent, REQUEST_EDIT_PICTURE_CODE);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putString(PICTURE_PATH_KEY, picturePath);
+    }
+
+    @Override
+    public void onBackPressed() {
+        retakeBtnClick();
+    }
+
+    private void init(Bundle savedInstanceState) {
+
+        if (savedInstanceState != null) {
+
+            picturePath = savedInstanceState.getString(PICTURE_PATH_KEY);
+        } else {
+
+            Intent intent = getIntent();
+            picturePath = intent.getStringExtra(PICTURE_PATH_KEY);
+        }
+
+        Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
+        picView.setImageBitmap(bitmap);
+    }
+
+    private void setupView() {
+
+        picView = findViewById(R.id.picture_preview_iv);
+        retakeBtn = findViewById(R.id.picture_retake_btn);
+        assignBtn = findViewById(R.id.picture_assign_btn);
+
+        retakeBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                retakeBtnClick();
+            }
+        });
+
+        assignBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                assignPictureClick();
+            }
+        });
+    }
+
+    private void retakeBtnClick() {
+
+        Intent intent = new Intent();
+        intent.putExtra(PICTURE_PATH_KEY, picturePath);
+
+        setResult(RESULT_CANCELED, intent);
+        finish();
+    }
+
+    private void assignPictureClick() {
+
+        Intent intent = new Intent();
+        intent.putExtra(PICTURE_PATH_KEY, picturePath);
+
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}

+ 113 - 0
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/camera/PictureUtils.java

@@ -0,0 +1,113 @@
+package redant.usai.com.racameralib.camera;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.media.ExifInterface;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class PictureUtils {
+
+    public static Bitmap adjustPictureOrientation(String filename) {
+        if (filename == null) {
+            return null;
+        }
+
+        int degree = readPictureDegree(filename);
+
+        Bitmap bitmap = BitmapFactory.decodeFile(filename);
+
+        if (degree != 0) {
+
+            return rotatingPicture(degree,bitmap);
+        }
+
+        return bitmap;
+    }
+
+    public static int readPictureDegree(String path) {
+        int degree  = 0;
+        try {
+            ExifInterface exifInterface = new ExifInterface(path);
+            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+            switch (orientation) {
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    degree = 90;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    degree = 180;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    degree = 270;
+                    break;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return degree;
+    }
+
+    public static Bitmap rotatingPicture(int angle , Bitmap bitmap) {
+
+        //旋转图片 动作
+        Matrix matrix = new Matrix();
+        matrix.postRotate(angle);
+
+        // 创建新的图片
+        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+        return resizedBitmap;
+    }
+
+    public static void setPictureOrientation(String path, int orientation) {
+        try {
+            ExifInterface exifInterface = new ExifInterface(path);
+            // 修正图片的旋转角度,设置其不旋转。这里也可以设置其旋转的角度,可以传值过去,
+            // 例如旋转90度,传值ExifInterface.ORIENTATION_ROTATE_90,需要将这个值转换为String类型的
+            int rotate = ExifInterface.ORIENTATION_ROTATE_90;
+            if (orientation == 90) {
+                rotate = ExifInterface.ORIENTATION_ROTATE_90;
+            } else if (orientation == 270) {
+                rotate = ExifInterface.ORIENTATION_ROTATE_270;
+            }
+            exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(rotate));
+            exifInterface.saveAttributes();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void writePictureToFile(Bitmap bitmap, File photoFile) {
+        if (photoFile == null || bitmap == null) {
+            return;
+        }
+
+        BufferedOutputStream bos = null;
+        try {
+            bos = new BufferedOutputStream(new FileOutputStream(photoFile));
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+            bos.flush();
+            bos.close();
+
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+
+        } finally {
+
+            if (bos != null) {
+                try {
+                    bos.flush();
+                    bos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+            }
+        }
+    }
+}

+ 137 - 0
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/CropImageActivity.java

@@ -0,0 +1,137 @@
+package redant.usai.com.racameralib.crop;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import redant.usai.com.racameralib.R;
+
+
+public class CropImageActivity extends Activity {
+
+
+    private static String OUTPUT_X = "outputX";
+    private static String OUTPUT_Y = "outputY";
+    private static String OUTPOT_FILE = "outputFile";
+    private static String DATA_FILE = "dataFile";
+
+    private CropImageView mView;
+    private Button cropBtn;
+    private Button cancelBtn;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_crop_image);
+
+        Intent intent = getIntent();
+        final int outputX = intent.getIntExtra(OUTPUT_X,500);
+        final int outputY = intent.getIntExtra(OUTPUT_Y,500);
+        final File src = (File) intent.getExtras().get(DATA_FILE);
+        final File des = (File) intent.getExtras().get(OUTPOT_FILE);
+        if (src == null) {
+            setResult(RESULT_CANCELED);
+            finish();
+        }
+
+        mView = (CropImageView) findViewById(R.id.cropimage);
+
+        if (src.exists()) {
+            Bitmap bitmap = BitmapFactory.decodeFile(src.getAbsolutePath());
+            //设置资源和默认长宽
+//            mView.setDrawable(new BitmapDrawable(bitmap), outputX, outputY);
+
+            mView.setDrawableFile(src, outputX, outputY);
+
+        } else {
+            setResult(RESULT_CANCELED);
+            finish();
+        }
+
+        cropBtn = (Button) findViewById(R.id.crop_btn);
+        cropBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                //调用该方法得到剪裁好的图片
+                Bitmap mBitmap = mView.getCropImage();
+                if (mBitmap != null) {
+                    savePhotoToFile(mBitmap,des);
+                }
+            }
+        });
+
+        cancelBtn = (Button) findViewById(R.id.cancel_btn);
+        cancelBtn.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setResult(RESULT_CANCELED);
+                finish();
+            }
+        });
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+    }
+
+    private void savePhotoToFile(Bitmap bitmap, File photoFile) {
+        if (photoFile == null || bitmap == null) {
+            return;
+        }
+        try {
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(photoFile));
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+            bos.flush();
+            bos.close();
+
+            setResult(RESULT_OK);
+
+        } catch (IOException e) {
+            e.printStackTrace();
+
+            setResult(RESULT_CANCELED);
+
+        } finally {
+            finish();
+        }
+    }
+
+    public static class Builder {
+        private int width;
+        private int height;
+        private File input;
+        private File output;
+
+        public Builder(final int outputX, final int outputY, final File src, final File des) {
+            super();
+            width = outputX;
+            height = outputY;
+            input = src;
+            output = des;
+        }
+
+        public Intent getIntent(final Context ctx) {
+            final Intent intent = new Intent(ctx, CropImageActivity.class);
+
+            intent.putExtra(OUTPUT_X,width);
+            intent.putExtra(OUTPUT_Y,height);
+            intent.putExtra(DATA_FILE,input);
+            intent.putExtra(OUTPOT_FILE,output);
+
+            return intent;
+        }
+
+    }
+}

+ 425 - 0
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/CropImageView.java

@@ -0,0 +1,425 @@
+package redant.usai.com.racameralib.crop;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+public class CropImageView extends View {
+    // 在touch重要用到的点,
+    private float mX_1 = 0;
+    private float mY_1 = 0;
+    // 触摸事件判断
+    private final int STATUS_SINGLE = 1;
+    private final int STATUS_MULTI_START = 2;
+    private final int STATUS_MULTI_TOUCHING = 3;
+    // 当前状态
+    private int mStatus = STATUS_SINGLE;
+    // 默认裁剪的宽高
+    private int cropWidth;
+    private int cropHeight;
+    // 浮层Drawable的四个点
+    private final int EDGE_LT = 1;
+    private final int EDGE_RT = 2;
+    private final int EDGE_LB = 3;
+    private final int EDGE_RB = 4;
+    private final int EDGE_MOVE_IN = 5;
+    private final int EDGE_MOVE_OUT = 6;
+    private final int EDGE_NONE = 7;
+
+    public int currentEdge = EDGE_NONE;
+
+    protected float oriRationWH = 0;
+    protected final float maxZoomOut = 5.0f;
+    protected final float minZoomIn = 0.333333f;
+
+    protected Drawable mDrawable;
+    protected FloatDrawable mFloatDrawable;
+
+    protected Rect mDrawableSrc = new Rect();// 图片Rect变换时的Rect
+    protected Rect mDrawableDst = new Rect();// 图片Rect
+    protected Rect mDrawableFloat = new Rect();// 浮层的Rect
+    protected boolean isFrist = true;
+    private boolean isTouchInSquare = true;
+
+    protected Context mContext;
+
+    public CropImageView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public CropImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public CropImageView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context);
+
+    }
+
+    private void init(Context context) {
+        this.mContext = context;
+        try {
+            if (android.os.Build.VERSION.SDK_INT >= 11) {
+                this.setLayerType(LAYER_TYPE_SOFTWARE, null);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        mFloatDrawable = new FloatDrawable(context);
+    }
+
+    public void setDrawable(Drawable mDrawable, int cropWidth, int cropHeight) {
+        this.mDrawable = mDrawable;
+        this.cropWidth = cropWidth;
+        this.cropHeight = cropHeight;
+        this.isFrist = true;
+        invalidate();
+    }
+
+    private File file;
+    public void setDrawableFile(File file,int cropWidth, int cropHeight) {
+        this.file = file;
+        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
+        //设置资源和默认长宽
+        setDrawable(new BitmapDrawable(bitmap), cropWidth, cropHeight);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+
+        if (event.getPointerCount() > 1) {
+            if (mStatus == STATUS_SINGLE) {
+                mStatus = STATUS_MULTI_START;
+            } else if (mStatus == STATUS_MULTI_START) {
+                mStatus = STATUS_MULTI_TOUCHING;
+            }
+        } else {
+            if (mStatus == STATUS_MULTI_START
+                    || mStatus == STATUS_MULTI_TOUCHING) {
+                mX_1 = event.getX();
+                mY_1 = event.getY();
+            }
+
+            mStatus = STATUS_SINGLE;
+        }
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mX_1 = event.getX();
+                mY_1 = event.getY();
+                currentEdge = getTouch((int) mX_1, (int) mY_1);
+                isTouchInSquare = mDrawableFloat.contains((int) event.getX(),
+                        (int) event.getY());
+
+                break;
+
+            case MotionEvent.ACTION_UP:
+                checkBounds();
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                currentEdge = EDGE_NONE;
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                if (mStatus == STATUS_MULTI_TOUCHING) {
+
+                } else if (mStatus == STATUS_SINGLE) {
+                    int dx = (int) (event.getX() - mX_1);
+                    int dy = (int) (event.getY() - mY_1);
+
+                    mX_1 = event.getX();
+                    mY_1 = event.getY();
+                    // 根據得到的那一个角,并且变换Rect
+                    if (!(dx == 0 && dy == 0)) {
+                        /**限制裁剪框只能在图片区域*/
+                        int x = mDrawableDst.left;
+                        int y = mDrawableDst.top;
+                        int w = mDrawableDst.right;
+                        int h = mDrawableDst.bottom;
+
+                        switch (currentEdge) {
+                            case EDGE_LT: {
+                                int l = mDrawableFloat.left + dx;
+                                int t = mDrawableFloat.top + dy;
+                                int r = mDrawableFloat.right;
+                                int b = mDrawableFloat.bottom;
+                                if (l < x || t < y || r > w || b > h) {
+
+                                } else {
+                                    mDrawableFloat.set(l, t, r, b);
+                                }
+                            }
+                            break;
+
+                            case EDGE_RT: {
+                                int l = mDrawableFloat.left;
+                                int t = mDrawableFloat.top + dy;
+                                int r = mDrawableFloat.right + dx;
+                                int b = mDrawableFloat.bottom;
+                                if (l < x || t < y || r > w || b > h) {
+
+                                } else {
+                                    mDrawableFloat.set(l, t, r, b);
+                                }
+                            }
+                            break;
+
+                            case EDGE_LB: {
+                                int l = mDrawableFloat.left + dx;
+                                int t = mDrawableFloat.top;
+                                int r = mDrawableFloat.right;
+                                int b = mDrawableFloat.bottom + dy;
+                                if (l < x || t < y || r > w || b > h) {
+
+                                } else {
+                                    mDrawableFloat.set(l, t, r, b);
+                                }
+                            }
+                            break;
+
+                            case EDGE_RB: {
+                                int l = mDrawableFloat.left;
+                                int t = mDrawableFloat.top;
+                                int r = mDrawableFloat.right + dx;
+                                int b = mDrawableFloat.bottom + dy;
+                                if (l < x || t < y || r > w || b > h) {
+
+                                } else {
+                                    mDrawableFloat.set(l, t, r, b);
+                                }
+                            }
+                            break;
+
+                            case EDGE_MOVE_IN:
+                                if (isTouchInSquare) {
+                                    int l = mDrawableFloat.left + dx;
+                                    int t = mDrawableFloat.top + dy;
+                                    int r = mDrawableFloat.right + dx;
+                                    int b = mDrawableFloat.bottom + dy;
+                                    if (l < x || t < y || r > w || b > h) {
+
+                                    } else {
+                                        mDrawableFloat.offset((int) dx, (int) dy);
+                                    }
+                                }
+                                break;
+
+                            case EDGE_MOVE_OUT:
+                                break;
+                        }
+                        mDrawableFloat.sort();
+                        invalidate();
+                    }
+                }
+                break;
+        }
+
+        return true;
+    }
+
+    // 根据初触摸点判断是触摸的Rect哪一个角
+    public int getTouch(int eventX, int eventY) {
+        if (mFloatDrawable.getBounds().left <= eventX
+                && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
+                .getBorderWidth())
+                && mFloatDrawable.getBounds().top <= eventY
+                && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
+                .getBorderHeight())) {
+            return EDGE_LT;
+        } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
+                .getBorderWidth()) <= eventX
+                && eventX < mFloatDrawable.getBounds().right
+                && mFloatDrawable.getBounds().top <= eventY
+                && eventY < (mFloatDrawable.getBounds().top + mFloatDrawable
+                .getBorderHeight())) {
+            return EDGE_RT;
+        } else if (mFloatDrawable.getBounds().left <= eventX
+                && eventX < (mFloatDrawable.getBounds().left + mFloatDrawable
+                .getBorderWidth())
+                && (mFloatDrawable.getBounds().bottom - mFloatDrawable
+                .getBorderHeight()) <= eventY
+                && eventY < mFloatDrawable.getBounds().bottom) {
+            return EDGE_LB;
+        } else if ((mFloatDrawable.getBounds().right - mFloatDrawable
+                .getBorderWidth()) <= eventX
+                && eventX < mFloatDrawable.getBounds().right
+                && (mFloatDrawable.getBounds().bottom - mFloatDrawable
+                .getBorderHeight()) <= eventY
+                && eventY < mFloatDrawable.getBounds().bottom) {
+            return EDGE_RB;
+        } else if (mFloatDrawable.getBounds().contains(eventX, eventY)) {
+            return EDGE_MOVE_IN;
+        }
+        return EDGE_MOVE_OUT;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mDrawable == null) {
+            return;
+        }
+
+        if (mDrawable.getIntrinsicWidth() == 0
+                || mDrawable.getIntrinsicHeight() == 0) {
+            return;
+        }
+
+
+        configureBounds();
+        // 在画布上花图片
+        mDrawable.draw(canvas);
+        canvas.save();
+        // 在画布上画浮层FloatDrawable,Region.Op.DIFFERENCE是表示Rect交集的补集
+        canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE);
+        // 在交集的补集上画上灰色用来区分
+        canvas.drawColor(Color.parseColor("#a0000000"));
+        canvas.restore();
+        // 画浮层
+        mFloatDrawable.draw(canvas);
+    }
+
+    protected void configureBounds() {
+        // configureBounds在onDraw方法中调用
+        // isFirst的目的是下面对mDrawableSrc和mDrawableFloat只初始化一次,
+        // 之后的变化是根据touch事件来变化的,而不是每次执行重新对mDrawableSrc和mDrawableFloat进行设置
+        if (isFrist) {
+            oriRationWH = ((float) mDrawable.getIntrinsicWidth())
+                    / ((float) mDrawable.getIntrinsicHeight());
+
+            final float scale = mContext.getResources().getDisplayMetrics().density;
+            int w = Math.min(getWidth(), (int) (mDrawable.getIntrinsicWidth()
+                    * scale + 0.5f));
+            int h = (int) (w / oriRationWH);
+
+            int left = (getWidth() - w) / 2;
+            int top = (getHeight() - h) / 2;
+            int right = left + w;
+            int bottom = top + h;
+
+            mDrawableSrc.set(left, top, right, bottom);
+            mDrawableDst.set(mDrawableSrc);
+
+            int floatWidth = dipTopx(mContext, cropWidth);
+            int floatHeight = dipTopx(mContext, cropHeight);
+
+            /**限制裁剪框初始化在图像区域*/
+            if (floatWidth > w) {
+                floatWidth = w;
+                floatHeight = cropHeight * floatWidth / cropWidth;
+            }
+
+            if (floatHeight > h) {
+                floatHeight = h;
+                floatWidth = cropWidth * floatHeight / cropHeight;
+            }
+
+            int floatLeft = (getWidth() - floatWidth) / 2;
+            int floatTop = (getHeight() - floatHeight) / 2;
+            mDrawableFloat.set(floatLeft, floatTop, floatLeft + floatWidth,
+                    floatTop + floatHeight);
+
+            isFrist = false;
+        }
+
+        mDrawable.setBounds(mDrawableDst);
+        mFloatDrawable.setBounds(mDrawableFloat);
+    }
+
+    // 在up事件中调用了该方法,目的是检查是否把浮层拖出了图像区域
+    protected void checkBounds() {
+        int newLeft = mDrawableFloat.left;
+        int newTop = mDrawableFloat.top;
+
+        int l = mDrawableDst.left;
+        int t = mDrawableDst.top;
+        int r = mDrawableDst.right;
+        int b = mDrawableDst.bottom;
+
+        boolean isChange = false;
+        if (mDrawableFloat.left < l) {
+            newLeft = l;
+            isChange = true;
+        }
+
+        if (mDrawableFloat.top < t) {
+            newTop = t;
+            isChange = true;
+        }
+
+        if (mDrawableFloat.right > r) {
+            newLeft = r - mDrawableFloat.width();
+            isChange = true;
+        }
+
+        if (mDrawableFloat.bottom > b) {
+            newTop = b - mDrawableFloat.height();
+            isChange = true;
+        }
+
+        mDrawableFloat.offsetTo(newLeft, newTop);
+        if (isChange) {
+            invalidate();
+        }
+    }
+
+    // 进行图片的裁剪
+    public Bitmap getCropImage() {
+
+        int width = mDrawableFloat.width();
+        int height = mDrawableFloat.height();
+
+
+        Rect cropRect = new Rect(0, 0, width, height);
+
+
+
+        try {
+            final Matrix outputMatrix = new Matrix();//用于最图图片的精确缩放
+            InputStream inputStream = new FileInputStream(file);
+            final BitmapFactory.Options ops = new BitmapFactory.Options();
+            BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream, false);
+
+            final float scale = ((float) decoder.getWidth()) / mDrawableDst.width();
+            cropRect.left = (int) ((mDrawableFloat.left - mDrawableDst.left) * scale);
+            cropRect.top = (int) ((mDrawableFloat.top - mDrawableDst.top) * scale);
+            cropRect.right = (int) ((mDrawableFloat.right - mDrawableDst.left) * scale);
+            cropRect.bottom = (int) ((mDrawableFloat.bottom - mDrawableDst.top) * scale);
+
+            final Bitmap source = decoder.decodeRegion(cropRect, ops);
+            return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), outputMatrix, false);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+
+
+        return null;
+    }
+
+    public int dipTopx(Context context, float dpValue) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+}

+ 123 - 0
ApexDrivers/racameralib/src/main/java/redant/usai/com/racameralib/crop/FloatDrawable.java

@@ -0,0 +1,123 @@
+package redant.usai.com.racameralib.crop;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class FloatDrawable extends Drawable {
+
+    private Context mContext;
+    private int offset = 50;
+    private Paint mLinePaint = new Paint();
+    private Paint mLinePaint2 = new Paint();
+    {
+        mLinePaint.setARGB(200, 50, 50, 50);
+        mLinePaint.setStrokeWidth(1F);
+        mLinePaint.setStyle(Paint.Style.STROKE);
+        mLinePaint.setAntiAlias(true);
+        mLinePaint.setColor(Color.WHITE);
+        //
+        mLinePaint2.setARGB(200, 50, 50, 50);
+        mLinePaint2.setStrokeWidth(7F);
+        mLinePaint2.setStyle(Paint.Style.STROKE);
+        mLinePaint2.setAntiAlias(true);
+        mLinePaint2.setColor(Color.WHITE);
+    }
+
+    public FloatDrawable(Context context) {
+        super();
+        this.mContext = context;
+
+    }
+
+    public int getBorderWidth() {
+        return dipTopx(mContext, offset);//根据dip计算的像素值,做适配用的
+    }
+
+    public int getBorderHeight() {
+        return dipTopx(mContext, offset);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+
+        int left = getBounds().left;
+        int top = getBounds().top;
+        int right = getBounds().right;
+        int bottom = getBounds().bottom;
+
+        Rect mRect = new Rect(left + dipTopx(mContext, offset) / 2, top
+                + dipTopx(mContext, offset) / 2, right
+                - dipTopx(mContext, offset) / 2, bottom
+                - dipTopx(mContext, offset) / 2);
+        //画默认的选择框
+        canvas.drawRect(mRect, mLinePaint);
+        //画四个角的四个粗拐角、也就是八条粗线
+        canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), top
+                        + dipTopx(mContext, offset) / 2,
+                left + dipTopx(mContext, offset) - 8f,
+                top + dipTopx(mContext, offset) / 2, mLinePaint2);
+        canvas.drawLine(left + dipTopx(mContext, offset) / 2,
+                top + dipTopx(mContext, offset) / 2,
+                left + dipTopx(mContext, offset) / 2,
+                top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
+        canvas.drawLine(right - dipTopx(mContext, offset) + 8f,
+                top + dipTopx(mContext, offset) / 2,
+                right - dipTopx(mContext, offset) / 2,
+                top + dipTopx(mContext, offset) / 2, mLinePaint2);
+        canvas.drawLine(right - dipTopx(mContext, offset) / 2,
+                top + dipTopx(mContext, offset) / 2 - 3.5f,
+                right - dipTopx(mContext, offset) / 2,
+                top + dipTopx(mContext, offset) / 2 + 30, mLinePaint2);
+        canvas.drawLine((left + dipTopx(mContext, offset) / 2 - 3.5f), bottom
+                        - dipTopx(mContext, offset) / 2,
+                left + dipTopx(mContext, offset) - 8f,
+                bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
+        canvas.drawLine((left + dipTopx(mContext, offset) / 2), bottom
+                        - dipTopx(mContext, offset) / 2,
+                (left + dipTopx(mContext, offset) / 2),
+                bottom - dipTopx(mContext, offset) / 2 - 30f, mLinePaint2);
+        canvas.drawLine((right - dipTopx(mContext, offset) + 8f), bottom
+                        - dipTopx(mContext, offset) / 2,
+                right - dipTopx(mContext, offset) / 2,
+                bottom - dipTopx(mContext, offset) / 2, mLinePaint2);
+        canvas.drawLine((right - dipTopx(mContext, offset) / 2), bottom
+                        - dipTopx(mContext, offset) / 2 - 30f,
+                right - dipTopx(mContext, offset) / 2,
+                bottom - dipTopx(mContext, offset) / 2 + 3.5f, mLinePaint2);
+
+    }
+
+    @Override
+    public void setBounds(Rect bounds) {
+        super.setBounds(new Rect(bounds.left - dipTopx(mContext, offset) / 2,
+                bounds.top - dipTopx(mContext, offset) / 2, bounds.right
+                + dipTopx(mContext, offset) / 2, bounds.bottom
+                + dipTopx(mContext, offset) / 2));
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+
+    }
+
+    @Override
+    public int getOpacity() {
+        return 0;
+    }
+
+    public int dipTopx(Context context, float dpValue) {
+        final float scale = context.getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+}

BIN
ApexDrivers/racameralib/src/main/res/drawable-hdpi/btn_photo_assign.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-hdpi/btn_photo_retake.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-ldpi/btn_photo_assign.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-ldpi/btn_photo_retake.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-mdpi/btn_photo_assign.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-mdpi/btn_photo_retake.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-xhdpi/btn_photo_assign.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-xhdpi/btn_photo_retake.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-xxhdpi/btn_photo_assign.png


BIN
ApexDrivers/racameralib/src/main/res/drawable-xxhdpi/btn_photo_retake.png


+ 68 - 0
ApexDrivers/racameralib/src/main/res/layout/activity_crop_image.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="#000000">
+
+    <RelativeLayout
+        android:id="@+id/control_v"
+        android:layout_width="match_parent"
+        android:layout_height="80dp"
+        android:layout_alignParentBottom="true"
+        android:background="#000000">
+
+        <View
+            android:id="@+id/control_seperator"
+            android:layout_width="match_parent"
+            android:layout_height="0.5dp"
+            android:background="#ffffff"
+            />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_below="@id/control_seperator"
+            android:orientation="horizontal"
+            android:layout_centerInParent="true"
+            >
+
+            <Button
+                android:id="@+id/cancel_btn"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="#00000000"
+                android:text="cancel"
+                android:textColor="#ffffff"
+                android:textAllCaps="false"
+                android:layout_margin="10dp"
+                />
+
+            <Button
+                android:id="@+id/crop_btn"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="#00000000"
+                android:text="save"
+                android:textColor="#ffffff"
+                android:textAllCaps="false"
+                android:layout_margin="10dp"
+                />
+
+        </LinearLayout>
+
+
+
+    </RelativeLayout>
+
+    <redant.usai.com.racameralib.crop.CropImageView
+        android:id="@+id/cropimage"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_above="@id/control_v"
+        />
+
+
+</RelativeLayout>

+ 68 - 0
ApexDrivers/racameralib/src/main/res/layout/picture_preview_activity.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".camera.PicturePreviewActivity"
+    android:background="#000000"
+    >
+
+
+    <ImageView
+        android:id="@+id/picture_preview_iv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="fitCenter"
+        />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_alignParentBottom="true"
+        android:layout_marginBottom="80dp"
+        android:background="#00000000"
+        >
+
+        <RelativeLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:background="#00000000"
+            >
+
+            <ImageButton
+                android:id="@+id/picture_retake_btn"
+                android:layout_width="60dp"
+                android:layout_height="60dp"
+                android:layout_centerInParent="true"
+                android:background="#00000000"
+                android:src="@drawable/btn_photo_retake"
+                />
+
+        </RelativeLayout>
+
+
+        <RelativeLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:background="#00000000"
+            >
+
+            <ImageButton
+                android:id="@+id/picture_assign_btn"
+                android:layout_width="60dp"
+                android:layout_height="60dp"
+                android:layout_centerInParent="true"
+                android:background="#00000000"
+                android:src="@drawable/btn_photo_assign"
+                />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+</RelativeLayout>

+ 11 - 0
ApexDrivers/racameralib/src/main/res/menu/preview_picture_menu.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/edit_item"
+        android:title="Edit"
+        app:showAsAction="always"
+        />
+
+</menu>