diff --git a/cropimageview-samples/src/main/AndroidManifest.xml b/cropimageview-samples/src/main/AndroidManifest.xml index 19500b3..2cde5b2 100644 --- a/cropimageview-samples/src/main/AndroidManifest.xml +++ b/cropimageview-samples/src/main/AndroidManifest.xml @@ -20,7 +20,11 @@ android:label="@string/title_activity_simple_network_crop" /> + android:label="@string/title_activity_test" /> + \ No newline at end of file diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/HomeActivity.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/HomeActivity.java index 30435dc..bd75363 100644 --- a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/HomeActivity.java +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/HomeActivity.java @@ -3,6 +3,7 @@ import android.content.Intent; import android.os.Bundle; +import com.cesards.samples.cropimageview.rounded_corners.RoundedCornerImagesActivity; import com.cesards.samples.cropimageview.simple_crop.SimpleCropActivity; import com.cesards.samples.cropimageview.simple_network_crop.SimpleNetworkCropActivity; import com.cesards.samples.cropimageview.test.TestActivity; @@ -24,6 +25,8 @@ protected void onCreate(Bundle savedInstanceState) { findViewById(R.id.test).setOnClickListener(view -> startActivity(new Intent(this, TestActivity.class)) ); + findViewById(R.id.rounded_corners).setOnClickListener(view -> + startActivity(new Intent(this, RoundedCornerImagesActivity.class)) + ); } - } diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/Image.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/Image.java new file mode 100644 index 0000000..6882cef --- /dev/null +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/Image.java @@ -0,0 +1,30 @@ +package com.cesards.samples.cropimageview.rounded_corners; + +import com.cesards.cropimageview.crop.CropType; + +import androidx.annotation.DrawableRes; + +final class Image { + + @DrawableRes private final int drawableResource; + private final boolean roundedCorners; + @CropType private final int cropType; + + Image(@DrawableRes int drawableResource, boolean roundedCorners, int cropType) { + this.drawableResource = drawableResource; + this.roundedCorners = roundedCorners; + this.cropType = cropType; + } + + public int drawableResource() { + return drawableResource; + } + + public boolean roundedCorners() { + return roundedCorners; + } + + public int cropType() { + return cropType; + } +} diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImageFactory.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImageFactory.java new file mode 100644 index 0000000..53464df --- /dev/null +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImageFactory.java @@ -0,0 +1,92 @@ +package com.cesards.samples.cropimageview.rounded_corners; + +import com.cesards.cropimageview.crop.CropType; +import com.cesards.samples.cropimageview.R; + +import java.util.Arrays; +import java.util.List; + +final class ImageFactory { + + private ImageFactory() { + throw new AssertionError("This shouldn't be initialized!"); + } + + static List imagesWithoutRoundedCorners() { + return Arrays.asList( +// new Image(R.drawable.ball_vertical, false, CropType.NONE), +// new Image(R.drawable.ball_horizontal, false, CropType.NONE), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER) + ); + } + + static List imagesWithRoundedCorners() { + return Arrays.asList( +// new Image(R.drawable.ball_vertical, false, CropType.NONE), +// new Image(R.drawable.ball_horizontal, false, CropType.NONE), + new Image(R.drawable.ball_vertical, true, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, true, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, true, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, true, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, true, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, true, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, true, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, true, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_TOP), + new Image(R.drawable.ball_vertical, false, CropType.CENTER_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_CENTER), + new Image(R.drawable.ball_horizontal, false, CropType.LEFT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_TOP), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_BOTTOM), + new Image(R.drawable.ball_horizontal, false, CropType.RIGHT_CENTER) + ); + } +} diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImagesAdapter.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImagesAdapter.java new file mode 100644 index 0000000..8e70ce4 --- /dev/null +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/ImagesAdapter.java @@ -0,0 +1,72 @@ +package com.cesards.samples.cropimageview.rounded_corners; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.cesards.cropimageview.CropImageView; +import com.cesards.samples.cropimageview.R; + +import java.util.ArrayList; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; + +final class ImagesAdapter extends RecyclerView.Adapter { + + private final List images = new ArrayList<>(); + + void removeAll() { + if (images.isEmpty()) { + return; + } + int imagesLength = images.size(); + images.clear(); + notifyItemRangeRemoved(0, imagesLength); + } + + void add(Image image) { + images.add(image); + notifyItemInserted(this.images.size() - 1); + } + + void add(List images) { + this.images.addAll(images); + notifyItemRangeInserted(this.images.size() - 1, images.size()); + } + + @NonNull + @Override + public ItemImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ItemImageViewHolder(LayoutInflater.from( + parent.getContext()).inflate(R.layout.item_image, parent, false) + ); + } + + @Override + public void onBindViewHolder(@NonNull ItemImageViewHolder itemImageViewHolder, int position) { + itemImageViewHolder.bind(images.get(position)); + } + + @Override + public int getItemCount() { + return images.size(); + } + + static class ItemImageViewHolder extends RecyclerView.ViewHolder { + + private final CropImageView cropImageView; + + ItemImageViewHolder(@NonNull View itemView) { + super(itemView); + this.cropImageView = (CropImageView) itemView; + } + + void bind(Image image) { + cropImageView.setImageDrawable(ContextCompat.getDrawable(itemView.getContext(), image.drawableResource())); + cropImageView.croppedImage().withCropType(image.cropType()); + } + } +} diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/RoundedCornerImagesActivity.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/RoundedCornerImagesActivity.java new file mode 100644 index 0000000..9f693bf --- /dev/null +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/RoundedCornerImagesActivity.java @@ -0,0 +1,77 @@ +package com.cesards.samples.cropimageview.rounded_corners; + +import android.os.Bundle; +import android.app.Activity; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import com.cesards.samples.cropimageview.R; +import com.cesards.samples.cropimageview.rounded_corners.widget.VerticalTransparentItemDecorator; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.RecyclerView; + +public class RoundedCornerImagesActivity extends AppCompatActivity { + + private ImagesAdapter imagesAdapter = new ImagesAdapter(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_rounded_corner_images); + setupViews(savedInstanceState); + } + + private void setupViews(Bundle savedInstanceState) { + setSupportActionBar(findViewById(R.id.title_container)); +// getSupportActionBar().setHomeButtonEnabled(true); + getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP); + + RecyclerView images = findViewById(R.id.images); + images.addItemDecoration(new VerticalTransparentItemDecorator(getResources().getDimensionPixelOffset(R.dimen.activity_horizontal_margin))); + images.setAdapter(imagesAdapter); + + Spinner navSpinner = findViewById(R.id.image_options); + + navSpinner.setAdapter( + ArrayAdapter.createFromResource( + navSpinner.getContext(), + R.array.options, + android.R.layout.simple_spinner_dropdown_item + ) + ); + + navSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + switch (position) { + case 0: + imagesAdapter.removeAll(); + imagesAdapter.add(ImageFactory.imagesWithoutRoundedCorners()); + break; + + case 1: + imagesAdapter.removeAll(); + imagesAdapter.add(ImageFactory.imagesWithRoundedCorners()); + break; + + default: + throw new IllegalStateException("If it exists you can handle it."); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { } + }); + + if (savedInstanceState == null) { + navSpinner.setSelection(0); + } + } + +} diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/widget/VerticalTransparentItemDecorator.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/widget/VerticalTransparentItemDecorator.java new file mode 100644 index 0000000..b780159 --- /dev/null +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/rounded_corners/widget/VerticalTransparentItemDecorator.java @@ -0,0 +1,23 @@ +package com.cesards.samples.cropimageview.rounded_corners.widget; + +import android.graphics.Rect; +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +public class VerticalTransparentItemDecorator extends RecyclerView.ItemDecoration { + + private int space; + + public VerticalTransparentItemDecorator(int value) { + this.space = value; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + // Skip first item in the list. + if (parent.getChildAdapterPosition(view) != 0) { + outRect.set(0, space, 0, 0); + } + } +} diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_crop/SimpleCropActivity.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_crop/SimpleCropActivity.java index 046380b..1e24390 100644 --- a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_crop/SimpleCropActivity.java +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_crop/SimpleCropActivity.java @@ -4,7 +4,7 @@ import android.view.LayoutInflater; import android.widget.ImageView; -import com.cesards.cropimageview.model.CropType; +import com.cesards.cropimageview.crop.CropType; import com.cesards.samples.cropimageview.R; import com.cesards.samples.cropimageview._activity.CommonImagesAdapter; import com.cesards.samples.cropimageview._util.SystemUiHelper; @@ -54,7 +54,7 @@ public ImageView instantiatePagerItem(int position) { // cropImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ball_horizontal)); cropImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); } else { -// ((CropImageView) cropImageView).setCropType(images[position - 1]); +// ((CropImageView) cropImageView).withCropType(images[position - 1]); } return cropImageView; } diff --git a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_network_crop/SimpleNetworkCropActivity.java b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_network_crop/SimpleNetworkCropActivity.java index 4af9385..7422ecc 100644 --- a/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_network_crop/SimpleNetworkCropActivity.java +++ b/cropimageview-samples/src/main/java/com/cesards/samples/cropimageview/simple_network_crop/SimpleNetworkCropActivity.java @@ -1,19 +1,18 @@ package com.cesards.samples.cropimageview.simple_network_crop; import android.os.Bundle; -import android.app.Activity; import android.view.LayoutInflater; import android.widget.ImageView; import com.cesards.cropimageview.CropImageView; -import com.cesards.cropimageview.model.CropType; +import com.cesards.cropimageview.crop.CropType; import com.cesards.samples.cropimageview.R; import com.cesards.samples.cropimageview._activity.CommonImagesAdapter; import com.cesards.samples.cropimageview._util.SystemUiHelper; -import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; import androidx.viewpager.widget.ViewPager; public class SimpleNetworkCropActivity extends AppCompatActivity implements CommonImagesAdapter { @@ -55,21 +54,13 @@ public ImageView instantiatePagerItem(int position) { // cropImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ball_horizontal)); if (position == 0) { -// cropImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ball_horizontal)); - cropImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); +// cropImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + cropImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ball_horizontal)); } else { - Picasso.get().load(R.drawable.ball_horizontal).into(cropImageView, new Callback() { - @Override - public void onSuccess() { - cropImageView.setCropType(CropType.LEFT_CENTER); - } - - @Override - public void onError(Exception e) { - - } - }); -// ((CropImageView) cropImageView).setCropType(images[position - 1]); + cropImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ball_horizontal)); +// cropImageView.croppedImage().withCropType(CropType.LEFT_CENTER); +// Picasso.get().load(R.drawable.ball_horizontal).into(cropImageView); +// ((CropImageView) cropImageView).withCropType(images[position - 1]); } return cropImageView; } diff --git a/cropimageview-samples/src/main/res/layout/activity_home.xml b/cropimageview-samples/src/main/res/layout/activity_home.xml index 098a6cf..64a1a5c 100644 --- a/cropimageview-samples/src/main/res/layout/activity_home.xml +++ b/cropimageview-samples/src/main/res/layout/activity_home.xml @@ -48,5 +48,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/simple_network_crop" /> + + \ No newline at end of file diff --git a/cropimageview-samples/src/main/res/layout/activity_rounded_corner_images.xml b/cropimageview-samples/src/main/res/layout/activity_rounded_corner_images.xml new file mode 100644 index 0000000..091f4ae --- /dev/null +++ b/cropimageview-samples/src/main/res/layout/activity_rounded_corner_images.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/cropimageview-samples/src/main/res/layout/activity_test.xml b/cropimageview-samples/src/main/res/layout/activity_test.xml index 3f2b81c..2aecd9f 100644 --- a/cropimageview-samples/src/main/res/layout/activity_test.xml +++ b/cropimageview-samples/src/main/res/layout/activity_test.xml @@ -4,7 +4,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - + \ No newline at end of file diff --git a/cropimageview-samples/src/main/res/values/rounded_corner_options.xml b/cropimageview-samples/src/main/res/values/rounded_corner_options.xml new file mode 100644 index 0000000..8788520 --- /dev/null +++ b/cropimageview-samples/src/main/res/values/rounded_corner_options.xml @@ -0,0 +1,7 @@ + + + + Without Rounded Corners + With Rounded Corners + + \ No newline at end of file diff --git a/cropimageview-samples/src/main/res/values/strings.xml b/cropimageview-samples/src/main/res/values/strings.xml index 9e22a1f..bea659f 100644 --- a/cropimageview-samples/src/main/res/values/strings.xml +++ b/cropimageview-samples/src/main/res/values/strings.xml @@ -3,4 +3,5 @@ CropImageView Samples SimpleNetworkCrop TestActivity + RoundedCornerImagesActivity diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/CropImageView.java b/cropimageview/src/main/java/com/cesards/cropimageview/CropImageView.java index c2c4d64..860d1f7 100644 --- a/cropimageview/src/main/java/com/cesards/cropimageview/CropImageView.java +++ b/cropimageview/src/main/java/com/cesards/cropimageview/CropImageView.java @@ -1,103 +1,184 @@ package com.cesards.cropimageview; import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.widget.ImageView; -import com.cesards.cropimageview.model.CropImage; -import com.cesards.cropimageview.model.CropImageFactory; -import com.cesards.cropimageview.model.CropType; +import android.util.Log; +import android.view.ViewOutlineProvider; -import androidx.annotation.Nullable; +import androidx.annotation.NonNull; import androidx.appcompat.widget.AppCompatImageView; public class CropImageView extends AppCompatImageView { - @Nullable private CropImage cropImage; - @CropType private int cropType = CropType.NONE; + private final CroppedImage croppedImage = new CroppedImage(this); + private final RoundedCornerImage roundedImage = new RoundedCornerImage(this); public CropImageView(Context context) { super(context); - setup(); + roundedImage.setup(context, null); + croppedImage.setupCrop(context, null); } public CropImageView(Context context, AttributeSet attrs) { super(context, attrs, 0); - parseAttributes(attrs); - setup(); + roundedImage.setup(context, attrs); + croppedImage.setupCrop(context, attrs); } public CropImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - parseAttributes(attrs); - setup(); + roundedImage.setup(context, attrs); + croppedImage.setupCrop(context, attrs); } /** - * Set crop type for the {@link ImageView} - * - * @param cropType A {@link CropType} desired scaling mode. + * Returns {@link RoundedCornerImage} so the user can retrieve and set information related to cropping + * the image. */ - public void setCropType(@CropType int cropType) { - this.cropType = cropType; - - setWillNotCacheDrawing(false); - - requestLayout(); - invalidate(); + @NonNull + public CroppedImage croppedImage() { + return croppedImage; } /** - * Return the current crop type in use by this ImageView. - * - * @return a {@link CropType} in use by this ImageView + * Returns {@link RoundedCornerImage} so the user can retrieve and set information related to rounding + * the image corners. */ - @CropType - public int getCropType() { - return cropType; - } +// @NonNull +// RoundedCornerImage roundedImage() { +// return roundedImage; +// } @Override protected boolean setFrame(int l, int t, int r, int b) { final boolean changed = super.setFrame(l, t, r, b); - if (!isInEditMode() && cropImage != null && getDrawable() != null) { - cropImage.computeImageTransformation(); + if (changed) { + croppedImage.applyTransformation(); } + return changed; } @Override - public void setImageBitmap(Bitmap bm) { - super.setImageBitmap(bm); - setup(); + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + Log.d("", ""); + super.onLayout(changed, left, top, right, bottom); + if (changed) { + roundedImage.requiresShapeUpdate(); + } + } + + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + Log.d("onDraw", toString()); + roundedImage.dispatchOnDraw(canvas); } + @Override public void setImageDrawable(Drawable drawable) { + Log.d("", ""); super.setImageDrawable(drawable); - setup(); - } + roundedImage.requiresShapeUpdate(); - @Override - public void setImageResource(int resId) { - super.setImageResource(resId); - setup(); + +// if (drawable instanceof RoundedBitmapDrawable) { +// super.setImageDrawable(drawable); +// return; +// } +// RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(getResources(), ((BitmapDrawable) drawable).getBitmap()); +// mBitmapShader = new BitmapShader(dr.getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + +// dr.setCornerRadius(10f); +// super.setImageDrawable(dr); +// this.requiersShapeUpdate = true; +// super.setImageDrawable(drawable); +// postInvalidate(); } - private void setup() { - setScaleType(ScaleType.MATRIX); + @Override + public ViewOutlineProvider getOutlineProvider() { + Log.d("", ""); +// return super.getOutlineProvider(); + return roundedImage.getOutlineProvider(); + } - if (getDrawable() != null) { - cropImage = new CropImageFactory().getCropImage(this, cropType); - } + @Override + protected void drawableStateChanged() { + Log.d("", ""); + super.drawableStateChanged(); + roundedImage.notifyDrawableStateChanges(); // is it really necessary? } - private void parseAttributes(AttributeSet attrs) { - final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.civ_CropImageView); - cropType = a.getInt(R.styleable.civ_CropImageView_civ_crop, CropType.NONE); - a.recycle(); + @Override + public void setScaleType(ScaleType scaleType) { + Log.d("", ""); + croppedImage.delegateSetScaleType(scaleType); +// roundedImage.delegateScaleType(scaleType); + super.setScaleType(scaleType); } + + + + + + + + + + +// @Override +// protected void onDraw(Canvas canvas) { +// super.onDraw(canvas); +// if (getDrawable() instanceof RoundedBitmapDrawable) { +// canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, paint); +// } +// } + + + + + + + + + + // @Override +// public void setImageBitmap(Bitmap bm) { +// super.setImageDrawable(roundedImage.modifiedDrawable(bm)); +// // I think it would be better not to modify this method because we are breaking Liskov principle? +// // so we could use Bitmap modifiedBitmap(Bitmap bitmap); +// // as +// // super.setImageBitmap(roundedImage.modifiedBitmap(bm)); + // bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); +// } +// + +// @Override +// protected void onDraw(Canvas canvas) { +// super.onDraw(canvas); +// rect.bottom = getHeight(); +// rect.right = getWidth(); +// rect.left = 0; +// rect.top = 0; +// +// canvas.drawRoundRect(rect, 10f, 10f, paint); +// +// if (requiersShapeUpdate) { +// calculateLayout(canvas.getWidth(), canvas.getHeight()); +// requiersShapeUpdate = false; +// } +// +// } + + + + + + } diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/CroppedImage.java b/cropimageview/src/main/java/com/cesards/cropimageview/CroppedImage.java new file mode 100644 index 0000000..f5814f1 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/CroppedImage.java @@ -0,0 +1,89 @@ +package com.cesards.cropimageview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.cesards.cropimageview.crop.CropType; +import com.cesards.cropimageview.crop.ImageTransformation; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public final class CroppedImage implements LifecycleCroppedImage { + + private final ImageView imageView; + private final ImageTransformation imageTransformation; + @CropType private int cropType = CropType.NONE; + + public CroppedImage(@NonNull ImageView imageView) { + this.imageView = imageView; + imageTransformation = new ImageTransformation(imageView); + } + + /** + * Set crop type for the {@link ImageView} + * + * @param cropType A {@link CropType} desired scaling mode. + */ + public void withCropType(@CropType int cropType) { + if (cropType == this.cropType) { + return; + } + + this.cropType = cropType; + setupScaleType(); + } + + public void crop(@CropType int cropType) { + withCropType(cropType); + imageView.requestLayout(); + imageView.invalidate(); + } + + /** + * Return the current crop type in use by this ImageView. + * + * @return a {@link CropType} in use by this ImageView + */ + @CropType + public int getCropType() { + return cropType; + } + + @Override + public void setupCrop(@NonNull Context context, @Nullable AttributeSet attributeSet) { + if (attributeSet != null) { + parseAttributes(attributeSet); + } + + setupScaleType(); + } + + private void setupScaleType() { + if (cropType != CropType.NONE) { + imageView.setScaleType(ImageView.ScaleType.MATRIX); + } + } + + @Override + public void applyTransformation() { + if (!imageView.isInEditMode() && imageView.getDrawable() != null && cropType != CropType.NONE) { + imageTransformation.compute(cropType); + } + } + + @Override + public void delegateSetScaleType(ImageView.ScaleType scaleType) { + if (scaleType != ImageView.ScaleType.MATRIX) { + cropType = CropType.NONE; + } + } + + private void parseAttributes(AttributeSet attrs) { + final TypedArray a = imageView.getContext().obtainStyledAttributes(attrs, R.styleable.civ_CropImageView); + cropType = a.getInt(R.styleable.civ_CropImageView_civ_crop, CropType.NONE); + a.recycle(); + } +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/LifecycleCroppedImage.java b/cropimageview/src/main/java/com/cesards/cropimageview/LifecycleCroppedImage.java new file mode 100644 index 0000000..9deab70 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/LifecycleCroppedImage.java @@ -0,0 +1,18 @@ +package com.cesards.cropimageview; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public interface LifecycleCroppedImage { + void setupCrop(@NonNull Context context, @Nullable AttributeSet attributeSet); + + /** + * Call super first and return the result + * */ + void applyTransformation(); + void delegateSetScaleType(ImageView.ScaleType scaleType); +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImage.java b/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImage.java new file mode 100644 index 0000000..a9dd975 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImage.java @@ -0,0 +1,595 @@ +package com.cesards.cropimageview; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.ImageView; + +import com.cesards.cropimageview.rounded_corners.ClipPathManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.view.ViewCompat; + +public final class RoundedCornerImage implements RoundedCornerImageViewHook { + + + @NonNull private final ImageView imageView; + private final Paint clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path clipPath = new Path(); + private final PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT); + @Nullable protected Drawable drawable = null; + private ClipPathManager clipManager = new ClipPathManager(); + private boolean requiersShapeUpdate = true; + private Bitmap clipBitmap; + final Path rectView = new Path(); + private final RectF rectF = new RectF(); + private int topLeftRadius = 15; + private int topRightRadius= 15; + private int bottomRightRadius= 15; + private int bottomLeftRadius= 15; + + private final RectF borderRectF = new RectF(); + private final Path borderPath = new Path(); + + + public RoundedCornerImage(@NonNull ImageView imageView) { + this.imageView = imageView; + } + + @Override + public void setup(@NonNull Context context, @Nullable AttributeSet attributeSet) { + final TypedArray a = imageView.getContext().obtainStyledAttributes(attributeSet, R.styleable.civ_CropImageView); + +// final int cornerRadius = a.getInt(R.styleable.civ_CropImageView_civ_cornerRadius, Corner.NONE); +// if (cornerRadius != Corner.NONE) { +// this.cornerRadius[Corner.TOP_LEFT] = cornerRadius; +// this.cornerRadius[Corner.TOP_RIGHT] = cornerRadius; +// this.cornerRadius[Corner.BOTTOM_LEFT] = cornerRadius; +// this.cornerRadius[Corner.BOTTOM_RIGHT] = cornerRadius; +// } else { +// this.cornerRadius[Corner.TOP_LEFT] = a.getInt(R.styleable.civ_CropImageView_civ_cornerRadiusTopLeft, Corner.NONE); +// this.cornerRadius[Corner.TOP_RIGHT] = a.getInt(R.styleable.civ_CropImageView_civ_cornerRadiusTopRight, Corner.NONE); +// this.cornerRadius[Corner.BOTTOM_LEFT] = a.getInt(R.styleable.civ_CropImageView_civ_cornerRadiusBottomLeft, Corner.NONE); +// this.cornerRadius[Corner.BOTTOM_RIGHT] = a.getInt(R.styleable.civ_CropImageView_civ_cornerRadiusBottomRight, Corner.NONE); +// } +// for (int i = 0, len = this.cornerRadius.length; i < len; i++) { +// if (this.cornerRadius[i] < 0) { +// this.cornerRadius[i] = DEFAULT_RADIUS; +// } +// } + + + clipPaint.setAntiAlias(true); + imageView.setWillNotDraw(false); + clipPaint.setColor(Color.BLUE); + clipPaint.setStyle(Paint.Style.FILL); + clipPaint.setStrokeWidth(1); + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) { + clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, clipPaint); //Only works for software layers + } else { + clipPaint.setXfermode(pdMode); + imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); //Only works for software layers + } + + clipManager.setClipPathCreator(new ClipPathManager.ClipPathCreator() { + @Override + public Path createClipPath(int width, int height) { + rectF.set(0, 0, width, height); + return generatePath(rectF, + Math.min(topLeftRadius, Math.min(width, height)), + Math.min(topRightRadius, Math.min(width, height)), + Math.min(bottomRightRadius, Math.min(width, height)), + Math.min(bottomLeftRadius, Math.min(width, height)) + ); + + } + + @Override + public boolean requiresBitmap() { + return false; + } + }); + + a.recycle(); + } + + private Path generatePath(RectF rect, float topLeftRadius, float topRightRadius, float bottomRightRadius, float bottomLeftRadius) { + return generatePath(false, rect, topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius); + } + + private Path generatePath(boolean useBezier, RectF rect, float topLeftRadius, float topRightRadius, float bottomRightRadius, float bottomLeftRadius) { + final Path path = new Path(); + + final float left = rect.left; + final float top = rect.top; + final float bottom = rect.bottom; + final float right = rect.right; + + final float minSize = Math.min(rect.width() / 2f, rect.height() / 2f); + + topLeftRadius = topLeftRadius < 0 ? 0 : topLeftRadius; + topRightRadius = topRightRadius < 0 ? 0 : topRightRadius; + bottomLeftRadius = bottomLeftRadius < 0 ? 0 : bottomLeftRadius; + bottomRightRadius = bottomRightRadius < 0 ? 0 : bottomRightRadius; + + if (topLeftRadius > minSize) { + topLeftRadius = minSize; + } + if (topRightRadius > minSize) { + topRightRadius = minSize; + } + if (bottomLeftRadius > minSize) { + bottomLeftRadius = minSize; + } + if (bottomRightRadius > minSize) { + bottomRightRadius = minSize; + } + + path.moveTo(left + topLeftRadius, top); + path.lineTo(right - topRightRadius, top); + + //float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo + if (useBezier) { + path.quadTo(right, top, right, top + topRightRadius); + } else { + path.arcTo(new RectF(right - topRightRadius * 2f, top, right, top + topRightRadius * 2f), -90, 90); + } + path.lineTo(right, bottom - bottomRightRadius); + if (useBezier) { + path.quadTo(right, bottom, right - bottomRightRadius, bottom); + } else { + path.arcTo(new RectF(right - bottomRightRadius * 2f, bottom - bottomRightRadius * 2f, right, bottom), 0, 90); + } + path.lineTo(left + bottomLeftRadius, bottom); + if (useBezier) { + path.quadTo(left, bottom, left, bottom - bottomLeftRadius); + } else { + path.arcTo(new RectF(left, bottom - bottomLeftRadius * 2f, left + bottomLeftRadius * 2f, bottom), 90, 90); + } + path.lineTo(left, top + topLeftRadius); + if (useBezier) { + path.quadTo(left, top, left + topLeftRadius, top); + } else { + path.arcTo(new RectF(left, top, left + topLeftRadius * 2f, top + topLeftRadius * 2f), 180, 90); + } + path.close(); + + return path; + } + + public void setDrawable(Drawable drawable) { + this.drawable = drawable; + requiresShapeUpdate(); + } + + public void setDrawable(int redId) { + setDrawable(AppCompatResources.getDrawable(imageView.getContext(), redId)); + } + + public void requiresShapeUpdate() { + borderPath.set(generatePath(borderRectF, + topLeftRadius, + topRightRadius, + bottomRightRadius, + bottomLeftRadius + )); + requiersShapeUpdate = true; + imageView.postInvalidate(); + } + + public void dispatchOnDraw(Canvas canvas) { + if (requiersShapeUpdate) { + calculateLayout(canvas.getWidth(), canvas.getHeight()); + requiersShapeUpdate = false; + } + + if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1){ + canvas.drawPath(clipPath, clipPaint); + } else { + canvas.drawPath(rectView, clipPaint); + } + + if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) { + imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + } + + private void calculateLayout(int width, int height) { + rectView.reset(); + rectView.addRect(0f, 0f, 1f * imageView.getWidth(), 1f * imageView.getHeight(), Path.Direction.CW); + + if (clipManager != null) { + if (width > 0 && height > 0) { + clipManager.setupClipLayout(width, height); + clipPath.reset(); + clipPath.set(clipManager.createMask(width, height)); + + //invert the path for android P + if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) { + final boolean success = rectView.op(clipPath, Path.Op.DIFFERENCE); + } + + //this needs to be fixed for 25.4.0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && ViewCompat.getElevation(imageView) > 0f) { + try { + imageView.setOutlineProvider(getOutlineProvider()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + imageView.postInvalidate(); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public ViewOutlineProvider getOutlineProvider() { + return new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + final Path shadowConvexPath = clipManager.getShadowConvexPath(); + if (shadowConvexPath != null) { + try { + outline.setConvexPath(shadowConvexPath); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + }; + + } + + + + + + + + + +// private static final float DEFAULT_RADIUS = 0f; +// +// private final float[] cornerRadius = new float[] { +// DEFAULT_RADIUS, +// DEFAULT_RADIUS, +// DEFAULT_RADIUS, +// DEFAULT_RADIUS +// }; +// private Drawable backgroundDrawable; //? needed? @Nullable ? @NonNull? +// private Drawable drawable; //? needed? @Nullable ? @NonNull?private boolean mMutateBackground = false; + + + + /** + * @return the largest corner radius. + */ +// public float getCornerRadiuses() { +// return getMaxCornerRadius(); +// } + + /** + * @return the largest corner radius. + */ +// public float getMaxCornerRadius() { +// float maxRadius = DEFAULT_RADIUS; +// for (int i = cornerRadius.length - 1; i >= 0; i--) { +// maxRadius = Math.max(cornerRadius[i], maxRadius); +// } +// return maxRadius; +// } + + /** + * Get the imageCorner radius of a specified imageCorner. + * + * @param imageCorner the imageCorner. + * @return the radius. + */ +// public float getCornerRadius(@Corner int imageCorner) { +// return cornerRadius[imageCorner]; +// } + + /** + * Set the corner radii of all corners in px. + * + * @param radius the radius to set. + */ +// public void setCornerRadiuses(int radius) { +// setCornerRadius(radius, radius, radius, radius); +// } + + /** + * Set all the corner radii from a dimension resource id. + * + * @param resId dimension resource id of radii. + */ +// public void setCornerRadiusDimen(@DimenRes int resId) { +// float radius = imageView.getResources().getDimension(resId); +// setCornerRadius(radius, radius, radius, radius); +// } + + /** + * Set the corner radius of a specific corner from a dimension resource id. + * + * @param corner the corner to set. + * @param resId the dimension resource id of the corner radius. + */ +// public void setCornerRadiusDimen(@Corner int corner, @DimenRes int resId) { +// if (corner == Corner.NONE) { +// return; +// } +// setCornerRadius(corner, imageView.getResources().getDimensionPixelSize(resId)); +// } + + /** + * Set the corner radii of all corners in px. + * + * @param radius the radius to set. + */ +// public void setCornerRadius(float radius) { +// setCornerRadius(radius, radius, radius, radius); +// } + + /** + * Set the corner radius of a specific corner in px. + * + * @param corner the corner to set. + * @param radius the corner radius to set in px. + */ +// public void setCornerRadius(@Corner int corner, float radius) { +// if (cornerRadius[corner] == radius) { +// return; +// } +// +// cornerRadius[corner] = radius; +// +// updateDrawableAttrs(); +// updateBackgroundDrawableAttrs(false); +// imageView.invalidate(); +// } + + /** + * Set the corner radii of each corner individually. Currently only one unique nonzero value is + * supported. + * +// * @param topLeft radius of the top left corner in px. +// * @param topRight radius of the top right corner in px. +// * @param bottomRight radius of the bottom right corner in px. +// * @param bottomLeft radius of the bottom left corner in px. + */ +// public void setCornerRadius(float topLeft, float topRight, float bottomLeft, float bottomRight) { +// if (cornerRadius[Corner.TOP_LEFT] == topLeft && cornerRadius[Corner.TOP_RIGHT] == topRight && cornerRadius[Corner.BOTTOM_RIGHT] == bottomRight && cornerRadius[Corner.BOTTOM_LEFT] == bottomLeft) { +// return; +// } +// +// cornerRadius[Corner.TOP_LEFT] = topLeft; +// cornerRadius[Corner.TOP_RIGHT] = topRight; +// cornerRadius[Corner.BOTTOM_LEFT] = bottomLeft; +// cornerRadius[Corner.BOTTOM_RIGHT] = bottomRight; +// +// updateDrawableAttrs(); +// updateBackgroundDrawableAttrs(false); +// imageView.invalidate(); +// } + + + + + + + + @Override + public Drawable modifiedDrawable(Drawable drawable) { +// this.drawable = SuperDrawable.fromDrawable(drawable); + updateDrawableAttrs(); + return this.drawable; + } + + @Override + public Drawable modifiedDrawable(Bitmap bitmap) { +// this.drawable = SuperDrawable.fromBitmap(bitmap); + updateDrawableAttrs(); + return this.drawable; + } + + @Override + public void notifyDrawableStateChanges() { +// imageView.invalidate(); + } + +// @Override +// public void delegateScaleType(ImageView.ScaleType scaleType) { +// // TODO: 8/28/18 CHECK IF REALLY NEEDED +// if (imageView.getScaleType() != scaleType) { +// updateDrawableAttrs(); +// updateBackgroundDrawableAttrs(false); +// } +// // parent scaleType invalidates. +// } + + private void updateDrawableAttrs() { + updateAttrs(drawable); + } + + private void updateAttrs(Drawable drawable) { + if (drawable == null) { return; } + + // TODO how this can be possible? +// if (drawable instanceof SuperDrawable) { +// ((SuperDrawable) drawable).setCornerRadius( +// cornerRadius[Corner.TOP_LEFT], +// cornerRadius[Corner.TOP_RIGHT], +// cornerRadius[Corner.BOTTOM_LEFT], +// cornerRadius[Corner.BOTTOM_RIGHT] +// ); +// } + } + + + +// private void updateBackgroundDrawableAttrs(boolean convert) { +// if (mutateBackground) { +// if (convert) { +//// backgroundDrawable = SuperDrawable.fromDrawable(backgroundDrawable); +// } +// updateAttrs(backgroundDrawable); +// } +// } + + + + + + + + + +// @Override +// public Drawable delegateSetImageBitmap(Bitmap bm) { +// drawable = SuperDrawable.fromBitmap(bm); +// updateDrawableAttrs(); +// imageView.setImageDrawable(this.drawable); +// return drawable; +// } + + + + + + + + +// @Override +// public void setImageBitmap(Bitmap bm) { +// +// +// super.setImageDrawable(drawable); +// } +// +// @Override +// public void setImageResource(@DrawableRes int resId) { +// if (resource != resId) { +// resource = resId; +// drawable = resolveResource(); +// updateDrawableAttrs(); +// super.setImageDrawable(drawable); +// } +// } +// +// @Override +// public void setImageURI(Uri uri) { +// super.setImageURI(uri); +// setImageDrawable(getDrawable()); +// } +// +// private Drawable resolveResource() { +// Resources rsrc = getResources(); +// if (rsrc == null) { return null; } +// +// Drawable d = null; +// +// if (resource != 0) { +// try { +// d = rsrc.getDrawable(resource); +// } catch (Exception e) { +// Log.w(TAG, "Unable to find resource: " + resource, e); +// // Don't try again. +// resource = 0; +// } +// } +// return SuperDrawable.fromDrawable(d); +// } +// +// @Override +// public void setBackground(Drawable background) { +// setBackgroundDrawable(background); +// } +// +// @Override +// public void setBackgroundResource(@DrawableRes int resId) { +// if (backgroundResource != resId) { +// backgroundResource = resId; +// backgroundDrawable = resolveBackgroundResource(); +// setBackgroundDrawable(backgroundDrawable); +// } +// } +// +// @Override +// public void setBackgroundColor(int color) { +// backgroundDrawable = new ColorDrawable(color); +// setBackgroundDrawable(backgroundDrawable); +// } +// +// private Drawable resolveBackgroundResource() { +// Resources rsrc = getResources(); +// if (rsrc == null) { return null; } +// +// Drawable d = null; +// +// if (backgroundResource != 0) { +// try { +// d = rsrc.getDrawable(backgroundResource); +// } catch (Exception e) { +// Log.w(TAG, "Unable to find resource: " + backgroundResource, e); +// // Don't try again. +// backgroundResource = 0; +// } +// } +// return SuperDrawable.fromDrawable(d); +// } +// +// @Override +// @Deprecated +// public void setBackgroundDrawable(Drawable background) { +// backgroundDrawable = background; +// updateBackgroundDrawableAttrs(true); +// //noinspection deprecation +// super.setBackgroundDrawable(backgroundDrawable); +// } + + + +// /** +// * If {@code true}, we will also round the background drawable according to the settings on this +// * ImageView. +// * +// * @return whether the background is mutated. +// */ +// public boolean mutatesBackground() { +// return mMutateBackground; +// } +// +// /** +// * Set whether the {@link RoundedImageView} should round the background drawable according to +// * the settings in addition to the source drawable. +// * +// * @param mutate true if this view should mutate the background drawable. +// */ +// public void mutateBackground(boolean mutate) { +// if (mMutateBackground == mutate) { return; } +// +// mMutateBackground = mutate; +// updateBackgroundDrawableAttrs(true); +// invalidate(); +// } +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImageViewHook.java b/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImageViewHook.java new file mode 100644 index 0000000..4376655 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/RoundedCornerImageViewHook.java @@ -0,0 +1,60 @@ +package com.cesards.cropimageview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public interface RoundedCornerImageViewHook { + void setup(@NonNull Context context, @Nullable AttributeSet attributeSet); + + // void delegateDispatchDraw(Canvas canvas); + + + + + + + + /** + * Needed for {@link ImageView#setImageDrawable(Drawable)} + * We process the drawable that will be set in the ImageView and we use it as a hook for every + * time {@link ImageView#setImageDrawable(Drawable)} is called + * + * This should be passed to the parent by the ImageView: + * super.setImageDrawable(roundedImage.modifiedDrawable(drawable)); + * + * + * + * + */ + Drawable modifiedDrawable(Drawable drawable); + + /** + * Usage: + * + * @Override + * public void setImageBitmap(Bitmap bm) { + * super.setImageDrawable(roundedImage.modifiedDrawable(bm)); + * } + */ + Drawable modifiedDrawable(Bitmap bitmap); + + /** + * Needed for {@link ImageView#drawableStateChanged()} + * + * Usage: + * + * @Override + * protected void drawableStateChanged() { + * super.drawableStateChanged(); + * roundedImage.notifyDrawableStateChanges(); + * } + */ + void notifyDrawableStateChanges(); +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropType.java b/cropimageview/src/main/java/com/cesards/cropimageview/crop/CropType.java similarity index 94% rename from cropimageview/src/main/java/com/cesards/cropimageview/model/CropType.java rename to cropimageview/src/main/java/com/cesards/cropimageview/crop/CropType.java index e405011..aa0c4c4 100644 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropType.java +++ b/cropimageview/src/main/java/com/cesards/cropimageview/crop/CropType.java @@ -1,4 +1,4 @@ -package com.cesards.cropimageview.model; +package com.cesards.cropimageview.crop; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -8,6 +8,7 @@ /** * Options for cropping the bounds of an image to the bounds of the ImageView. */ +@Retention(RetentionPolicy.SOURCE) @IntDef({ CropType.NONE, CropType.LEFT_TOP, @@ -19,7 +20,6 @@ CropType.CENTER_TOP, CropType.CENTER_BOTTOM, }) -@Retention(RetentionPolicy.SOURCE) public @interface CropType { int NONE = -1; diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/crop/ImageTransformation.java b/cropimageview/src/main/java/com/cesards/cropimageview/crop/ImageTransformation.java new file mode 100644 index 0000000..e747c2d --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/crop/ImageTransformation.java @@ -0,0 +1,140 @@ +package com.cesards.cropimageview.crop; + +import android.graphics.Matrix; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +public class ImageTransformation { + + private final ImageView imageView; + private final CompatMatrix compatMatrix; + + public ImageTransformation(ImageView imageView) { + this.imageView = imageView; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + compatMatrix = new PreAPI18Matrix(imageView); + } else { + compatMatrix = new API18Matrix(imageView); + } + } + + public void compute(@CropType int cropType) { + int viewWidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); + int viewHeight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); + + if (cropType != CropType.NONE && viewHeight > 0 && viewWidth > 0) { + Matrix matrix = compatMatrix.matrix(cropType); + + Drawable drawable = imageView.getDrawable(); + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + + float scaleY = (float) viewHeight / (float) drawableHeight; + float scaleX = (float) viewWidth / (float) drawableWidth; + float scale = scaleX > scaleY ? scaleX : scaleY; + matrix.setScale(scale, scale); // Same as doing matrix.reset() and matrix.preScale(...) + + boolean verticalImageMode = scaleX > scaleY; + + float postDrawableWidth = drawableWidth * scale; + float xTranslation = getXTranslation(cropType, viewWidth, postDrawableWidth, verticalImageMode); + float postDrawabeHeigth = drawableHeight * scale; + float yTranslation = getYTranslation(cropType, viewHeight, postDrawabeHeigth, verticalImageMode); + + matrix.postTranslate(xTranslation, yTranslation); + imageView.setImageMatrix(matrix); + } + } + + private float getYTranslation( + @CropType int cropType, + int viewHeight, + float postDrawableHeight, + boolean verticalImageMode + ) { + if (verticalImageMode) { + switch (cropType) { + case CropType.CENTER_BOTTOM: + case CropType.LEFT_BOTTOM: + case CropType.RIGHT_BOTTOM: + return viewHeight - postDrawableHeight; + case CropType.LEFT_CENTER: + case CropType.RIGHT_CENTER: + // View in the middle of the screen + return (viewHeight - postDrawableHeight) / 2f; + } + } + + // All other cases we don't need to translate + return 0f; + } + + private float getXTranslation( + @CropType int cropType, + int viewWidth, + float postDrawableWidth, + boolean verticalImageMode + ) { + if (!verticalImageMode) { + switch (cropType) { + case CropType.RIGHT_TOP: + case CropType.RIGHT_CENTER: + case CropType.RIGHT_BOTTOM: + return viewWidth - postDrawableWidth; + case CropType.CENTER_TOP: + case CropType.CENTER_BOTTOM: + // View in the middle of the screen + return (viewWidth - postDrawableWidth) / 2f; + } + } + // All other cases we don't need to translate + return 0f; + } + + + private static final class PreAPI18Matrix extends CompatMatrix { + + @Nullable + private Matrix matrix; + + PreAPI18Matrix(ImageView imageView) { + super(imageView); + } + + @Override + Matrix matrix(int cropType) { + if (cropType != CropType.NONE && matrix == null) { + matrix = new Matrix(); + } + + return matrix != null ? matrix : imageView.getImageMatrix(); + } + } + + private static final class API18Matrix extends CompatMatrix { + + API18Matrix(ImageView imageView) { + super(imageView); + } + + @Override + Matrix matrix(int cropType) { + return imageView.getImageMatrix(); + } + } + + static abstract class CompatMatrix { + + final ImageView imageView; + + CompatMatrix(ImageView imageView) { + this.imageView = imageView; + } + + abstract Matrix matrix(@CropType int cropType); + } +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/delegation/Crop.java b/cropimageview/src/main/java/com/cesards/cropimageview/delegation/Crop.java deleted file mode 100644 index b4a1bd2..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/delegation/Crop.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.cesards.cropimageview.delegation; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public interface Crop { - void initialize(@NonNull Context context, @Nullable AttributeSet attributeSet); - boolean delegateSetFrame(boolean setFrameResult); - void delegateSetImageBitmap(Bitmap bitmap); - void delegateSetImageDrawable(Drawable drawable); - void delegateSetImageResource(int resId); -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/delegation/CropDelegation.java b/cropimageview/src/main/java/com/cesards/cropimageview/delegation/CropDelegation.java deleted file mode 100644 index d65be99..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/delegation/CropDelegation.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.cesards.cropimageview.delegation; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.widget.ImageView; - -import com.cesards.cropimageview.R; -import com.cesards.cropimageview.model.CropImage; -import com.cesards.cropimageview.model.CropImageFactory; -import com.cesards.cropimageview.model.CropType; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public final class CropDelegation implements Crop { - - @NonNull private final ImageView imageView; - @Nullable private CropImage cropImage; - private int cropType = CropType.NONE; - - public CropDelegation(@NonNull ImageView imageView) { - this.imageView = imageView; - } - - /** - * Set crop type for the {@link ImageView} - * - * @param cropType A {@link CropType} desired scaling mode. - */ - public void setCropType(@CropType int cropType) { - this.cropType = cropType; - - imageView.setWillNotCacheDrawing(false); - - imageView.requestLayout(); - imageView.invalidate(); - } - - @Override - public void initialize(@NonNull Context context, @Nullable AttributeSet attributeSet) { - if (attributeSet != null) { - parseAttributes(attributeSet); - } - setup(); - } - - /** - * Return the current crop type in use by this ImageView. - * - * @return a {@link CropType} in use by this ImageView - */ - @CropType - public int getCropType() { - return cropType; - } - - @Override - public boolean delegateSetFrame(boolean setFrameResult) { - if (!imageView.isInEditMode() && cropImage != null && imageView.getDrawable() != null) { - cropImage.computeImageTransformation(); - } - return setFrameResult; - } - - @Override - public void delegateSetImageBitmap(Bitmap bitmap) { - setup(); - } - - @Override - public void delegateSetImageDrawable(Drawable drawable) { - setup(); - } - - @Override - public void delegateSetImageResource(int resId) { - setup(); - } - - private void setup() { - imageView.setScaleType(ImageView.ScaleType.MATRIX); - - if (imageView.getDrawable() != null) { - cropImage = new CropImageFactory().getCropImage(imageView, cropType); - } - } - - private void parseAttributes(AttributeSet attrs) { - final TypedArray a = imageView.getContext().obtainStyledAttributes(attrs, R.styleable.civ_CropImageView); - cropType = a.getInt(R.styleable.civ_CropImageView_civ_crop, CropType.NONE); - a.recycle(); - } -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/API18Image.java b/cropimageview/src/main/java/com/cesards/cropimageview/model/API18Image.java deleted file mode 100644 index aea6db8..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/API18Image.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.cesards.cropimageview.model; - -import android.graphics.Matrix; -import android.widget.ImageView; - -import com.cesards.cropimageview.CropImageView; - -public class API18Image extends CropImage { - - API18Image(ImageView imageView, @CropType int cropType) { - super(imageView, cropType); - } - - @Override - public Matrix getMatrix() { - return imageView.getImageMatrix(); - } -} \ No newline at end of file diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImage.java b/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImage.java deleted file mode 100644 index 6945cdc..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImage.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.cesards.cropimageview.model; - -import android.graphics.Matrix; -import android.graphics.drawable.Drawable; -import android.widget.ImageView; - -import androidx.annotation.NonNull; - -public abstract class CropImage implements Transformation { - - @NonNull final ImageView imageView; - @CropType private final int cropType; - - public CropImage(@NonNull ImageView imageView, @CropType int cropType) { - this.imageView = imageView; - this.cropType = cropType; - } - - public void computeImageTransformation() { - int viewWidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); - int viewHeight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); - - if (cropType != CropType.NONE && viewHeight > 0 && viewWidth > 0) { - Matrix matrix = getMatrix(); - - Drawable drawable = imageView.getDrawable(); - int drawableWidth = drawable.getIntrinsicWidth(); - int drawableHeight = drawable.getIntrinsicHeight(); - - float scaleY = (float) viewHeight / (float) drawableHeight; - float scaleX = (float) viewWidth / (float) drawableWidth; - float scale = scaleX > scaleY ? scaleX : scaleY; - matrix.setScale(scale, scale); // Same as doing matrix.reset() and matrix.preScale(...) - - boolean verticalImageMode = scaleX > scaleY; - - float postDrawableWidth = drawableWidth * scale; - float xTranslation = getXTranslation(cropType, viewWidth, postDrawableWidth, verticalImageMode); - float postDrawabeHeigth = drawableHeight * scale; - float yTranslation = getYTranslation(cropType, viewHeight, postDrawabeHeigth, verticalImageMode); - - matrix.postTranslate(xTranslation, yTranslation); - imageView.setImageMatrix(matrix); - } - } - - private float getYTranslation(@CropType int cropType, - int viewHeight, - float postDrawabeHeigth, - boolean verticalImageMode) { - if (verticalImageMode) { - switch (cropType) { - case CropType.CENTER_BOTTOM: - case CropType.LEFT_BOTTOM: - case CropType.RIGHT_BOTTOM: - return viewHeight - postDrawabeHeigth; - case CropType.LEFT_CENTER: - case CropType.RIGHT_CENTER: - // View in the middle of the screen - return (viewHeight - postDrawabeHeigth) / 2f; - } - } - - // All other cases we don't need to translate - return 0f; - } - - private float getXTranslation(@CropType int cropType, - int viewWidth, - float postDrawableWidth, - boolean verticalImageMode) { - if (!verticalImageMode) { - switch (cropType) { - case CropType.RIGHT_TOP: - case CropType.RIGHT_CENTER: - case CropType.RIGHT_BOTTOM: - return viewWidth - postDrawableWidth; - case CropType.CENTER_TOP: - case CropType.CENTER_BOTTOM: - // View in the middle of the screen - return (viewWidth - postDrawableWidth) / 2f; - } - } - // All other cases we don't need to translate - return 0f; - } -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImageFactory.java b/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImageFactory.java deleted file mode 100644 index ad98a0d..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/CropImageFactory.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.cesards.cropimageview.model; - -import android.os.Build; -import android.widget.ImageView; - -import com.cesards.cropimageview.CropImageView; - -import androidx.annotation.NonNull; - -public class CropImageFactory { - - public CropImage getCropImage(@NonNull ImageView imageView, @CropType int cropType) { - final boolean preApi18 = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2; - return preApi18 ? new PreApi18CropImage(imageView, cropType) : new API18Image(imageView, cropType); - } -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/PreApi18CropImage.java b/cropimageview/src/main/java/com/cesards/cropimageview/model/PreApi18CropImage.java deleted file mode 100644 index 43cf04c..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/PreApi18CropImage.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cesards.cropimageview.model; - -import android.graphics.Matrix; -import android.widget.ImageView; - -import com.cesards.cropimageview.CropImageView; - -public final class PreApi18CropImage extends CropImage { - - private Matrix matrix; - - PreApi18CropImage(final ImageView imageView, final @CropType int cropType) { - super(imageView, cropType); - init(cropType); - } - - private void init(@CropType int cropType) { - if (cropType != CropType.NONE) { - matrix = new Matrix(); - } - } - - @Override - public Matrix getMatrix() { - return matrix == null ? imageView.getImageMatrix() : matrix; - } -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/model/Transformation.java b/cropimageview/src/main/java/com/cesards/cropimageview/model/Transformation.java deleted file mode 100644 index 717ae5c..0000000 --- a/cropimageview/src/main/java/com/cesards/cropimageview/model/Transformation.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cesards.cropimageview.model; - -import android.graphics.Matrix; - -public interface Transformation { - Matrix getMatrix(); -} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/ClipPathManager.java b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/ClipPathManager.java new file mode 100644 index 0000000..20c0e69 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/ClipPathManager.java @@ -0,0 +1,64 @@ +package com.cesards.cropimageview.rounded_corners; + +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; + +import androidx.annotation.Nullable; + +public final class ClipPathManager { + + protected final Path path = new Path(); + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private ClipPathCreator createClipPath = null; + + public ClipPathManager() { + paint.setColor(Color.BLACK); + paint.setStyle(Paint.Style.FILL); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + } + + public Paint getPaint() { + return paint; + } + + public boolean requiresBitmap() { + return createClipPath != null && createClipPath.requiresBitmap(); + } + + @Nullable + public final Path createClipPath(int width, int height) { + if (createClipPath != null) { + return createClipPath.createClipPath(width, height); + } + return null; + } + + public void setClipPathCreator(ClipPathCreator createClipPath) { + this.createClipPath = createClipPath; + } + + public Path createMask(int width, int height) { + return path; + } + + @Nullable + public Path getShadowConvexPath() { + return path; + } + + public void setupClipLayout(int width, int height) { + path.reset(); + final Path clipPath = createClipPath(width, height); + if (clipPath != null) { + path.set(clipPath); + } + } + + public interface ClipPathCreator { + Path createClipPath(int width, int height); + boolean requiresBitmap(); + } +} diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/Corner.java b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/Corner.java new file mode 100644 index 0000000..b8986fc --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/Corner.java @@ -0,0 +1,31 @@ +package com.cesards.cropimageview.rounded_corners; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import androidx.annotation.IntDef; + +import static com.cesards.cropimageview.rounded_corners.Corner.BOTTOM_LEFT; +import static com.cesards.cropimageview.rounded_corners.Corner.BOTTOM_RIGHT; +import static com.cesards.cropimageview.rounded_corners.Corner.NONE; +import static com.cesards.cropimageview.rounded_corners.Corner.TOP_LEFT; +import static com.cesards.cropimageview.rounded_corners.Corner.TOP_RIGHT; + +/** + * Options for cropping the bounds of an image to the bounds of the ImageView. + */ +@Retention(RetentionPolicy.SOURCE) +@IntDef({ + NONE, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, +}) +public @interface Corner { + int NONE = -1; + int TOP_LEFT = 0; + int TOP_RIGHT = 1; + int BOTTOM_LEFT = 2; + int BOTTOM_RIGHT = 3; +} \ No newline at end of file diff --git a/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/RoundedBitmapViewDrawable.java b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/RoundedBitmapViewDrawable.java new file mode 100644 index 0000000..77a85e6 --- /dev/null +++ b/cropimageview/src/main/java/com/cesards/cropimageview/rounded_corners/RoundedBitmapViewDrawable.java @@ -0,0 +1,494 @@ +package com.cesards.cropimageview.rounded_corners; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * shows a bitmap as if it had rounded corners. based on : + * http://rahulswackyworld.blogspot.co.il/2013/04/android-drawables-with-rounded_7.html + * easy alternative from support library: RoundedBitmapDrawableFactory.create( ...) ; + */ +//public class RoundedBitmapViewDrawable extends RoundedBitmapDrawable { +class RoundedBitmapViewDrawable extends Drawable { + + @Override + public void draw(@NonNull Canvas canvas) { + + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + + + + + + // +// private final BitmapShader bitmapShader; +// private final Paint p; +// private final RectF rect; +// private final float borderRadius; +// +// public RoundedBitmapViewDrawable(final Resources resources, final Bitmap bitmap, final float borderRadius) { +// super(resources, bitmap); +// bitmapShader = new BitmapShader(getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); +// final Bitmap b = getBitmap(); +// p = getPaint(); +// p.setAntiAlias(true); +// p.setShader(bitmapShader); +// final int w = b.getWidth(), h = b.getHeight(); +// rect = new RectF(0, 0, w, h); +// this.borderRadius = borderRadius < 0 ? 0.15f * Math.min(w, h) : borderRadius; +// } +// +// @Override +// public void draw(final Canvas canvas) { +// canvas.drawRoundRect(rect, borderRadius, borderRadius, p); +// } +// + + + + +// private static final float DEFAULT_RADIUS = 0f; +// +// private final RectF bounds = new RectF(); +// private final RectF drawableRect = new RectF(); +// private final RectF bitmapRect = new RectF(); +// private final Bitmap bitmap; +// private final Paint bitmapPaint; +// private final int bitmapWidth; +// private final int bitmapHeight; +// private final Matrix shaderMatrix = new Matrix(); +// private final RectF squareCornersRect = new RectF(); +// private Shader.TileMode tileModeX = Shader.TileMode.CLAMP; +// private Shader.TileMode tileModeY = Shader.TileMode.CLAMP; +// private boolean mRebuildShader = true; +// private float cornerRadius = 0f; +// private final boolean[] cornerRadiusRounded = new boolean[] { true, true, true, true }; +// private ImageView.ScaleType scaleType = ImageView.ScaleType.FIT_CENTER; +// +// public RoundedBitmapViewDrawable(Bitmap bitmap) { +// this.bitmap = bitmap; +// +// bitmapWidth = bitmap.getWidth(); +// bitmapHeight = bitmap.getHeight(); +// bitmapRect.set(0, 0, bitmapWidth, bitmapHeight); +// +// bitmapPaint = new Paint(); +// bitmapPaint.setStyle(Paint.Style.FILL); +// bitmapPaint.setAntiAlias(true); +// } +// +// public static Drawable fromDrawable(Drawable drawable) { +// if (drawable != null) { +// if (drawable instanceof RoundedBitmapViewDrawable) { +// // just return if it's already a RoundedBitmapViewDrawable +// return drawable; +// } +// +// // try to get a bitmap from the drawable and +// Bitmap bm = drawableToBitmap(drawable); +// if (bm != null) { +// return new RoundedBitmapViewDrawable(bm); +// } +// } +// return drawable; +// } +// +// public static RoundedBitmapViewDrawable fromBitmap(Bitmap bitmap) { +// if (bitmap != null) { +// return new RoundedBitmapViewDrawable(bitmap); +// } else { +// return null; +// } +// } +// +// public static Bitmap drawableToBitmap(Drawable drawable) { +// if (drawable instanceof BitmapDrawable) { +// return ((BitmapDrawable) drawable).getBitmap(); +// } +// +// Bitmap bitmap; +// int width = Math.max(drawable.getIntrinsicWidth(), 2); +// int height = Math.max(drawable.getIntrinsicHeight(), 2); +// try { +// bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); +// Canvas canvas = new Canvas(bitmap); +// drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); +// drawable.draw(canvas); +// } catch (Exception e) { +// e.printStackTrace(); +// Log.w(RoundedBitmapViewDrawable.class.getSimpleName(), "Failed to create bitmap from drawable!"); +// bitmap = null; +// } +// +// return bitmap; +// } +// +// +// +// public Bitmap getSourceBitmap() { +// return bitmap; +// } +// +// /** +// * @return the corner radius. +// */ +// public float getCornerRadius() { +// return cornerRadius; +// } +// +// /** +// * @param corner the specific corner to get radius of. +// * @return the corner radius of the specified corner. +// */ +// public float getCornerRadius(@Corner int corner) { +// return cornerRadiusRounded[corner] ? cornerRadius : 0f; +// } +// +// /** +// * Sets all corners to the specified radius. +// * +// * @param radius the radius. +// * @return the {@link RoundedBitmapViewDrawable} for chaining. +// */ +// public RoundedBitmapViewDrawable setCornerRadius(float radius) { +// setCornerRadius(radius, radius, radius, radius); +// return this; +// } +// +// /** +// * Sets the corner radius of one specific corner. +// * +// * @param corner the corner. +// * @param radius the radius. +// * @return the {@link RoundedBitmapViewDrawable} for chaining. +// */ +// public RoundedBitmapViewDrawable setCornerRadius(@Corner int corner, float radius) { +// if (radius != 0 && cornerRadius != 0 && cornerRadius != radius) { +// throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); +// } +// +// if (radius == 0) { +// if (only(corner, cornerRadiusRounded)) { +// cornerRadius = 0; +// } +// cornerRadiusRounded[corner] = false; +// } else { +// if (cornerRadius == 0) { +// cornerRadius = radius; +// } +// cornerRadiusRounded[corner] = true; +// } +// +// return this; +// } +// +// /** +// * Sets the corner radii of all the corners. +// * +// * @param topLeft top left corner radius. +// * @param topRight top right corner radius +// * @param bottomRight bototm right corner radius. +// * @param bottomLeft bottom left corner radius. +// * @return the {@link RoundedBitmapViewDrawable} for chaining. +// */ +// public RoundedBitmapViewDrawable setCornerRadius( +// float topLeft, +// float topRight, +// float bottomRight, +// float bottomLeft +// ) { +// Set radiusSet = new HashSet<>(4); +// radiusSet.add(topLeft); +// radiusSet.add(topRight); +// radiusSet.add(bottomRight); +// radiusSet.add(bottomLeft); +// +// radiusSet.remove(0f); +// +// if (radiusSet.size() > 1) { +// throw new IllegalArgumentException("Multiple nonzero corner radii not yet supported."); +// } +// +// if (!radiusSet.isEmpty()) { +// float radius = radiusSet.iterator().next(); +// if (Float.isInfinite(radius) || Float.isNaN(radius) || radius < 0) { +// throw new IllegalArgumentException("Invalid radius value: " + radius); +// } +// cornerRadius = radius; +// } else { +// cornerRadius = 0f; +// } +// +// cornerRadiusRounded[Corner.TOP_LEFT] = topLeft > 0; +// cornerRadiusRounded[Corner.TOP_RIGHT] = topRight > 0; +// cornerRadiusRounded[Corner.BOTTOM_RIGHT] = bottomRight > 0; +// cornerRadiusRounded[Corner.BOTTOM_LEFT] = bottomLeft > 0; +// return this; +// } +// +// public ImageView.ScaleType getScaleType() { +// return scaleType; +// } +// +// public RoundedBitmapViewDrawable setScaleType(ImageView.ScaleType scaleType) { +// if (scaleType == null) { +// scaleType = ImageView.ScaleType.FIT_CENTER; +// } +// if (this.scaleType != scaleType) { +// this.scaleType = scaleType; +// updateShaderMatrix(); +// } +// return this; +// } +// +// public Shader.TileMode getTileModeX() { +// return tileModeX; +// } +// +// public RoundedBitmapViewDrawable setTileModeX(Shader.TileMode tileModeX) { +// if (this.tileModeX != tileModeX) { +// this.tileModeX = tileModeX; +// mRebuildShader = true; +// invalidateSelf(); +// } +// return this; +// } +// +// public Shader.TileMode getTileModeY() { +// return tileModeY; +// } +// +// public RoundedBitmapViewDrawable setTileModeY(Shader.TileMode tileModeY) { +// if (this.tileModeY != tileModeY) { +// this.tileModeY = tileModeY; +// mRebuildShader = true; +// invalidateSelf(); +// } +// return this; +// } +// +// public Bitmap toBitmap() { +// return drawableToBitmap(this); +// } +// +// @Override +// protected void onBoundsChange(@NonNull Rect bounds) { +// super.onBoundsChange(bounds); +// +// bounds.set(bounds); +// +// updateShaderMatrix(); +// } +// +// @Override +// public void draw(@NonNull Canvas canvas) { +// +// } +// +// // @Override +//// public void draw(@NonNull Canvas canvas) { +////// bitmapPaint.setShader(mShader); +////// canvas.drawRoundRect(drawableRect, cornerRadius, cornerRadius, bitmapPaint); +//// if (mRebuildShader) { +//// BitmapShader bitmapShader = new BitmapShader(bitmap, tileModeX, tileModeY); +//// if (tileModeX == Shader.TileMode.CLAMP && tileModeY == Shader.TileMode.CLAMP) { +//// bitmapShader.setLocalMatrix(shaderMatrix); +//// } +//// bitmapPaint.setShader(bitmapShader); +//// mRebuildShader = false; +//// } +//// +//// if (any(cornerRadiusRounded)) { +//// float radius = cornerRadius; +//// canvas.drawRoundRect(drawableRect, radius, radius, bitmapPaint); +//// redrawBitmapForSquareCorners(canvas); +//// } else { +//// canvas.drawRect(drawableRect, bitmapPaint); +//// } +//// } +// +// @Override +// public int getAlpha() { +// return bitmapPaint.getAlpha(); +// } +// +// @Override +// public void setAlpha(int alpha) { +// bitmapPaint.setAlpha(alpha); +// invalidateSelf(); +// } +// +// @Override +// public ColorFilter getColorFilter() { +// return bitmapPaint.getColorFilter(); +// } +// +// @Override +// public void setColorFilter(ColorFilter cf) { +// bitmapPaint.setColorFilter(cf); +// invalidateSelf(); +// } +// +// @Override +// public void setDither(boolean dither) { +// bitmapPaint.setDither(dither); +// invalidateSelf(); +// } +// +// @Override +// public void setFilterBitmap(boolean filter) { +// bitmapPaint.setFilterBitmap(filter); +// invalidateSelf(); +// } +// +// @Override +// public int getIntrinsicWidth() { +// return bitmapWidth; +// } +// +// @Override +// public int getIntrinsicHeight() { +// return bitmapHeight; +// } +// +// private void updateShaderMatrix() { +// float scale; +// float dx; +// float dy; +// +// switch (scaleType) { +// case CENTER: +// shaderMatrix.reset(); +// shaderMatrix.setTranslate(( bitmapWidth * 0.5f + 0.5f), bitmapHeight * 0.5f + 0.5f); +// break; +// +// case CENTER_CROP: +// break; +// +// case CENTER_INSIDE: +// shaderMatrix.reset(); +// +// if (bitmapWidth <= bounds.width() && bitmapHeight <= bounds.height()) { +// scale = 1.0f; +// } else { +// scale = Math.min(bounds.width() / (float) bitmapWidth, bounds.height() / (float) bitmapHeight); +// } +// +// dx = (int) ((bounds.width() - bitmapWidth * scale) * 0.5f + 0.5f); +// dy = (int) ((bounds.height() - bitmapHeight * scale) * 0.5f + 0.5f); +// +// shaderMatrix.setScale(scale, scale); +// shaderMatrix.postTranslate(dx, dy); +// break; +// +// default: +// case FIT_CENTER: +// shaderMatrix.setRectToRect(bitmapRect, bounds, Matrix.ScaleToFit.CENTER); +// break; +// +// case FIT_END: +// shaderMatrix.setRectToRect(bitmapRect, bounds, Matrix.ScaleToFit.END); +// break; +// +// case FIT_START: +// shaderMatrix.setRectToRect(bitmapRect, bounds, Matrix.ScaleToFit.START); +// break; +// +// case FIT_XY: +// shaderMatrix.reset(); +// break; +// } +// +// mRebuildShader = true; +// } +// +// private void redrawBitmapForSquareCorners(Canvas canvas) { +// if (all(cornerRadiusRounded)) { +// // no square corners +// return; +// } +// +// if (cornerRadius == 0) { +// return; // no round corners +// } +// +// float left = drawableRect.left; +// float top = drawableRect.top; +// float right = left + drawableRect.width(); +// float bottom = top + drawableRect.height(); +// float radius = cornerRadius; +// +// if (!cornerRadiusRounded[Corner.TOP_LEFT]) { +// squareCornersRect.set(left, top, left + radius, top + radius); +// canvas.drawRect(squareCornersRect, bitmapPaint ); +// } +// +// if (!cornerRadiusRounded[Corner.TOP_RIGHT]) { +// squareCornersRect.set(right - radius, top, right, radius); +// canvas.drawRect(squareCornersRect, bitmapPaint ); +// } +// +// if (!cornerRadiusRounded[Corner.BOTTOM_RIGHT]) { +// squareCornersRect.set(right - radius, bottom - radius, right, bottom); +// canvas.drawRect(squareCornersRect, bitmapPaint ); +// } +// +// if (!cornerRadiusRounded[Corner.BOTTOM_LEFT]) { +// squareCornersRect.set(left, bottom - radius, left + radius, bottom); +// canvas.drawRect(squareCornersRect, bitmapPaint ); +// } +// } +// +// private static boolean only(int index, boolean[] booleans) { +// for (int i = 0, len = booleans.length; i < len; i++) { +// if (booleans[i] != (i == index)) { +// return false; +// } +// } +// return true; +// } +// +// private static boolean any(boolean[] booleans) { +// for (boolean b : booleans) { +// if (b) { return true; } +// } +// return false; +// } +// +// private static boolean all(boolean[] booleans) { +// for (boolean b : booleans) { +// if (b) { return false; } +// } +// return true; +// } +} diff --git a/cropimageview/src/main/res/values/attrs.xml b/cropimageview/src/main/res/values/attrs.xml index 4f4c816..000fbfd 100644 --- a/cropimageview/src/main/res/values/attrs.xml +++ b/cropimageview/src/main/res/values/attrs.xml @@ -11,5 +11,11 @@ + + + + + + \ No newline at end of file