diff --git a/uCropTest/.gitignore b/uCropTest/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/uCropTest/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/uCropTest/.idea/gradle.xml b/uCropTest/.idea/gradle.xml new file mode 100644 index 0000000..c30b44b --- /dev/null +++ b/uCropTest/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/uCropTest/.idea/misc.xml b/uCropTest/.idea/misc.xml new file mode 100644 index 0000000..3963879 --- /dev/null +++ b/uCropTest/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/uCropTest/.idea/modules.xml b/uCropTest/.idea/modules.xml new file mode 100644 index 0000000..d46ce81 --- /dev/null +++ b/uCropTest/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/uCropTest/.idea/runConfigurations.xml b/uCropTest/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/uCropTest/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/uCropTest/app/.gitignore b/uCropTest/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/uCropTest/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/uCropTest/app/build.gradle b/uCropTest/app/build.gradle new file mode 100644 index 0000000..4c52551 --- /dev/null +++ b/uCropTest/app/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + defaultConfig { + applicationId "com.yongle.ucroptest" + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + implementation project(':ucrop') + //图片阴影 + compile 'com.dingmouren.paletteimageview:paletteimageview:1.0.7' +} diff --git a/uCropTest/app/proguard-rules.pro b/uCropTest/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/uCropTest/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/uCropTest/app/src/androidTest/java/com/yongle/ucroptest/ExampleInstrumentedTest.java b/uCropTest/app/src/androidTest/java/com/yongle/ucroptest/ExampleInstrumentedTest.java new file mode 100644 index 0000000..3cd84d7 --- /dev/null +++ b/uCropTest/app/src/androidTest/java/com/yongle/ucroptest/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.yongle.ucroptest; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.yongle.ucroptest", appContext.getPackageName()); + } +} diff --git a/uCropTest/app/src/main/AndroidManifest.xml b/uCropTest/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..55dc466 --- /dev/null +++ b/uCropTest/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/uCropTest/app/src/main/java/com/yongle/ucroptest/ImageUtils.java b/uCropTest/app/src/main/java/com/yongle/ucroptest/ImageUtils.java new file mode 100644 index 0000000..a3e7021 --- /dev/null +++ b/uCropTest/app/src/main/java/com/yongle/ucroptest/ImageUtils.java @@ -0,0 +1,179 @@ +package com.yongle.ucroptest; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.view.ViewTreeObserver; +import android.widget.ImageView; + + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public class ImageUtils { + public static final int REQUE_CODE_CAMERA = 100; + public static final int REQUE_CODE_PHOTO = 101; + public static final int REQUE_CODE_CROP = 103; + + /** + * 平铺图片 + * + * @param imageView + * @param src + * @return + */ + public static void tileImage(final ImageView imageView, final Bitmap src){ + ViewTreeObserver vto1 = imageView.getViewTreeObserver(); + vto1.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + imageView.setImageBitmap(ImageUtils.createRepeater(imageView.getWidth(),src)); + } + }); + } + public static Bitmap createRepeaters(int width, Bitmap src) { + int count = width / src.getWidth(); + + Bitmap bitmap = Bitmap.createBitmap(width, src.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + for (int idx = 0; idx < count; ++idx) { + canvas.drawBitmap(src, idx * src.getWidth(), 0, null); + } + return bitmap; + } + + /** + * 平铺图片 + * + * @param width + * @param src + * @return + */ + public static Bitmap createRepeater(int width, Bitmap src) { + int count = (width + src.getWidth() - 1) / src.getWidth(); + + Bitmap bitmap = Bitmap.createBitmap(width, src.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + for (int idx = 0; idx < count; ++idx) { + canvas.drawBitmap(src, idx * src.getWidth(), 0, null); + } + return bitmap; + } + + public static void startPhotoZoom(Context context, Uri uri, + int REQUE_CODE_CROP) { + int dp = 500; + + Intent intent = new Intent("com.android.camera.action.CROP"); + intent.setDataAndType(uri, "image/*"); + // 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪 + intent.putExtra("crop", "true"); + intent.putExtra("scale", true);// 去黑边 + intent.putExtra("scaleUpIfNeeded", true);// 去黑边 + // aspectX aspectY 是宽高的比例 + intent.putExtra("aspectX", 1);// 输出是X方向的比例 + intent.putExtra("aspectY", 1); + // outputX outputY 是裁剪图片宽高,切忌不要再改动下列数字,会卡死 + intent.putExtra("outputX", dp);// 输出X方向的像素 + intent.putExtra("outputY", dp); + intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); + intent.putExtra("noFaceDetection", true); + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + intent.putExtra("return-data", false);// 设置为不返回数据 + ((Activity) context).startActivityForResult(intent, REQUE_CODE_CROP); + } + public final static String photoPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/ylpw/photo"; + public final static String takePhotoName = "takePhoto.jpeg"; + //打开照相机 + public static File getImageFromCamer(Context context, + int REQUE_CODE_CAMERA) { + // 保存裁剪后的图片文件 + File pictureFileDir = new File(photoPath); + if (!pictureFileDir.exists()) { + pictureFileDir.mkdirs(); + } + File picFile = new File(pictureFileDir, takePhotoName); + if (!picFile.exists()) { + try { + picFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + Uri photoUri = Uri.fromFile(picFile); + + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); +// File fileDir = HelpUtil.getFile(context, "/Tour/user_photos"); +// cameraFile = new File(fileDir.getAbsoluteFile() + "/" +// + System.currentTimeMillis() + ".jpg"); + intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picFile)); + ((Activity) context).startActivityForResult(intent, REQUE_CODE_CAMERA); + return picFile; + } + + //打开相册 + public static void getImageFromPhoto(Context context, int REQUE_CODE_PHOTO) { + Intent intent = new Intent(Intent.ACTION_PICK, null); + intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + "image/*"); + ((Activity) context).startActivityForResult(intent, REQUE_CODE_PHOTO); + } + + public static Bitmap getBitmapFromUri(Uri uri, Context mContext) { + try { + // 读取uri所在的图片 + Bitmap bitmap = MediaStore.Images.Media.getBitmap( + mContext.getContentResolver(), uri); + return bitmap; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据图片的url路径获得Bitmap对象 + * @param url + * @return + */ + public static Bitmap returnBitmap(String url) { + URL fileUrl = null; + Bitmap bitmap = null; + + try { + fileUrl = new URL(url); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + + try { + HttpURLConnection conn = (HttpURLConnection) fileUrl + .openConnection(); + conn.setDoInput(true); + conn.connect(); + InputStream is = conn.getInputStream(); + bitmap = BitmapFactory.decodeStream(is); + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return bitmap; + + } +} diff --git a/uCropTest/app/src/main/java/com/yongle/ucroptest/MainActivity.java b/uCropTest/app/src/main/java/com/yongle/ucroptest/MainActivity.java new file mode 100644 index 0000000..16efd0e --- /dev/null +++ b/uCropTest/app/src/main/java/com/yongle/ucroptest/MainActivity.java @@ -0,0 +1,208 @@ +package com.yongle.ucroptest; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + + +import com.dingmouren.paletteimageview.PaletteImageView; +import com.dingmouren.paletteimageview.listener.OnParseColorListener; + +import java.io.File; + +public class MainActivity extends AppCompatActivity { + private AlertDialog mAlertDialog; + protected static final int REQUEST_STORAGE_READ_ACCESS_PERMISSION = 101; + protected static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 102; + PaletteImageView mPaletteImageView; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + getImageFromPhoto(MainActivity.this, ImageUtils.REQUE_CODE_PHOTO); + } + }); + mPaletteImageView=findViewById(R.id.palette); + + } + + /** + * 弹出请求权限的对话框 + * + * @param permission + * @param rationale + * @param requestCode + */ + protected void requestPermission(final String permission, String rationale, final int requestCode) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { + showAlertDialog(getString(R.string.permission_title_rationale), rationale, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ActivityCompat.requestPermissions(MainActivity.this, + new String[]{permission}, requestCode); + } + }, getString(R.string.label_ok), null, getString(R.string.label_cancel)); + } else { + ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); + } + } + + //打开相册 + public void getImageFromPhoto(Context context, int REQUE_CODE_PHOTO) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN + && ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, + getString(R.string.permission_read_storage_rationale), + REQUEST_STORAGE_READ_ACCESS_PERMISSION); + } else { + Intent intent = new Intent(Intent.ACTION_PICK, null); + intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + "image/*"); + ((Activity) context).startActivityForResult(intent, REQUE_CODE_PHOTO); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { + //相册返回 + if (requestCode == ImageUtils.REQUE_CODE_PHOTO) { + final Uri selectedUri = data.getData(); + if (selectedUri != null) { + startCropActivity(data.getData()); + } else { + Toast.makeText(MainActivity.this, "toast_cannot_retrieve_selected_image", Toast.LENGTH_SHORT).show(); + } + } else if (requestCode == MyUCrop.REQUEST_CROP) { + handleCropResult(data); + } + } + if (resultCode == MyUCrop.RESULT_ERROR) { + handleCropError(data); + } + } + + private void startCropActivity(@NonNull Uri uri) { + String destinationFileName = System.currentTimeMillis()+"SampleCropImage.jpg"; + MyUCrop uCrop = MyUCrop.of(uri, Uri.fromFile(new File(getCacheDir(), destinationFileName))); + uCrop = basisConfig(uCrop); + uCrop = advancedConfig(uCrop); + uCrop.startMyActivity(this,MyCropActivity.class); + } + + /** + * Sometimes you want to adjust more options, it's done via {@link com.yalantis.ucrop.UCrop.Options} class. + * + * @param uCrop - ucrop builder instance + * @return - ucrop builder instance + */ + private MyUCrop advancedConfig(@NonNull MyUCrop uCrop) { + MyUCrop.Options options = new MyUCrop.Options(); + options.setCompressionFormat(Bitmap.CompressFormat.JPEG); + options.setHideBottomControls(true); + //自由裁剪是否开启 + options.setFreeStyleCropEnabled(true); + options.setCropGridColor(getResources().getColor(R.color.colorAccent)); + options.setCropFrameColor(getResources().getColor(R.color.colorAccent)); + options.setMaxBitmapSize(640); + options.setToolbarTitle("裁剪去吧baby"); + return uCrop.withOptions(options); + } + + /** + * In most cases you need only to set crop aspect ration and max size for resulting image. + * + * @param uCrop - ucrop builder instance + * @return - ucrop builder instance + */ + private MyUCrop basisConfig(@NonNull MyUCrop uCrop) { + uCrop = uCrop.useSourceImageAspectRatio();// 根据图片等比裁剪 +// uCrop = uCrop.withAspectRatio(1, 1); 1 :1 裁剪 + return uCrop; + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_STORAGE_READ_ACCESS_PERMISSION: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + getImageFromPhoto(MainActivity.this, ImageUtils.REQUE_CODE_PHOTO); + } + break; + default: + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + + /** + * This method shows dialog with given title & message. + * Also there is an option to pass onClickListener for positive & negative button. + * + * @param title - dialog title + * @param message - dialog message + * @param onPositiveButtonClickListener - listener for positive button + * @param positiveText - positive button text + * @param onNegativeButtonClickListener - listener for negative button + * @param negativeText - negative button text + */ + protected void showAlertDialog(@Nullable String title, @Nullable String message, + @Nullable DialogInterface.OnClickListener onPositiveButtonClickListener, + @NonNull String positiveText, + @Nullable DialogInterface.OnClickListener onNegativeButtonClickListener, + @NonNull String negativeText) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(title); + builder.setMessage(message); + builder.setPositiveButton(positiveText, onPositiveButtonClickListener); + builder.setNegativeButton(negativeText, onNegativeButtonClickListener); + mAlertDialog = builder.show(); + } + //裁剪返回 + private void handleCropResult(@NonNull Intent result) { + final Uri resultUri = MyUCrop.getOutput(result); + if (resultUri != null) { + ImageView imageView= findViewById(R.id.image); + Toast.makeText(this,"裁剪返回", Toast.LENGTH_SHORT).show(); + imageView.setImageURI(resultUri); + } else { + Toast.makeText(this,"裁剪返回失败", Toast.LENGTH_SHORT).show(); + } + } + + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + private void handleCropError(@NonNull Intent result) { + final Throwable cropError = MyUCrop.getError(result); + if (cropError != null) { + + Toast.makeText(this, cropError.getMessage(), Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(this, "unexpected_error", Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/uCropTest/app/src/main/java/com/yongle/ucroptest/MyCropActivity.java b/uCropTest/app/src/main/java/com/yongle/ucroptest/MyCropActivity.java new file mode 100644 index 0000000..c9d3b45 --- /dev/null +++ b/uCropTest/app/src/main/java/com/yongle/ucroptest/MyCropActivity.java @@ -0,0 +1,464 @@ +package com.yongle.ucroptest; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.graphics.PorterDuff; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.yalantis.ucrop.callback.BitmapCropCallback; +import com.yalantis.ucrop.model.AspectRatio; +import com.yalantis.ucrop.view.CropImageView; +import com.yalantis.ucrop.view.GestureCropImageView; +import com.yalantis.ucrop.view.OverlayView; +import com.yalantis.ucrop.view.TransformImageView; +import com.yalantis.ucrop.view.UCropView; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class MyCropActivity extends AppCompatActivity { + public static final int DEFAULT_COMPRESS_QUALITY = 90; + public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.JPEG; + + public static final int NONE = 0; + public static final int SCALE = 1; + public static final int ROTATE = 2; + public static final int ALL = 3; + + @IntDef({NONE, SCALE, ROTATE, ALL}) + @Retention(RetentionPolicy.SOURCE) + public @interface GestureTypes { + + } + + private static final String TAG = "UCropActivity"; + + private static final int TABS_COUNT = 3; + private static final int SCALE_WIDGET_SENSITIVITY_COEFFICIENT = 15000; + private static final int ROTATE_WIDGET_SENSITIVITY_COEFFICIENT = 42; + + private String mToolbarTitle; + + // Enables dynamic coloring + private int mToolbarColor; + private int mStatusBarColor; + private int mActiveWidgetColor; + private int mToolbarWidgetColor; + @ColorInt private int mRootViewBackgroundColor; + @DrawableRes + private int mToolbarCancelDrawable; + @DrawableRes private int mToolbarCropDrawable; + private int mLogoColor; + + private boolean mShowLoader = true; + + private UCropView mUCropView; + private GestureCropImageView mGestureCropImageView; + private OverlayView mOverlayView; + private View mBlockingView; + + private Bitmap.CompressFormat mCompressFormat = DEFAULT_COMPRESS_FORMAT; + private int mCompressQuality = DEFAULT_COMPRESS_QUALITY; + private int[] mAllowedGestures = new int[]{SCALE, ROTATE, ALL}; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_my_crop); + final Intent intent = getIntent(); + setupViews(intent); + setImageData(intent); + setAllowedGestures(0); + addBlockingView(); + Toast.makeText(this, mGestureCropImageView.getCurrentScale()+"", Toast.LENGTH_SHORT).show(); + } + private void setAllowedGestures(int tab) { + mGestureCropImageView.setScaleEnabled(mAllowedGestures[tab] == ALL || mAllowedGestures[tab] == SCALE); + mGestureCropImageView.setRotateEnabled(mAllowedGestures[tab] == ALL || mAllowedGestures[tab] == ROTATE); + } + /** + * 这里不太懂什么意思 =为什么要在ucrop_photobox 里面 添加一个view// 已经看懂 + * + * Adds view that covers everything below the Toolbar. + * When it's clickable - user won't be able to click/touch anything below the Toolbar. + * Need to block user input while loading and cropping an image. + */ + private void addBlockingView() { + if (mBlockingView == null) { + mBlockingView = new View(this); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, R.id.toolbar); + mBlockingView.setLayoutParams(lp); + mBlockingView.setClickable(true); + } + + ((RelativeLayout) findViewById(R.id.ucrop_photobox)).addView(mBlockingView); + } + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.ucrop_menu_activity, menu); + + // Change crop & loader menu icons color to match the rest of the UI colors + + MenuItem menuItemLoader = menu.findItem(R.id.menu_loader); + Drawable menuItemLoaderIcon = menuItemLoader.getIcon(); + if (menuItemLoaderIcon != null) { + try { + menuItemLoaderIcon.mutate(); + menuItemLoaderIcon.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); + menuItemLoader.setIcon(menuItemLoaderIcon); + } catch (IllegalStateException e) { + Log.i(TAG, String.format("%s - %s", e.getMessage(), getString(R.string.ucrop_mutate_exception_hint))); + } + ((Animatable) menuItemLoader.getIcon()).start(); + } + + MenuItem menuItemCrop = menu.findItem(R.id.menu_crop); + Drawable menuItemCropIcon = ContextCompat.getDrawable(this, mToolbarCropDrawable); + if (menuItemCropIcon != null) { + menuItemCropIcon.mutate(); + menuItemCropIcon.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); + menuItemCrop.setIcon(menuItemCropIcon); + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.menu_crop).setVisible(!mShowLoader); + menu.findItem(R.id.menu_loader).setVisible(mShowLoader); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_crop) { + cropAndSaveImage(); + } else if (item.getItemId() == android.R.id.home) { + onBackPressed(); + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStop() { + super.onStop(); + if (mGestureCropImageView != null) { + mGestureCropImageView.cancelAllAnimations(); + } + } + + /** + * This method extracts all data from the incoming intent and setups views properly. + * 设置 裁剪图片相关 + */ + private void setImageData(@NonNull Intent intent) { + Uri inputUri = intent.getParcelableExtra(MyUCrop.EXTRA_INPUT_URI); + Uri outputUri = intent.getParcelableExtra(MyUCrop.EXTRA_OUTPUT_URI); + processOptions(intent); + + if (inputUri != null && outputUri != null) { + try { + mGestureCropImageView.setImageUri(inputUri, outputUri); + } catch (Exception e) { + setResultError(e); + finish(); + } + } else { + setResultError(new NullPointerException(getString(R.string.ucrop_error_input_data_is_absent))); + finish(); + } + } + + /** + * This method extracts {@link com.yalantis.ucrop.UCrop.Options #optionsBundle} from incoming intent + * and setups Activity, {@link OverlayView} and {@link CropImageView} properly. + */ + @SuppressWarnings("deprecation") + private void processOptions(@NonNull Intent intent) { + // Bitmap compression options + String compressionFormatName = intent.getStringExtra(MyUCrop.Options.EXTRA_COMPRESSION_FORMAT_NAME); + Bitmap.CompressFormat compressFormat = null; + if (!TextUtils.isEmpty(compressionFormatName)) { + compressFormat = Bitmap.CompressFormat.valueOf(compressionFormatName); + } + mCompressFormat = (compressFormat == null) ? DEFAULT_COMPRESS_FORMAT : compressFormat; + + mCompressQuality = intent.getIntExtra(MyUCrop.Options.EXTRA_COMPRESSION_QUALITY, DEFAULT_COMPRESS_QUALITY); + + // Gestures options + int[] allowedGestures = intent.getIntArrayExtra(MyUCrop.Options.EXTRA_ALLOWED_GESTURES); + if (allowedGestures != null && allowedGestures.length == TABS_COUNT) { + mAllowedGestures = allowedGestures; + } + + // Crop image view options + mGestureCropImageView.setMaxBitmapSize(intent.getIntExtra(MyUCrop.Options.EXTRA_MAX_BITMAP_SIZE, CropImageView.DEFAULT_MAX_BITMAP_SIZE)); + mGestureCropImageView.setMaxScaleMultiplier(intent.getFloatExtra(MyUCrop.Options.EXTRA_MAX_SCALE_MULTIPLIER, CropImageView.DEFAULT_MAX_SCALE_MULTIPLIER)); + mGestureCropImageView.setImageToWrapCropBoundsAnimDuration(intent.getIntExtra(MyUCrop.Options.EXTRA_IMAGE_TO_CROP_BOUNDS_ANIM_DURATION, CropImageView.DEFAULT_IMAGE_TO_CROP_BOUNDS_ANIM_DURATION)); + + // Overlay view options + mOverlayView.setFreestyleCropEnabled(intent.getBooleanExtra(MyUCrop.Options.EXTRA_FREE_STYLE_CROP, OverlayView.DEFAULT_FREESTYLE_CROP_MODE != OverlayView.FREESTYLE_CROP_MODE_DISABLE)); + + mOverlayView.setDimmedColor(intent.getIntExtra(MyUCrop.Options.EXTRA_DIMMED_LAYER_COLOR, getResources().getColor(R.color.ucrop_color_default_dimmed))); + mOverlayView.setCircleDimmedLayer(intent.getBooleanExtra(MyUCrop.Options.EXTRA_CIRCLE_DIMMED_LAYER, OverlayView.DEFAULT_CIRCLE_DIMMED_LAYER)); + + mOverlayView.setShowCropFrame(intent.getBooleanExtra(MyUCrop.Options.EXTRA_SHOW_CROP_FRAME, OverlayView.DEFAULT_SHOW_CROP_FRAME)); + mOverlayView.setCropFrameColor(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_FRAME_COLOR, getResources().getColor(R.color.colorAccent))); + mOverlayView.setCropFrameStrokeWidth(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_FRAME_STROKE_WIDTH, getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_frame_stoke_width))); + + mOverlayView.setShowCropGrid(intent.getBooleanExtra(MyUCrop.Options.EXTRA_SHOW_CROP_GRID, OverlayView.DEFAULT_SHOW_CROP_GRID)); + mOverlayView.setCropGridRowCount(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_GRID_ROW_COUNT, OverlayView.DEFAULT_CROP_GRID_ROW_COUNT)); + mOverlayView.setCropGridColumnCount(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_GRID_COLUMN_COUNT, OverlayView.DEFAULT_CROP_GRID_COLUMN_COUNT)); + mOverlayView.setCropGridColor(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_GRID_COLOR, getResources().getColor(R.color.ucrop_color_default_crop_grid))); + mOverlayView.setCropGridStrokeWidth(intent.getIntExtra(MyUCrop.Options.EXTRA_CROP_GRID_STROKE_WIDTH, getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_grid_stoke_width))); + + // Aspect ratio options + float aspectRatioX = intent.getFloatExtra(MyUCrop.EXTRA_ASPECT_RATIO_X, 0); + float aspectRatioY = intent.getFloatExtra(MyUCrop.EXTRA_ASPECT_RATIO_Y, 0); + + int aspectRationSelectedByDefault = intent.getIntExtra(MyUCrop.Options.EXTRA_ASPECT_RATIO_SELECTED_BY_DEFAULT, 0); + ArrayList aspectRatioList = intent.getParcelableArrayListExtra(MyUCrop.Options.EXTRA_ASPECT_RATIO_OPTIONS); + + if (aspectRatioX > 0 && aspectRatioY > 0) { + mGestureCropImageView.setTargetAspectRatio(aspectRatioX / aspectRatioY); + } else if (aspectRatioList != null && aspectRationSelectedByDefault < aspectRatioList.size()) { + mGestureCropImageView.setTargetAspectRatio(aspectRatioList.get(aspectRationSelectedByDefault).getAspectRatioX() / + aspectRatioList.get(aspectRationSelectedByDefault).getAspectRatioY()); + } else { + mGestureCropImageView.setTargetAspectRatio(CropImageView.SOURCE_IMAGE_ASPECT_RATIO); + } + + // Result bitmap max size options + int maxSizeX = intent.getIntExtra(MyUCrop.EXTRA_MAX_SIZE_X, 0); + int maxSizeY = intent.getIntExtra(MyUCrop.EXTRA_MAX_SIZE_Y, 0); + + if (maxSizeX > 0 && maxSizeY > 0) { + mGestureCropImageView.setMaxResultImageSizeX(maxSizeX); + mGestureCropImageView.setMaxResultImageSizeY(maxSizeY); + } + } + + /** + * 设置界面 toobar 相关 + * @param intent + */ + private void setupViews(@NonNull Intent intent) { + mStatusBarColor = intent.getIntExtra(MyUCrop.Options.EXTRA_STATUS_BAR_COLOR, ContextCompat.getColor(this, R.color.ucrop_color_statusbar)); + mToolbarColor = intent.getIntExtra(MyUCrop.Options.EXTRA_TOOL_BAR_COLOR, ContextCompat.getColor(this, R.color.ucrop_color_toolbar)); + mActiveWidgetColor = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_COLOR_WIDGET_ACTIVE, ContextCompat.getColor(this, R.color.ucrop_color_widget_active)); + mToolbarWidgetColor = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_WIDGET_COLOR_TOOLBAR, ContextCompat.getColor(this, R.color.ucrop_color_toolbar_widget)); + mToolbarCancelDrawable = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_WIDGET_CANCEL_DRAWABLE, R.drawable.ucrop_ic_cross); + mToolbarCropDrawable = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_WIDGET_CROP_DRAWABLE, R.drawable.ucrop_ic_done); + mToolbarTitle = intent.getStringExtra(MyUCrop.Options.EXTRA_UCROP_TITLE_TEXT_TOOLBAR); + mToolbarTitle = mToolbarTitle != null ? mToolbarTitle : getResources().getString(R.string.ucrop_label_edit_photo); + mLogoColor = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_LOGO_COLOR, ContextCompat.getColor(this, R.color.ucrop_color_default_logo)); + mRootViewBackgroundColor = intent.getIntExtra(MyUCrop.Options.EXTRA_UCROP_ROOT_VIEW_BACKGROUND_COLOR, ContextCompat.getColor(this, R.color.ucrop_color_crop_background)); + + setupAppBar(); + initiateRootViews(); + ViewGroup photoBox = (ViewGroup) findViewById(R.id.ucrop_photobox); + View.inflate(this, R.layout.ucrop_bottom_control, photoBox); + findViewById(R.id.state_rotate).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + rotateByAngle(90); + } + }); + findViewById(R.id.state_reset).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + resetRotation(); + } + }); + + } + + /** + * Configures and styles both status bar and toolbar. + */ + private void setupAppBar() { + setStatusBarColor(mStatusBarColor); + + final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + + // Set all of the Toolbar coloring + toolbar.setBackgroundColor(mToolbarColor); + toolbar.setTitleTextColor(mToolbarWidgetColor); + final TextView toolbarTitle = (TextView) toolbar.findViewById(R.id.toolbar_title); + toolbarTitle.setTextColor(mToolbarWidgetColor); + toolbarTitle.setText(mToolbarTitle); + + // Color buttons inside the Toolbar + Drawable stateButtonDrawable = ContextCompat.getDrawable(this, mToolbarCancelDrawable).mutate(); + stateButtonDrawable.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); + toolbar.setNavigationIcon(stateButtonDrawable); + + setSupportActionBar(toolbar); + final ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayShowTitleEnabled(false); + } + } + + private void initiateRootViews() { + mUCropView = (UCropView) findViewById(R.id.ucrop); + mGestureCropImageView = mUCropView.getCropImageView(); + mOverlayView = mUCropView.getOverlayView(); + mGestureCropImageView.setTransformImageListener(mImageListener); + + ((ImageView) findViewById(R.id.image_view_logo)).setColorFilter(mLogoColor, PorterDuff.Mode.SRC_ATOP); + + findViewById(R.id.ucrop_frame).setBackgroundColor(mRootViewBackgroundColor); + } + + private TransformImageView.TransformImageListener mImageListener = new TransformImageView.TransformImageListener() { + @Override + public void onRotate(float currentAngle) { +// setAngleText(currentAngle); + } + + @Override + public void onScale(float currentScale) { +// setScaleText(currentScale); + } + + @Override + public void onLoadComplete() { + mUCropView.animate().alpha(1).setDuration(300).setInterpolator(new AccelerateInterpolator()); + mBlockingView.setClickable(false); + mShowLoader = false; + supportInvalidateOptionsMenu(); + } + + @Override + public void onLoadFailure(@NonNull Exception e) { + setResultError(e); + finish(); + } + + }; + + + + /** + * Sets status-bar color for L devices. + * + * @param color - status-bar color + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void setStatusBarColor(@ColorInt int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + final Window window = getWindow(); + if (window != null) { + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(color); + } + } + } + + + /** + * 还原裁剪 + */ + private void resetRotation() { + mGestureCropImageView.setTargetAspectRatio(CropImageView.SOURCE_IMAGE_ASPECT_RATIO);//裁剪比例還原 + mGestureCropImageView.postRotate(-mGestureCropImageView.getCurrentAngle()); + //缩放为原来的比例 + float currentScale=mGestureCropImageView.getCurrentScale(); + Matrix matrix = new Matrix(); + matrix.setScale(1.0f/currentScale,1.0f/currentScale); + mGestureCropImageView.setImageMatrix(new Matrix()); + + + mGestureCropImageView.setImageToWrapCropBounds(); + } + + /** + * 旋转 + * @param angle + */ + private void rotateByAngle(int angle) { + mGestureCropImageView.postRotate(angle); + mGestureCropImageView.setImageToWrapCropBounds(); + } + + + /** + * 裁剪 保存图片 + */ + protected void cropAndSaveImage() { + mBlockingView.setClickable(true); + mShowLoader = true; + supportInvalidateOptionsMenu(); + + mGestureCropImageView.cropAndSaveImage(mCompressFormat, mCompressQuality, new BitmapCropCallback() { + + @Override + public void onBitmapCropped(@NonNull Uri resultUri, int offsetX, int offsetY, int imageWidth, int imageHeight) { + setResultUri(resultUri, mGestureCropImageView.getTargetAspectRatio(), offsetX, offsetY, imageWidth, imageHeight); + finish(); + } + + @Override + public void onCropFailure(@NonNull Throwable t) { + setResultError(t); + finish(); + } + }); + } + + /** + * 裁剪结果返回 + * @param uri + * @param resultAspectRatio + * @param offsetX + * @param offsetY + * @param imageWidth + * @param imageHeight + */ + protected void setResultUri(Uri uri, float resultAspectRatio, int offsetX, int offsetY, int imageWidth, int imageHeight) { + setResult(RESULT_OK, new Intent() + .putExtra(MyUCrop.EXTRA_OUTPUT_URI, uri) + .putExtra(MyUCrop.EXTRA_OUTPUT_CROP_ASPECT_RATIO, resultAspectRatio) + .putExtra(MyUCrop.EXTRA_OUTPUT_IMAGE_WIDTH, imageWidth) + .putExtra(MyUCrop.EXTRA_OUTPUT_IMAGE_HEIGHT, imageHeight) + .putExtra(MyUCrop.EXTRA_OUTPUT_OFFSET_X, offsetX) + .putExtra(MyUCrop.EXTRA_OUTPUT_OFFSET_Y, offsetY) + ); + } + + protected void setResultError(Throwable throwable) { + setResult(MyUCrop.RESULT_ERROR, new Intent().putExtra(MyUCrop.EXTRA_ERROR, throwable)); + } +} diff --git a/uCropTest/app/src/main/java/com/yongle/ucroptest/MyUCrop.java b/uCropTest/app/src/main/java/com/yongle/ucroptest/MyUCrop.java new file mode 100644 index 0000000..70dfd1c --- /dev/null +++ b/uCropTest/app/src/main/java/com/yongle/ucroptest/MyUCrop.java @@ -0,0 +1,559 @@ +package com.yongle.ucroptest; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Fragment; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcelable; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.FloatRange; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.yalantis.ucrop.BuildConfig; +import com.yalantis.ucrop.UCropActivity; +import com.yalantis.ucrop.model.AspectRatio; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; + +/** + * Created by Oleksii Shliama (https://github.com/shliama). + *

+ * Builder class to ease Intent setup. + */ +public class MyUCrop { + + public static final int REQUEST_CROP = 69; + public static final int RESULT_ERROR = 96; + + private static final String EXTRA_PREFIX = BuildConfig.APPLICATION_ID; + + public static final String EXTRA_INPUT_URI = EXTRA_PREFIX + ".InputUri"; + public static final String EXTRA_OUTPUT_URI = EXTRA_PREFIX + ".OutputUri"; + public static final String EXTRA_OUTPUT_CROP_ASPECT_RATIO = EXTRA_PREFIX + ".CropAspectRatio"; + public static final String EXTRA_OUTPUT_IMAGE_WIDTH = EXTRA_PREFIX + ".ImageWidth"; + public static final String EXTRA_OUTPUT_IMAGE_HEIGHT = EXTRA_PREFIX + ".ImageHeight"; + public static final String EXTRA_OUTPUT_OFFSET_X = EXTRA_PREFIX + ".OffsetX"; + public static final String EXTRA_OUTPUT_OFFSET_Y = EXTRA_PREFIX + ".OffsetY"; + public static final String EXTRA_ERROR = EXTRA_PREFIX + ".Error"; + + public static final String EXTRA_ASPECT_RATIO_X = EXTRA_PREFIX + ".AspectRatioX"; + public static final String EXTRA_ASPECT_RATIO_Y = EXTRA_PREFIX + ".AspectRatioY"; + + public static final String EXTRA_MAX_SIZE_X = EXTRA_PREFIX + ".MaxSizeX"; + public static final String EXTRA_MAX_SIZE_Y = EXTRA_PREFIX + ".MaxSizeY"; + + private Intent mCropIntent; + private Bundle mCropOptionsBundle; + + /** + * This method creates new Intent builder and sets both source and destination image URIs. + * + * @param source Uri for image to crop + * @param destination Uri for saving the cropped image + */ + public static MyUCrop of(@NonNull Uri source, @NonNull Uri destination) { + return new MyUCrop(source, destination); + } + + private MyUCrop(@NonNull Uri source, @NonNull Uri destination) { + mCropIntent = new Intent(); + mCropOptionsBundle = new Bundle(); + mCropOptionsBundle.putParcelable(EXTRA_INPUT_URI, source); + mCropOptionsBundle.putParcelable(EXTRA_OUTPUT_URI, destination); + } + + /** + * Set an aspect ratio for crop bounds. + * User won't see the menu with other ratios options. + * + * @param x aspect ratio X + * @param y aspect ratio Y + */ + public MyUCrop withAspectRatio(float x, float y) { + mCropOptionsBundle.putFloat(EXTRA_ASPECT_RATIO_X, x); + mCropOptionsBundle.putFloat(EXTRA_ASPECT_RATIO_Y, y); + return this; + } + + /** + * Set an aspect ratio for crop bounds that is evaluated from source image width and height. + * User won't see the menu with other ratios options. + */ + public MyUCrop useSourceImageAspectRatio() { + mCropOptionsBundle.putFloat(EXTRA_ASPECT_RATIO_X, 0); + mCropOptionsBundle.putFloat(EXTRA_ASPECT_RATIO_Y, 0); + return this; + } + + /** + * Set maximum size for result cropped image. + * + * @param width max cropped image width + * @param height max cropped image height + */ + public MyUCrop withMaxResultSize(@IntRange(from = 100) int width, @IntRange(from = 100) int height) { + mCropOptionsBundle.putInt(EXTRA_MAX_SIZE_X, width); + mCropOptionsBundle.putInt(EXTRA_MAX_SIZE_Y, height); + return this; + } + + public MyUCrop withOptions(@NonNull Options options) { + mCropOptionsBundle.putAll(options.getOptionBundle()); + return this; + } + + /** + * Send the crop Intent from an Activity + * + * @param activity Activity to receive result + */ + public void start(@NonNull Activity activity) { + start(activity, REQUEST_CROP); + } + + /** + * Send the crop Intent from an Activity with a custom request code + * + * @param activity Activity to receive result + * @param requestCode requestCode for result + */ + public void start(@NonNull Activity activity, int requestCode) { + activity.startActivityForResult(getIntent(activity), requestCode); + } + /** + *进入自定义的Activity + * + * @param activity Activity to receive result + * @param cls 要进入的裁剪Activity + */ + public void startMyActivity(@NonNull Activity activity,@NonNull Class cls) { + activity.startActivityForResult(getActivityIntent(activity,cls), REQUEST_CROP); + } + /** + * Send the crop Intent from a Fragment + * + * @param fragment Fragment to receive result + */ + public void start(@NonNull Context context, @NonNull Fragment fragment) { + start(context, fragment, REQUEST_CROP); + } + + /** + * Send the crop Intent from a support library Fragment + * + * @param fragment Fragment to receive result + */ + public void start(@NonNull Context context, @NonNull android.support.v4.app.Fragment fragment) { + start(context, fragment, REQUEST_CROP); + } + + /** + * Send the crop Intent with a custom request code + * + * @param fragment Fragment to receive result + * @param requestCode requestCode for result + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void start(@NonNull Context context, @NonNull Fragment fragment, int requestCode) { + fragment.startActivityForResult(getIntent(context), requestCode); + } + + /** + * Send the crop Intent with a custom request code + * + * @param fragment Fragment to receive result + * @param requestCode requestCode for result + */ + public void start(@NonNull Context context, @NonNull android.support.v4.app.Fragment fragment, int requestCode) { + fragment.startActivityForResult(getIntent(context), requestCode); + } + + /** + * Get Intent to start {@link UCropActivity} + * + * @return Intent for {@link UCropActivity} + */ + public Intent getIntent(@NonNull Context context) { + mCropIntent.setClass(context, UCropActivity.class); + mCropIntent.putExtras(mCropOptionsBundle); + return mCropIntent; + } + + /** + * + * @param context 接受result 的Activity + * @param cls 要实现裁剪的Activity + * @return + */ + public Intent getActivityIntent(@NonNull Context context,@NonNull Class cls) { + mCropIntent.setClass(context, cls); + mCropIntent.putExtras(mCropOptionsBundle); + return mCropIntent; + } + /** + * Retrieve cropped image Uri from the result Intent + * + * @param intent crop result intent + */ + @Nullable + public static Uri getOutput(@NonNull Intent intent) { + return intent.getParcelableExtra(EXTRA_OUTPUT_URI); + } + + /** + * Retrieve the width of the cropped image + * + * @param intent crop result intent + */ + public static int getOutputImageWidth(@NonNull Intent intent) { + return intent.getIntExtra(EXTRA_OUTPUT_IMAGE_WIDTH, -1); + } + + /** + * Retrieve the height of the cropped image + * + * @param intent crop result intent + */ + public static int getOutputImageHeight(@NonNull Intent intent) { + return intent.getIntExtra(EXTRA_OUTPUT_IMAGE_HEIGHT, -1); + } + + /** + * Retrieve cropped image aspect ratio from the result Intent + * + * @param intent crop result intent + * @return aspect ratio as a floating point value (x:y) - so it will be 1 for 1:1 or 4/3 for 4:3 + */ + public static float getOutputCropAspectRatio(@NonNull Intent intent) { + return intent.getParcelableExtra(EXTRA_OUTPUT_CROP_ASPECT_RATIO); + } + + /** + * Method retrieves error from the result intent. + * + * @param result crop result Intent + * @return Throwable that could happen while image processing + */ + @Nullable + public static Throwable getError(@NonNull Intent result) { + return (Throwable) result.getSerializableExtra(EXTRA_ERROR); + } + + + /** + * Class that helps to setup advanced configs that are not commonly used. + * Use it with method {@link #withOptions(Options)} + */ + public static class Options { + + public static final String EXTRA_COMPRESSION_FORMAT_NAME = EXTRA_PREFIX + ".CompressionFormatName"; + public static final String EXTRA_COMPRESSION_QUALITY = EXTRA_PREFIX + ".CompressionQuality"; + + public static final String EXTRA_ALLOWED_GESTURES = EXTRA_PREFIX + ".AllowedGestures"; + + public static final String EXTRA_MAX_BITMAP_SIZE = EXTRA_PREFIX + ".MaxBitmapSize"; + public static final String EXTRA_MAX_SCALE_MULTIPLIER = EXTRA_PREFIX + ".MaxScaleMultiplier"; + public static final String EXTRA_IMAGE_TO_CROP_BOUNDS_ANIM_DURATION = EXTRA_PREFIX + ".ImageToCropBoundsAnimDuration"; + + public static final String EXTRA_DIMMED_LAYER_COLOR = EXTRA_PREFIX + ".DimmedLayerColor"; + public static final String EXTRA_CIRCLE_DIMMED_LAYER = EXTRA_PREFIX + ".CircleDimmedLayer"; + + public static final String EXTRA_SHOW_CROP_FRAME = EXTRA_PREFIX + ".ShowCropFrame"; + public static final String EXTRA_CROP_FRAME_COLOR = EXTRA_PREFIX + ".CropFrameColor"; + public static final String EXTRA_CROP_FRAME_STROKE_WIDTH = EXTRA_PREFIX + ".CropFrameStrokeWidth"; + + public static final String EXTRA_SHOW_CROP_GRID = EXTRA_PREFIX + ".ShowCropGrid"; + public static final String EXTRA_CROP_GRID_ROW_COUNT = EXTRA_PREFIX + ".CropGridRowCount"; + public static final String EXTRA_CROP_GRID_COLUMN_COUNT = EXTRA_PREFIX + ".CropGridColumnCount"; + public static final String EXTRA_CROP_GRID_COLOR = EXTRA_PREFIX + ".CropGridColor"; + public static final String EXTRA_CROP_GRID_STROKE_WIDTH = EXTRA_PREFIX + ".CropGridStrokeWidth"; + + public static final String EXTRA_TOOL_BAR_COLOR = EXTRA_PREFIX + ".ToolbarColor"; + public static final String EXTRA_STATUS_BAR_COLOR = EXTRA_PREFIX + ".StatusBarColor"; + public static final String EXTRA_UCROP_COLOR_WIDGET_ACTIVE = EXTRA_PREFIX + ".UcropColorWidgetActive"; + + public static final String EXTRA_UCROP_WIDGET_COLOR_TOOLBAR = EXTRA_PREFIX + ".UcropToolbarWidgetColor"; + public static final String EXTRA_UCROP_TITLE_TEXT_TOOLBAR = EXTRA_PREFIX + ".UcropToolbarTitleText"; + public static final String EXTRA_UCROP_WIDGET_CANCEL_DRAWABLE = EXTRA_PREFIX + ".UcropToolbarCancelDrawable"; + public static final String EXTRA_UCROP_WIDGET_CROP_DRAWABLE = EXTRA_PREFIX + ".UcropToolbarCropDrawable"; + + public static final String EXTRA_UCROP_LOGO_COLOR = EXTRA_PREFIX + ".UcropLogoColor"; + + public static final String EXTRA_HIDE_BOTTOM_CONTROLS = EXTRA_PREFIX + ".HideBottomControls"; + public static final String EXTRA_FREE_STYLE_CROP = EXTRA_PREFIX + ".FreeStyleCrop"; + + public static final String EXTRA_ASPECT_RATIO_SELECTED_BY_DEFAULT = EXTRA_PREFIX + ".AspectRatioSelectedByDefault"; + public static final String EXTRA_ASPECT_RATIO_OPTIONS = EXTRA_PREFIX + ".AspectRatioOptions"; + + public static final String EXTRA_UCROP_ROOT_VIEW_BACKGROUND_COLOR = EXTRA_PREFIX + ".UcropRootViewBackgroundColor"; + + + private final Bundle mOptionBundle; + + public Options() { + mOptionBundle = new Bundle(); + } + + @NonNull + public Bundle getOptionBundle() { + return mOptionBundle; + } + + /** + * Set one of {@link Bitmap.CompressFormat} that will be used to save resulting Bitmap. + */ + public void setCompressionFormat(@NonNull Bitmap.CompressFormat format) { + mOptionBundle.putString(EXTRA_COMPRESSION_FORMAT_NAME, format.name()); + } + + /** + * Set compression quality [0-100] that will be used to save resulting Bitmap. + */ + public void setCompressionQuality(@IntRange(from = 0) int compressQuality) { + mOptionBundle.putInt(EXTRA_COMPRESSION_QUALITY, compressQuality); + } + + /** + * Choose what set of gestures will be enabled on each tab - if any. + */ + public void setAllowedGestures(@UCropActivity.GestureTypes int tabScale, + @UCropActivity.GestureTypes int tabRotate, + @UCropActivity.GestureTypes int tabAspectRatio) { + mOptionBundle.putIntArray(EXTRA_ALLOWED_GESTURES, new int[]{tabScale, tabRotate, tabAspectRatio}); + } + + /** + * This method sets multiplier that is used to calculate max image scale from min image scale. + * + * @param maxScaleMultiplier - (minScale * maxScaleMultiplier) = maxScale + */ + public void setMaxScaleMultiplier(@FloatRange(from = 1.0, fromInclusive = false) float maxScaleMultiplier) { + mOptionBundle.putFloat(EXTRA_MAX_SCALE_MULTIPLIER, maxScaleMultiplier); + } + + /** + * This method sets animation duration for image to wrap the crop bounds + * + * @param durationMillis - duration in milliseconds + */ + public void setImageToCropBoundsAnimDuration(@IntRange(from = 100) int durationMillis) { + mOptionBundle.putInt(EXTRA_IMAGE_TO_CROP_BOUNDS_ANIM_DURATION, durationMillis); + } + + /** + * Setter for max size for both width and height of bitmap that will be decoded from an input Uri and used in the view. + * + * @param maxBitmapSize - size in pixels + */ + public void setMaxBitmapSize(@IntRange(from = 100) int maxBitmapSize) { + mOptionBundle.putInt(EXTRA_MAX_BITMAP_SIZE, maxBitmapSize); + } + + /** + * @param color - desired color of dimmed area around the crop bounds + */ + public void setDimmedLayerColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_DIMMED_LAYER_COLOR, color); + } + + /** + * @param isCircle - set it to true if you want dimmed layer to have an circle inside + */ + public void setCircleDimmedLayer(boolean isCircle) { + mOptionBundle.putBoolean(EXTRA_CIRCLE_DIMMED_LAYER, isCircle); + } + + /** + * @param show - set to true if you want to see a crop frame rectangle on top of an image + */ + public void setShowCropFrame(boolean show) { + mOptionBundle.putBoolean(EXTRA_SHOW_CROP_FRAME, show); + } + + /** + * @param color - desired color of crop frame + */ + public void setCropFrameColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_CROP_FRAME_COLOR, color); + } + + /** + * @param width - desired width of crop frame line in pixels + */ + public void setCropFrameStrokeWidth(@IntRange(from = 0) int width) { + mOptionBundle.putInt(EXTRA_CROP_FRAME_STROKE_WIDTH, width); + } + + /** + * @param show - set to true if you want to see a crop grid/guidelines on top of an image + */ + public void setShowCropGrid(boolean show) { + mOptionBundle.putBoolean(EXTRA_SHOW_CROP_GRID, show); + } + + /** + * @param count - crop grid rows count. + */ + public void setCropGridRowCount(@IntRange(from = 0) int count) { + mOptionBundle.putInt(EXTRA_CROP_GRID_ROW_COUNT, count); + } + + /** + * @param count - crop grid columns count. + */ + public void setCropGridColumnCount(@IntRange(from = 0) int count) { + mOptionBundle.putInt(EXTRA_CROP_GRID_COLUMN_COUNT, count); + } + + /** + * @param color - desired color of crop grid/guidelines + */ + public void setCropGridColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_CROP_GRID_COLOR, color); + } + + /** + * @param width - desired width of crop grid lines in pixels + */ + public void setCropGridStrokeWidth(@IntRange(from = 0) int width) { + mOptionBundle.putInt(EXTRA_CROP_GRID_STROKE_WIDTH, width); + } + + /** + * @param color - desired resolved color of the toolbar + */ + public void setToolbarColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_TOOL_BAR_COLOR, color); + } + + /** + * @param color - desired resolved color of the statusbar + */ + public void setStatusBarColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_STATUS_BAR_COLOR, color); + } + + /** + * @param color - desired resolved color of the active and selected widget (default is orange) and progress wheel middle line + */ + public void setActiveWidgetColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_UCROP_COLOR_WIDGET_ACTIVE, color); + } + + /** + * @param color - desired resolved color of Toolbar text and buttons (default is darker orange) + */ + public void setToolbarWidgetColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_UCROP_WIDGET_COLOR_TOOLBAR, color); + } + + /** + * @param text - desired text for Toolbar title + */ + public void setToolbarTitle(@Nullable String text) { + mOptionBundle.putString(EXTRA_UCROP_TITLE_TEXT_TOOLBAR, text); + } + + /** + * @param drawable - desired drawable for the Toolbar left cancel icon + */ + public void setToolbarCancelDrawable(@DrawableRes int drawable) { + mOptionBundle.putInt(EXTRA_UCROP_WIDGET_CANCEL_DRAWABLE, drawable); + } + + /** + * @param drawable - desired drawable for the Toolbar right crop icon + */ + public void setToolbarCropDrawable(@DrawableRes int drawable) { + mOptionBundle.putInt(EXTRA_UCROP_WIDGET_CROP_DRAWABLE, drawable); + } + + /** + * @param color - desired resolved color of logo fill (default is darker grey) + */ + public void setLogoColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_UCROP_LOGO_COLOR, color); + } + + /** + * @param hide - set to true to hide the bottom controls (shown by default) + */ + public void setHideBottomControls(boolean hide) { + mOptionBundle.putBoolean(EXTRA_HIDE_BOTTOM_CONTROLS, hide); + } + + /** + * @param enabled - set to true to let user resize crop bounds (disabled by default) + */ + public void setFreeStyleCropEnabled(boolean enabled) { + mOptionBundle.putBoolean(EXTRA_FREE_STYLE_CROP, enabled); + } + + /** + * Pass an ordered list of desired aspect ratios that should be available for a user. + * + * @param selectedByDefault - index of aspect ratio option that is selected by default (starts with 0). + * @param aspectRatio - list of aspect ratio options that are available to user + */ + public void setAspectRatioOptions(int selectedByDefault, AspectRatio... aspectRatio) { + if (selectedByDefault > aspectRatio.length) { + throw new IllegalArgumentException(String.format(Locale.US, + "Index [selectedByDefault = %d] cannot be higher than aspect ratio options count [count = %d].", + selectedByDefault, aspectRatio.length)); + } + mOptionBundle.putInt(EXTRA_ASPECT_RATIO_SELECTED_BY_DEFAULT, selectedByDefault); + mOptionBundle.putParcelableArrayList(EXTRA_ASPECT_RATIO_OPTIONS, new ArrayList(Arrays.asList(aspectRatio))); + } + + /** + * @param color - desired background color that should be applied to the root view + */ + public void setRootViewBackgroundColor(@ColorInt int color) { + mOptionBundle.putInt(EXTRA_UCROP_ROOT_VIEW_BACKGROUND_COLOR, color); + } + + /** + * Set an aspect ratio for crop bounds. + * User won't see the menu with other ratios options. + * + * @param x aspect ratio X + * @param y aspect ratio Y + */ + public void withAspectRatio(float x, float y) { + mOptionBundle.putFloat(EXTRA_ASPECT_RATIO_X, x); + mOptionBundle.putFloat(EXTRA_ASPECT_RATIO_Y, y); + } + + /** + * Set an aspect ratio for crop bounds that is evaluated from source image width and height. + * User won't see the menu with other ratios options. + */ + public void useSourceImageAspectRatio() { + mOptionBundle.putFloat(EXTRA_ASPECT_RATIO_X, 0); + mOptionBundle.putFloat(EXTRA_ASPECT_RATIO_Y, 0); + } + + /** + * Set maximum size for result cropped image. + * + * @param width max cropped image width + * @param height max cropped image height + */ + public void withMaxResultSize(@IntRange(from = 100) int width, @IntRange(from = 100) int height) { + mOptionBundle.putInt(EXTRA_MAX_SIZE_X, width); + mOptionBundle.putInt(EXTRA_MAX_SIZE_Y, height); + } + + } + +} diff --git a/uCropTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/uCropTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/uCropTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/uCropTest/app/src/main/res/drawable/ic_launcher_background.xml b/uCropTest/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/uCropTest/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/uCropTest/app/src/main/res/layout/activity_main.xml b/uCropTest/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a77f162 --- /dev/null +++ b/uCropTest/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,41 @@ + + + + + + + +