-
-
Save arriolac/3843346 to your computer and use it in GitHub Desktop.
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.graphics.Matrix; | |
import android.graphics.drawable.Drawable; | |
import android.os.Build; | |
import android.util.AttributeSet; | |
import android.widget.ImageView; | |
/** | |
* Created by chris on 7/27/16. | |
*/ | |
public class TopCropImageView extends ImageView { | |
public TopCropImageView(Context context) { | |
super(context); | |
init(); | |
} | |
public TopCropImageView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
public TopCropImageView(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(); | |
} | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
public TopCropImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(); | |
} | |
@Override | |
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { | |
super.onLayout(changed, left, top, right, bottom); | |
recomputeImgMatrix(); | |
} | |
@Override | |
protected boolean setFrame(int l, int t, int r, int b) { | |
recomputeImgMatrix(); | |
return super.setFrame(l, t, r, b); | |
} | |
private void init() { | |
setScaleType(ScaleType.MATRIX); | |
} | |
private void recomputeImgMatrix() { | |
final Drawable drawable = getDrawable(); | |
if (drawable == null) { | |
return; | |
} | |
final Matrix matrix = getImageMatrix(); | |
float scale; | |
final int viewWidth = getWidth() - getPaddingLeft() - getPaddingRight(); | |
final int viewHeight = getHeight() - getPaddingTop() - getPaddingBottom(); | |
final int drawableWidth = drawable.getIntrinsicWidth(); | |
final int drawableHeight = drawable.getIntrinsicHeight(); | |
if (drawableWidth * viewHeight > drawableHeight * viewWidth) { | |
scale = (float) viewHeight / (float) drawableHeight; | |
} else { | |
scale = (float) viewWidth / (float) drawableWidth; | |
} | |
matrix.setScale(scale, scale); | |
setImageMatrix(matrix); | |
} | |
} |
please add this method on code
@Override protected void onDraw(Canvas canvas) { recomputeImgMatrix(); super.onDraw(canvas); }
Thanks @Dishant624 for your code
You save my day :)
@Dishant624 - could you elaborate why? It would be much more costly in terms of performance.
Also this was enough for me:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
recomputeImgMatrix();
}
Hence no need for overriding setFrame
method. Also, setFrame
is supported ONLY for two View
subclasses: https://stackoverflow.com/questions/4751963/android-why-cant-i-override-setframe-from-view
In Kotlin, I got succeeded with MotionLayout
with:
class BottomCenterImageView : AppCompatImageView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
init {
scaleType = ScaleType.MATRIX
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
recomputeImageMatrix()
}
private fun recomputeImageMatrix() {
val drawable = drawable ?: return
val viewWidth = width - paddingLeft - paddingRight
val viewHeight = height - paddingTop - paddingBottom
val drawableWidth = drawable.intrinsicWidth
val drawableHeight = drawable.intrinsicHeight
imageMatrix = imageMatrix.apply {
setTranslate(
Math.round((viewWidth - drawableWidth) * 0.5f).toFloat(),
Math.round((viewHeight - drawableHeight).toFloat()).toFloat()
)
}
}
}
Here is a Kolin version:
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
class TopCropImageView : AppCompatImageView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)
init {
scaleType = ScaleType.MATRIX
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
recomputeImgMatrix()
}
override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
recomputeImgMatrix()
return super.setFrame(l, t, r, b)
}
private fun recomputeImgMatrix() {
val matrix = imageMatrix
val viewWidth = width - paddingLeft - paddingRight
val viewHeight = height - paddingTop - paddingBottom
val drawableWidth = drawable.intrinsicWidth
val drawableHeight = drawable.intrinsicHeight
val scale = if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
viewHeight.toFloat() / drawableHeight.toFloat()
} else {
viewWidth.toFloat() / drawableWidth.toFloat()
}
matrix.setScale(scale, scale)
imageMatrix = matrix
}
}
And here a version adapted to allow you to set the alignment in the range (0.0, 0.0)
= top left to (1.0, 1.0)
= (bottom right) either via code or XML attributes:
open class AlignmentCropImageView : AppCompatImageView {
constructor(context: Context) : super(context) {
initAttrs(context, null, 0)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initAttrs(context, attrs, 0)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
initAttrs(context, attrs, defStyleAttr)
}
open var alignmentX = 0.5f
open var alignmentY = 0.5f
private fun initAttrs(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
scaleType = ScaleType.MATRIX
context.obtainStyledAttributes(
attrs,
R.styleable.AlignmentCropImageView,
defStyleAttr,
0
).apply {
alignmentX = getFloat(R.styleable.AlignmentCropImageView_alignmentX, alignmentX)
alignmentY = getFloat(R.styleable.AlignmentCropImageView_alignmentX, alignmentY)
}.recycle()
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
recomputeImgMatrix()
}
override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
recomputeImgMatrix()
return super.setFrame(l, t, r, b)
}
private fun recomputeImgMatrix() {
val matrix = imageMatrix
val viewWidth = width - paddingLeft - paddingRight
val viewHeight = height - paddingTop - paddingBottom
val drawableWidth = drawable?.intrinsicWidth ?: 0
val drawableHeight = drawable?.intrinsicHeight ?: 0
val scale = if (drawableWidth * viewHeight > drawableHeight * viewWidth) {
viewHeight.toFloat() / drawableHeight.toFloat()
} else {
viewWidth.toFloat() / drawableWidth.toFloat()
}
matrix.setScale(scale, scale)
matrix.postTranslate(
(viewWidth - drawableWidth * scale) * alignmentX,
(viewHeight - drawableHeight * scale) * alignmentY
)
imageMatrix = matrix
}
}
attrs.xml
:
<resources>
<declare-styleable name="AlignmentCropImageView">
<attr name="alignmentX" format="float" />
<attr name="alignmentY" format="float" />
</declare-styleable>
</resources>
This is for the top.
What if I want to make sure a specific point/rectangle is shown while fitting&cropping, keeping the aspect ratio?
How can I do it?
Currently the only similar thing that officially exists is center-crop, but it's only to the center. What if the most important part in the image is at the bottom, instead? Or 10% (or 10px) from the bottom, etc... ? Or if there is a specific region in the bitmap that's most important?
Image not loading after swipe to refresh layout in glide library.
Please give some feedback.
`public class TopCropImageView extends android.support.v7.widget.AppCompatImageView {
}
`