diff --git a/CHANGELOG.md b/CHANGELOG.md
index 341f0a74..0ed6834b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Fixed
- The translation of `Camera` and `Gallery` does not exist in some languages.
+## [unreleased 4.2.2]
+### Added
+- Added a helper text on top of crop overlay which moves along with it. [#381](https://github.com/CanHub/Android-Image-Cropper/issues/381)
+### Fixed
+- Crash on click of Sample of CustomActivity option over sample App, added the missing activity declaration on Manifest.xml
+
## [4.2.1] - 04/04/2022
### Added
- Added support for optionally displaying an intent chooser when selecting image source. [#325](https://github.com/CanHub/Android-Image-Cropper/issues/325)
diff --git a/cropper/src/main/java/com/canhub/cropper/CropImageContractOptions.kt b/cropper/src/main/java/com/canhub/cropper/CropImageContractOptions.kt
index bdddaa7e..babfa250 100644
--- a/cropper/src/main/java/com/canhub/cropper/CropImageContractOptions.kt
+++ b/cropper/src/main/java/com/canhub/cropper/CropImageContractOptions.kt
@@ -127,6 +127,11 @@ data class CropImageContractOptions @JvmOverloads constructor(
return this
}
+ fun setShowCropLabel(showCropLabel: Boolean): CropImageContractOptions {
+ cropImageOptions.showCropLabel = showCropLabel
+ return this
+ }
+
/**
* if auto-zoom functionality is enabled.
* default: true.
diff --git a/cropper/src/main/java/com/canhub/cropper/CropImageOptions.kt b/cropper/src/main/java/com/canhub/cropper/CropImageOptions.kt
index 136c9ccc..081142bc 100644
--- a/cropper/src/main/java/com/canhub/cropper/CropImageOptions.kt
+++ b/cropper/src/main/java/com/canhub/cropper/CropImageOptions.kt
@@ -83,6 +83,12 @@ open class CropImageOptions : Parcelable {
@JvmField
var showCropOverlay: Boolean
+ /**
+ * If enabled, show a text label on top of crop overlay UI, which gets moved along with the cropper
+ */
+ @JvmField
+ var showCropLabel: Boolean
+
/**
* if to show progress bar when image async loading/cropping is in progress.
* default: true, disable to provide custom progress bar UI.
@@ -311,6 +317,18 @@ open class CropImageOptions : Parcelable {
@JvmField
var intentChooserPriorityList: List?
+ /** The initial text size of cropper label **/
+ @JvmField
+ var cropperLabelTextSize: Float
+
+ /** The default cropper label text color **/
+ @JvmField
+ var cropperLabelTextColor: Int
+
+ /** The default cropper label text **/
+ @JvmField
+ var cropperLabelText: String? = ""
+
/** Init options with defaults. */
constructor() {
val dm = Resources.getSystem().displayMetrics
@@ -373,6 +391,9 @@ open class CropImageOptions : Parcelable {
showIntentChooser = false
intentChooserTitle = null
intentChooserPriorityList = listOf()
+ cropperLabelTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20f, dm)
+ cropperLabelTextColor = Color.WHITE
+ showCropLabel = false
}
/** Create object from parcel. */
@@ -435,6 +456,10 @@ open class CropImageOptions : Parcelable {
showIntentChooser = parcel.readByte().toInt() != 0
intentChooserTitle = parcel.readString()
intentChooserPriorityList = parcel.createStringArrayList()
+ cropperLabelTextSize = parcel.readFloat()
+ cropperLabelTextColor = parcel.readInt()
+ cropperLabelText = parcel.readString()!!
+ showCropLabel = parcel.readByte().toInt() != 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
@@ -496,6 +521,10 @@ open class CropImageOptions : Parcelable {
dest.writeByte((if (showIntentChooser) 1 else 0).toByte())
dest.writeString(intentChooserTitle)
dest.writeStringList(intentChooserPriorityList)
+ dest.writeFloat(cropperLabelTextSize)
+ dest.writeInt(cropperLabelTextColor)
+ dest.writeString(cropperLabelText)
+ dest.writeByte((if (showCropLabel) 1 else 0).toByte())
}
override fun describeContents(): Int {
diff --git a/cropper/src/main/java/com/canhub/cropper/CropImageView.kt b/cropper/src/main/java/com/canhub/cropper/CropImageView.kt
index 7033b52c..7cf6c707 100644
--- a/cropper/src/main/java/com/canhub/cropper/CropImageView.kt
+++ b/cropper/src/main/java/com/canhub/cropper/CropImageView.kt
@@ -5,6 +5,7 @@ import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
+import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
@@ -105,6 +106,29 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
*/
private var mShowCropOverlay = true
+ /** If true, shows a helper text label over crop overlay UI
+ * default: false
+ */
+ private var mShowCropLabel = false
+
+ /**
+ * Helper text label over crop overlay UI
+ * default: empty string
+ */
+ private var mCropTextLabel = ""
+
+ /**
+ * Text size for text label over crop overlay UI
+ * default: 20sp
+ */
+ private var mCropLabelTextSize = 20f
+
+ /**
+ * Text color for text label over crop overlay UI
+ * default: White
+ */
+ private var mCropLabelTextColor = Color.WHITE
+
/**
* if to show progress bar when image async loading/cropping is in progress.
* default: true, disable to provide custom progress bar UI.
@@ -403,6 +427,35 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
setCropOverlayVisibility()
}
}
+ /**
+ * If enabled, show a text label on top of crop overlay UI, which gets moved along with the cropper
+ */
+ var isShowCropLabel: Boolean
+ get() = mShowCropLabel
+ set(showCropLabel) {
+ if (mShowCropLabel != showCropLabel) {
+ mShowCropLabel = showCropLabel
+ mCropOverlayView?.setCropperTextLabelVisibility(mShowCropLabel)
+ }
+ }
+ var cropLabelText: String
+ get() = mCropTextLabel
+ set(cropLabelText) {
+ mCropTextLabel = cropLabelText
+ mCropOverlayView?.setCropLabelText(cropLabelText)
+ }
+ var cropLabelTextSize: Float
+ get() = mCropLabelTextSize
+ set(textSize) {
+ mCropLabelTextSize = cropLabelTextSize
+ mCropOverlayView?.setCropLabelTextSize(textSize)
+ }
+ var cropLabelTextColor: Int
+ get() = mCropLabelTextColor
+ set(cropLabelTextColor) {
+ mCropLabelTextColor = cropLabelTextColor
+ mCropOverlayView?.setCropLabelTextColor(cropLabelTextColor)
+ }
/** Returns the integer of the imageResource */
/**
* Sets a Drawable as the content of the CropImageView.
@@ -1037,7 +1090,7 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
bundle.putInt("CROP_MAX_ZOOM", mMaxZoom)
bundle.putBoolean("CROP_FLIP_HORIZONTALLY", mFlipHorizontally)
bundle.putBoolean("CROP_FLIP_VERTICALLY", mFlipVertically)
-
+ bundle.putBoolean("SHOW_CROP_LABEL", mShowCropLabel)
return bundle
}
@@ -1088,6 +1141,8 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
mMaxZoom = state.getInt("CROP_MAX_ZOOM")
mFlipHorizontally = state.getBoolean("CROP_FLIP_HORIZONTALLY")
mFlipVertically = state.getBoolean("CROP_FLIP_VERTICALLY")
+ mShowCropLabel = state.getBoolean("SHOW_CROP_LABEL")
+ mCropOverlayView.setCropperTextLabelVisibility(mShowCropLabel)
}
super.onRestoreInstanceState(state.getParcelable("instanceState"))
} else {
@@ -1882,6 +1937,21 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
R.styleable.CropImageView_cropFlipHorizontally,
options.flipVertically
)
+ options.cropperLabelTextSize = ta.getDimension(
+ R.styleable.CropImageView_cropperLabelTextSize,
+ options.cropperLabelTextSize
+ )
+ options.cropperLabelTextColor = ta.getInteger(
+ R.styleable.CropImageView_cropperLabelTextColor,
+ options.cropperLabelTextColor
+ )
+ options.cropperLabelText = ta.getString(
+ R.styleable.CropImageView_cropperLabelText
+ )
+ options.showCropLabel = ta.getBoolean(
+ R.styleable.CropImageView_cropShowLabel,
+ options.showCropLabel
+ )
isSaveBitmapToInstanceState = ta.getBoolean(
R.styleable.CropImageView_cropSaveBitmapToInstanceState,
isSaveBitmapToInstanceState
@@ -1902,6 +1972,8 @@ class CropImageView @JvmOverloads constructor(context: Context, attrs: Attribute
mScaleType = options.scaleType
mAutoZoomEnabled = options.autoZoomEnabled
mMaxZoom = options.maxZoom
+ mCropLabelTextSize = options.cropperLabelTextSize
+ mShowCropLabel = options.showCropLabel
mShowCropOverlay = options.showCropOverlay
mShowProgressBar = options.showProgressBar
mFlipHorizontally = options.flipHorizontally
diff --git a/cropper/src/main/java/com/canhub/cropper/CropOverlayView.kt b/cropper/src/main/java/com/canhub/cropper/CropOverlayView.kt
index 9579ec7b..237191b8 100644
--- a/cropper/src/main/java/com/canhub/cropper/CropOverlayView.kt
+++ b/cropper/src/main/java/com/canhub/cropper/CropOverlayView.kt
@@ -34,6 +34,16 @@ class CropOverlayView
companion object {
+ /**
+ * Creates the paint object for drawing text label over crop overlay */
+ private fun getTextPaint(options: CropImageOptions): Paint =
+ Paint().apply {
+ strokeWidth = 1f
+ textSize = options.cropperLabelTextSize
+ style = Paint.Style.FILL
+ textAlign = Paint.Align.CENTER
+ this.color = options.cropperLabelTextColor
+ }
/** Creates the Paint object for drawing. */
private fun getNewPaint(color: Int): Paint =
Paint().apply {
@@ -92,6 +102,8 @@ class CropOverlayView
/** The Paint used to darken the surrounding areas outside the crop area. */
private var mBackgroundPaint: Paint? = null
+ private var textLabelPaint: Paint? = null
+
/** Used for oval crop window shape or non-straight rotation drawing. */
private val mPath = Path()
@@ -164,6 +176,14 @@ class CropOverlayView
var cornerShape: CropImageView.CropCornerShape? = null
private set
+ /** To show the text label over crop overlay **/
+ private var isCropLabelEnabled: Boolean = false
+ /** Text to show over text label over crop overlay */
+ private var cropLabelText: String = ""
+ /** Text color to apply over text label over crop overlay */
+ private var cropLabelTextSize: Float = 20f
+ /** Text color to apply over text label over crop overlay */
+ private var cropLabelTextColor = Color.WHITE
/** the initial crop window rectangle to set */
private val mInitialCropWindowRect = Rect()
@@ -253,7 +273,37 @@ class CropOverlayView
invalidate()
}
}
+ /**
+ * Sets the cropper label if it is enabled
+ */
+ fun setCropperTextLabelVisibility(isEnabled: Boolean) {
+ this.isCropLabelEnabled = isEnabled
+ invalidate()
+ }
+
+ /**
+ * Sets the copy text for cropper text
+ */
+ fun setCropLabelText(textLabel: String?) {
+ textLabel?.let {
+ this.cropLabelText = it
+ }
+ }
+ /**
+ * Sets the text size for cropper text
+ */
+ fun setCropLabelTextSize(textSize: Float) {
+ this.cropLabelTextSize = textSize
+ invalidate()
+ }
+ /**
+ * Sets the text color for cropper text
+ */
+ fun setCropLabelTextColor(textColor: Int) {
+ this.cropLabelTextColor = textColor
+ invalidate()
+ }
/**
* Sets the guidelines for the CropOverlayView to be either on, off, or to show when resizing the
* application.
@@ -410,6 +460,10 @@ class CropOverlayView
fun setInitialAttributeValues(options: CropImageOptions) {
mOptions = options
mCropWindowHandler.setInitialAttributeValues(options)
+ setCropLabelTextColor(options.cropperLabelTextColor)
+ setCropLabelTextSize(options.cropperLabelTextSize)
+ setCropLabelText(options.cropperLabelText)
+ setCropperTextLabelVisibility(options.showCropLabel)
setCropCornerRadius(options.cropCornerRadius)
setCropCornerShape(options.cornerShape)
setCropShape(options.cropShape)
@@ -430,6 +484,7 @@ class CropOverlayView
getNewPaintOrNull(options.borderCornerThickness, options.borderCornerColor)
mGuidelinePaint = getNewPaintOrNull(options.guidelinesThickness, options.guidelinesColor)
mBackgroundPaint = getNewPaint(options.backgroundColor)
+ textLabelPaint = getTextPaint(options)
}
/**
@@ -570,10 +625,26 @@ class CropOverlayView
}
// To retain the changes in Paint object when the App goes background this is required
mBorderCornerPaint = getNewPaintOrNull(mOptions?.borderCornerThickness ?: 0.0f, mOptions?.borderCornerColor ?: Color.WHITE)
+ drawCropLabelText(canvas)
drawBorders(canvas)
drawCorners(canvas)
}
+ /** Draws a text label (which can acts an helper text) on top of crop overlay **/
+ private fun drawCropLabelText(canvas: Canvas) {
+ if (isCropLabelEnabled) {
+ val rect = mCropWindowHandler.getRect()
+ var xCoordinate = (rect.left + rect.right) / 2
+ var yCoordinate = rect.top - 50
+ textLabelPaint?.apply {
+ textSize = cropLabelTextSize
+ color = cropLabelTextColor
+ }
+ canvas.drawText(cropLabelText, xCoordinate, yCoordinate, textLabelPaint!!)
+ canvas.save()
+ }
+ }
+
/** Draw shadow background over the image not including the crop area. */
private fun drawBackground(canvas: Canvas) {
val rect = mCropWindowHandler.getRect()
diff --git a/cropper/src/main/res/values/attrs.xml b/cropper/src/main/res/values/attrs.xml
index 82318aa8..0db22609 100644
--- a/cropper/src/main/res/values/attrs.xml
+++ b/cropper/src/main/res/values/attrs.xml
@@ -54,6 +54,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 6247b07c..55cb9a21 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -20,7 +20,7 @@
diff --git a/sample/src/main/java/com/canhub/cropper/sample/SampleUsingImageView.kt b/sample/src/main/java/com/canhub/cropper/sample/SampleUsingImageView.kt
index a214897a..b563aade 100644
--- a/sample/src/main/java/com/canhub/cropper/sample/SampleUsingImageView.kt
+++ b/sample/src/main/java/com/canhub/cropper/sample/SampleUsingImageView.kt
@@ -106,6 +106,7 @@ internal class SampleUsingImageView :
maxZoom = options.maxZoomLvl
isFlippedHorizontally = options.flipHorizontal
isFlippedVertically = options.flipVertically
+ isShowCropLabel = options.showCropLabel
}
if (options.scaleType == CropImageView.ScaleType.CENTER_INSIDE)
@@ -191,6 +192,7 @@ internal class SampleUsingImageView :
showCropOverlay = true,
showProgressBar = true,
flipHorizontal = false,
- flipVertically = false
+ flipVertically = false,
+ showCropLabel = false
)
}
diff --git a/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsBottomSheet.kt b/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsBottomSheet.kt
index 08e1cb48..ce45b5e1 100644
--- a/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsBottomSheet.kt
+++ b/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsBottomSheet.kt
@@ -114,6 +114,7 @@ internal class SampleOptionsBottomSheet : BottomSheetDialogFragment() {
binding.progressBar.toggle.isChecked = options.showProgressBar
binding.flipHorizontal.toggle.isChecked = options.flipHorizontal
binding.flipVertical.toggle.isChecked = options.flipVertically
+ binding.cropLabelText.toggle.isChecked = options.showCropLabel
}
override fun onDismiss(dialog: DialogInterface) {
@@ -134,7 +135,8 @@ internal class SampleOptionsBottomSheet : BottomSheetDialogFragment() {
showCropOverlay = true,
showProgressBar = true,
flipHorizontal = false,
- flipVertically = false
+ flipVertically = false,
+ showCropLabel = true
)
private fun bindingActions() {
@@ -253,5 +255,8 @@ internal class SampleOptionsBottomSheet : BottomSheetDialogFragment() {
binding.progressBar.toggle.setOnCheckedChangeListener { _, isChecked ->
options = options.copy(showProgressBar = isChecked)
}
+ binding.cropLabelText.toggle.setOnCheckedChangeListener { _, isChecked ->
+ options = options.copy(showCropLabel = isChecked)
+ }
}
}
diff --git a/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsEntity.kt b/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsEntity.kt
index cdc538a7..0e7597df 100644
--- a/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsEntity.kt
+++ b/sample/src/main/java/com/canhub/cropper/sample/options_dialog/SampleOptionsEntity.kt
@@ -20,4 +20,5 @@ internal data class SampleOptionsEntity(
val showProgressBar: Boolean,
val flipHorizontal: Boolean,
val flipVertically: Boolean,
+ val showCropLabel: Boolean
) : Parcelable
diff --git a/sample/src/main/res/layout/fragment_crop_image_view.xml b/sample/src/main/res/layout/fragment_crop_image_view.xml
index a2edb728..bea13125 100644
--- a/sample/src/main/res/layout/fragment_crop_image_view.xml
+++ b/sample/src/main/res/layout/fragment_crop_image_view.xml
@@ -24,6 +24,10 @@
app:cropCornerRadius="@dimen/default_crop_corner_radius"
app:cropBorderCornerOffset="@dimen/default_crop_corner_offset"
app:cropBorderCornerThickness="@dimen/default_crop_corner_offset"
+ app:cropShowLabel="true"
+ app:cropperLabelText="@string/default_crop_label_text"
+ app:cropperLabelTextSize="@dimen/default_crop_text_size"
+ app:cropBorderCornerColor="@color/cardview_shadow_start_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/sample/src/main/res/layout/fragment_options.xml b/sample/src/main/res/layout/fragment_options.xml
index 06db3d79..0e3c2e08 100644
--- a/sample/src/main/res/layout/fragment_options.xml
+++ b/sample/src/main/res/layout/fragment_options.xml
@@ -95,6 +95,9 @@
+
\ No newline at end of file
diff --git a/sample/src/main/res/layout/switch_crop_text_label.xml b/sample/src/main/res/layout/switch_crop_text_label.xml
new file mode 100644
index 00000000..ca675818
--- /dev/null
+++ b/sample/src/main/res/layout/switch_crop_text_label.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml
index fbc3b9c2..e63ff37a 100644
--- a/sample/src/main/res/values/dimens.xml
+++ b/sample/src/main/res/values/dimens.xml
@@ -39,4 +39,5 @@
8dp
8dp
4dp
+ 20sp
\ No newline at end of file
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
index 497af191..b8bba517 100644
--- a/sample/src/main/res/values/strings.xml
+++ b/sample/src/main/res/values/strings.xml
@@ -91,4 +91,6 @@
This is a sample code using the library. The sample here is for extend CropImageActivity, to be about to custom build your personal view around it. For that we need to use CropImageView.
Click to change rotation: %1$s
Corner Shape
+ Show Crop Label Text
+ Sample crop label