Skip to content

Commit

Permalink
Add option to disable translation of the crop window (#79)
Browse files Browse the repository at this point in the history
* Add option to disable center move of the crop window "centerMoveEnabled"

* Add options to Samples
  • Loading branch information
chriscoomber authored Mar 4, 2021
1 parent 96234d9 commit 245071e
Show file tree
Hide file tree
Showing 17 changed files with 120 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## [unreleased x.x.x] -
### Added
- Vertical-only and horizontal-only cropping modes [#76]((https://github.com/CanHub/Android-Image-Cropper/pull/76))
- Option to disable movement of the crop window by dragging the center [#79](https://github.com/CanHub/Android-Image-Cropper/pull/79)

### Fixed
- Turkish Translations [#72](https://github.com/CanHub/Android-Image-Cropper/pull/72)
Expand Down
9 changes: 9 additions & 0 deletions cropper/src/main/java/com/canhub/cropper/CropImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,15 @@ public ActivityBuilder setMultiTouchEnabled(boolean multiTouchEnabled) {
return this;
}

/**
* if the crop window can be moved by dragging the center.<br>
* default: true
*/
public ActivityBuilder setCenterMoveEnabled(boolean centerMoveEnabled) {
mOptions.centerMoveEnabled = centerMoveEnabled;
return this;
}

/**
* The max zoom allowed during cropping.<br>
* <i>Default: 4</i>
Expand Down
7 changes: 7 additions & 0 deletions cropper/src/main/java/com/canhub/cropper/CropImageOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ open class CropImageOptions : Parcelable {
@JvmField
var multiTouchEnabled: Boolean

/** if the the crop window can be moved by dragging the center; default: true */
@JvmField
var centerMoveEnabled: Boolean

/** The max zoom allowed during cropping. */
@JvmField
var maxZoom: Int
Expand Down Expand Up @@ -259,6 +263,7 @@ open class CropImageOptions : Parcelable {
showProgressBar = true
autoZoomEnabled = true
multiTouchEnabled = false
centerMoveEnabled = true
maxZoom = 4
initialCropWindowPaddingRatio = 0.1f
fixAspectRatio = false
Expand Down Expand Up @@ -312,6 +317,7 @@ open class CropImageOptions : Parcelable {
showProgressBar = parcel.readByte().toInt() != 0
autoZoomEnabled = parcel.readByte().toInt() != 0
multiTouchEnabled = parcel.readByte().toInt() != 0
centerMoveEnabled = parcel.readByte().toInt() != 0
maxZoom = parcel.readInt()
initialCropWindowPaddingRatio = parcel.readFloat()
fixAspectRatio = parcel.readByte().toInt() != 0
Expand Down Expand Up @@ -363,6 +369,7 @@ open class CropImageOptions : Parcelable {
dest.writeByte((if (showProgressBar) 1 else 0).toByte())
dest.writeByte((if (autoZoomEnabled) 1 else 0).toByte())
dest.writeByte((if (multiTouchEnabled) 1 else 0).toByte())
dest.writeByte((if (centerMoveEnabled) 1 else 0).toByte())
dest.writeInt(maxZoom)
dest.writeFloat(initialCropWindowPaddingRatio)
dest.writeByte((if (fixAspectRatio) 1 else 0).toByte())
Expand Down
12 changes: 11 additions & 1 deletion cropper/src/main/java/com/canhub/cropper/CropImageView.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import android.os.Parcelable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;
import androidx.fragment.app.FragmentActivity;

Expand Down Expand Up @@ -217,6 +216,9 @@ public CropImageView(Context context, AttributeSet attrs) {
options.multiTouchEnabled =
ta.getBoolean(
R.styleable.CropImageView_cropMultiTouchEnabled, options.multiTouchEnabled);
options.centerMoveEnabled =
ta.getBoolean(
R.styleable.CropImageView_cropCenterMoveEnabled, options.centerMoveEnabled);
options.maxZoom = ta.getInteger(R.styleable.CropImageView_cropMaxZoom, options.maxZoom);
options.cropShape =
CropShape.values()[
Expand Down Expand Up @@ -412,6 +414,14 @@ public void setMultiTouchEnabled(boolean multiTouchEnabled) {
}
}

/** Set moving of the crop window by dragging the center to enabled/disabled. */
public void setCenterMoveEnabled(boolean centerMoveEnabled) {
if (mCropOverlayView.setCenterMoveEnabled(centerMoveEnabled)) {
handleCropWindowChanged(false, false);
mCropOverlayView.invalidate();
}
}

/** The max zoom allowed during cropping. */
public int getMaxZoom() {
return mMaxZoom;
Expand Down
17 changes: 16 additions & 1 deletion cropper/src/main/java/com/canhub/cropper/CropOverlayView.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public class CropOverlayView extends View {
/** Boolean to see if multi touch is enabled for the crop rectangle */
private boolean mMultiTouchEnabled;

/** Boolean to see if movement via dragging center is enabled for the crop rectangle */
private boolean mCenterMoveEnabled = true;

/** Handler from crop window stuff, moving and knowing possition. */
private final CropWindowHandler mCropWindowHandler = new CropWindowHandler();

Expand Down Expand Up @@ -330,6 +333,15 @@ public boolean setMultiTouchEnabled(boolean multiTouchEnabled) {
return false;
}

/** Set movement of the crop window by dragging the center to enabled/disabled. */
public boolean setCenterMoveEnabled(boolean centerMoveEnabled) {
if (mCenterMoveEnabled != centerMoveEnabled) {
mCenterMoveEnabled = centerMoveEnabled;
return true;
}
return false;
}

/**
* the min size the resulting cropping image is allowed to be, affects the cropping window limits
* (in pixels).<br>
Expand Down Expand Up @@ -402,6 +414,8 @@ public void setInitialAttributeValues(CropImageOptions options) {

setMultiTouchEnabled(options.multiTouchEnabled);

setCenterMoveEnabled(options.centerMoveEnabled);

mTouchRadius = options.touchRadius;

mInitialCropWindowPaddingRatio = options.initialCropWindowPaddingRatio;
Expand Down Expand Up @@ -918,7 +932,8 @@ public boolean onTouchEvent(MotionEvent event) {
* if press is far from crop window then no move handler is returned (null).
*/
private void onActionDown(float x, float y) {
mMoveHandler = mCropWindowHandler.getMoveHandler(x, y, mTouchRadius, mCropShape);
mMoveHandler = mCropWindowHandler
.getMoveHandler(x, y, mTouchRadius, mCropShape, mCenterMoveEnabled);
if (mMoveHandler != null) {
invalidate();
}
Expand Down
48 changes: 34 additions & 14 deletions cropper/src/main/java/com/canhub/cropper/CropWindowHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -160,24 +160,27 @@ class CropWindowHandler {

/**
* Determines which, if any, of the handles are pressed given the touch coordinates, the bounding
* box, and the touch radius.
* box, the touch radius, the crop shape and whether movement of the crop window is enabled.
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param targetRadius the target radius in pixels
* @param cropShape the shape of the crop window
* @param isCenterMoveEnabled whether movement of the crop window by dragging center is enabled
* @return the Handle that was pressed; null if no Handle was pressed
*/
fun getMoveHandler(
x: Float,
y: Float,
targetRadius: Float,
cropShape: CropImageView.CropShape,
isCenterMoveEnabled: Boolean
): CropWindowMoveHandler? {
val type: CropWindowMoveHandler.Type? = when (cropShape) {
RECTANGLE -> getRectanglePressedMoveType(x, y, targetRadius)
OVAL -> getOvalPressedMoveType(x, y)
RECTANGLE_VERTICAL_ONLY -> getRectangleVerticalOnlyPressedMoveType(x, y, targetRadius)
RECTANGLE_HORIZONTAL_ONLY -> getRectangleHorizontalOnlyPressedMoveType(x, y, targetRadius)
RECTANGLE -> getRectanglePressedMoveType(x, y, targetRadius, isCenterMoveEnabled)
OVAL -> getOvalPressedMoveType(x, y, isCenterMoveEnabled)
RECTANGLE_VERTICAL_ONLY -> getRectangleVerticalOnlyPressedMoveType(x, y, targetRadius, isCenterMoveEnabled)
RECTANGLE_HORIZONTAL_ONLY -> getRectangleHorizontalOnlyPressedMoveType(x, y, targetRadius, isCenterMoveEnabled)
}

return if (type != null) CropWindowMoveHandler(type, this, x, y) else null
Expand All @@ -192,12 +195,14 @@ class CropWindowHandler {
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param targetRadius the target radius in pixels
* @param isCenterMoveEnabled whether movement of the crop window by dragging center is enabled
* @return the Handle that was pressed; null if no Handle was pressed
*/
private fun getRectanglePressedMoveType(
x: Float,
y: Float,
targetRadius: Float
targetRadius: Float,
isCenterMoveEnabled: Boolean
): CropWindowMoveHandler.Type? {

// Note: corner-handles take precedence, then side-handles, then center.
Expand All @@ -214,7 +219,8 @@ class CropWindowHandler {
isInCornerTargetZone(x, y, mEdges.right, mEdges.bottom, targetRadius) -> {
CropWindowMoveHandler.Type.BOTTOM_RIGHT
}
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) &&
isCenterMoveEnabled &&
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) &&
focusCenter() -> {
CropWindowMoveHandler.Type.CENTER
}
Expand All @@ -230,7 +236,8 @@ class CropWindowHandler {
isInVerticalTargetZone(x, y, mEdges.right, mEdges.top, mEdges.bottom, targetRadius) -> {
CropWindowMoveHandler.Type.RIGHT
}
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) &&
isCenterMoveEnabled &&
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) &&
!focusCenter() -> {
CropWindowMoveHandler.Type.CENTER
}
Expand All @@ -244,9 +251,14 @@ class CropWindowHandler {
*
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param isCenterMoveEnabled whether movement of the crop window by dragging center is enabled
* @return the Handle that was pressed; null if no Handle was pressed
*/
private fun getOvalPressedMoveType(x: Float, y: Float): CropWindowMoveHandler.Type {
private fun getOvalPressedMoveType(
x: Float,
y: Float,
isCenterMoveEnabled: Boolean
): CropWindowMoveHandler.Type? {
/*
Use a 6x6 grid system divided into 9 "handles", with the center the biggest region. While
this is not perfect, it's a good quick-to-ship approach.
Expand Down Expand Up @@ -276,7 +288,9 @@ class CropWindowHandler {
x < rightCenter -> {
when {
y < topCenter -> CropWindowMoveHandler.Type.TOP
y < bottomCenter -> CropWindowMoveHandler.Type.CENTER
y < bottomCenter -> if (isCenterMoveEnabled) {
CropWindowMoveHandler.Type.CENTER
} else null
else -> CropWindowMoveHandler.Type.BOTTOM
}
}
Expand All @@ -297,12 +311,14 @@ class CropWindowHandler {
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param targetRadius the target radius in pixels
* @param isCenterMoveEnabled whether movement of the crop window by dragging center is enabled
* @return the Handle that was pressed; null if no Handle was pressed
*/
private fun getRectangleVerticalOnlyPressedMoveType(
x: Float,
y: Float,
targetRadius: Float
targetRadius: Float,
isCenterMoveEnabled: Boolean
): CropWindowMoveHandler.Type? {

// Note: top and bottom handles take precedence, then center.
Expand All @@ -315,7 +331,8 @@ class CropWindowHandler {
distance(x, y, mEdges.centerX(), mEdges.bottom) <= targetRadius -> {
CropWindowMoveHandler.Type.BOTTOM
}
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) -> {
isCenterMoveEnabled &&
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) -> {
CropWindowMoveHandler.Type.CENTER
}
else -> null
Expand All @@ -329,12 +346,14 @@ class CropWindowHandler {
* @param x the x-coordinate of the touch point
* @param y the y-coordinate of the touch point
* @param targetRadius the target radius in pixels
* @param isCenterMoveEnabled whether movement of the crop window by dragging center is enabled
* @return the Handle that was pressed; null if no Handle was pressed
*/
private fun getRectangleHorizontalOnlyPressedMoveType(
x: Float,
y: Float,
targetRadius: Float
targetRadius: Float,
isCenterMoveEnabled: Boolean
): CropWindowMoveHandler.Type? {

// Note: left and right handles take precedence, then center.
Expand All @@ -347,7 +366,8 @@ class CropWindowHandler {
distance(x, y, mEdges.right, mEdges.centerY()) <= targetRadius -> {
CropWindowMoveHandler.Type.RIGHT
}
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) -> {
isCenterMoveEnabled &&
isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) -> {
CropWindowMoveHandler.Type.CENTER
}
else -> null
Expand Down
1 change: 1 addition & 0 deletions cropper/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<attr name="cropAutoZoomEnabled" format="boolean"/>
<attr name="cropMaxZoom" format="integer"/>
<attr name="cropMultiTouchEnabled" format="boolean"/>
<attr name="cropCenterMoveEnabled" format="boolean"/>
<attr name="cropFixAspectRatio" format="boolean"/>
<attr name="cropAspectRatioX" format="integer"/>
<attr name="cropAspectRatioY" format="integer"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ internal class CameraFragment :
.setMaxZoom(8)
.setAutoZoomEnabled(false)
.setMultiTouchEnabled(false)
.setCenterMoveEnabled(true)
.setShowCropOverlay(false)
.setAllowFlipping(false)
.setSnapRadius(10f)
Expand Down Expand Up @@ -165,6 +166,7 @@ internal class CameraFragment :
.setMaxZoom(4)
.setAutoZoomEnabled(true)
.setMultiTouchEnabled(true)
.setCenterMoveEnabled(true)
.setShowCropOverlay(true)
.setAllowFlipping(true)
.setSnapRadius(3f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ internal class CropImageViewFragment :
setAspectRatio(options.ratio.first, options.ratio.second)
}
setMultiTouchEnabled(options.multiTouch)
setCenterMoveEnabled(options.centerMove)
isShowCropOverlay = options.showCropOverlay
isShowProgressBar = options.showProgressBar
isAutoZoomEnabled = options.autoZoom
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal class CropImageViewPresenter : CropImageViewContract.Presenter {
autoZoom = true,
maxZoomLvl = 2,
multiTouch = true,
centerMove = true,
showCropOverlay = true,
showProgressBar = true,
flipHorizontal = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ internal class OptionsDialogBottomSheet : BottomSheetDialogFragment(), OptionsCo
presenter.onMultiTouchSelect(isChecked)
}

binding.centerMoveEnabled.toggle.setOnCheckedChangeListener { _, isChecked ->
presenter.onCenterMoveSelect(isChecked)
}

binding.progressBar.toggle.setOnCheckedChangeListener { _, isChecked ->
presenter.onProgressBarSelect(isChecked)
}
Expand Down Expand Up @@ -210,6 +214,7 @@ internal class OptionsDialogBottomSheet : BottomSheetDialogFragment(), OptionsCo

binding.autoZoom.toggle.isChecked = options.autoZoom
binding.multiTouch.toggle.isChecked = options.multiTouch
binding.centerMoveEnabled.toggle.isChecked = options.centerMove
binding.cropOverlay.toggle.isChecked = options.showCropOverlay
binding.progressBar.toggle.isChecked = options.showProgressBar
binding.flipHorizontal.toggle.isChecked = options.flipHorizontal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ internal interface OptionsContract {
fun onAutoZoomSelect(enable: Boolean)
fun onMaxZoomLvlSelect(maxZoom: Int)
fun onMultiTouchSelect(enable: Boolean)
fun onCenterMoveSelect(enable: Boolean)
fun onCropOverlaySelect(show: Boolean)
fun onProgressBarSelect(show: Boolean)
fun onFlipHorizontalSelect(enable: Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal data class OptionsDomain(
val maxZoomLvl: Int,
val autoZoom: Boolean,
val multiTouch: Boolean,
val centerMove: Boolean,
val showCropOverlay: Boolean,
val showProgressBar: Boolean,
val flipHorizontal: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ internal class OptionsPresenter : OptionsContract.Presenter {
options = options.copy(multiTouch = enable)
}

override fun onCenterMoveSelect(enable: Boolean) {
options = options.copy(centerMove = enable)
}

override fun onCropOverlaySelect(show: Boolean) {
options = options.copy(showCropOverlay = show)
}
Expand All @@ -78,6 +82,7 @@ internal class OptionsPresenter : OptionsContract.Presenter {
maxZoomLvl = 2,
autoZoom = true,
multiTouch = true,
centerMove = true,
showCropOverlay = true,
showProgressBar = true,
flipHorizontal = false,
Expand Down
4 changes: 4 additions & 0 deletions sample/src/main/res/layout/fragment_options.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
android:id="@+id/multiTouch"
layout="@layout/switch_multi_touch" />

<include
android:id="@+id/centerMoveEnabled"
layout="@layout/switch_center_move" />

<include
android:id="@+id/progressBar"
layout="@layout/switch_progress_bar" />
Expand Down
Loading

0 comments on commit 245071e

Please sign in to comment.