/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.widget; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; import org.mozilla.gecko.widget.themed.ThemedImageView; /** * An ImageView which will always display at the given width and calculated height (based on the width and * the supplied aspect ratio), drawn starting from the top left hand corner. A supplied drawable will be resized to fit * the width of the view; if the resized drawable is too tall for the view then the drawable will be cropped at the * bottom, however if the resized drawable is too short for the view to display whilst honouring it's given width and * height then the drawable will be displayed at full height with the right hand side cropped. */ public abstract class CropImageView extends ThemedImageView { public static final String LOGTAG = "Gecko" + CropImageView.class.getSimpleName(); private int viewWidth; private int viewHeight; private int drawableWidth; private int drawableHeight; private boolean resize = true; private Matrix layoutCurrentMatrix = new Matrix(); private Matrix layoutNextMatrix = new Matrix(); public CropImageView(final Context context) { this(context, null); } public CropImageView(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } public CropImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } protected abstract float getAspectRatio(); protected void init() { // Setting the pivots means that the image will be drawn from the top left hand corner. There are // issues in Android 4.1 (16) which mean setting these values to 0 may not work. // http://stackoverflow.com/questions/26658124/setpivotx-doesnt-work-on-android-4-1-1-nineoldandroids setPivotX(1); setPivotY(1); } /** * Measure the view to determine the measured width and height. * The height is constrained by the measured width. * * @param widthMeasureSpec horizontal space requirements as imposed by the parent. * @param heightMeasureSpec vertical space requirements as imposed by the parent, but ignored. */ @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { // Default measuring. super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Force the height based on the aspect ratio. viewWidth = getMeasuredWidth(); viewHeight = (int) (viewWidth * getAspectRatio()); setMeasuredDimension(viewWidth, viewHeight); updateImageMatrix(); } protected void updateImageMatrix() { if (!resize || getDrawable() == null) { return; } setScaleType(ImageView.ScaleType.MATRIX); getDrawable().setBounds(0, 0, viewWidth, viewHeight); final float horizontalScaleValue = (float) viewWidth / (float) drawableWidth; final float verticalScaleValue = (float) viewHeight / (float) drawableHeight; final float scale = Math.max(verticalScaleValue, horizontalScaleValue); layoutNextMatrix.reset(); layoutNextMatrix.setScale(scale, scale); setImageMatrix(layoutNextMatrix); // You can't modify the matrix in place and we want to avoid allocation, so let's keep two references to two // different matrix objects that we can swap when the values need to change final Matrix swapReferenceMatrix = layoutCurrentMatrix; layoutCurrentMatrix = layoutNextMatrix; layoutNextMatrix = swapReferenceMatrix; } public void setImageBitmap(final Bitmap bm, final boolean resize) { super.setImageBitmap(bm); this.resize = resize; updateImageMatrix(); } @Override public void setImageResource(final int resId) { super.setImageResource(resId); setImageMatrix(null); resize = false; } @Override public void setImageDrawable(final Drawable drawable) { this.setImageDrawable(drawable, false); } public void setImageDrawable(final Drawable drawable, final boolean resize) { super.setImageDrawable(drawable); if (drawable != null) { // Reset the matrix to ensure that any previous changes aren't carried through. setImageMatrix(null); drawableWidth = drawable.getIntrinsicWidth(); drawableHeight = drawable.getIntrinsicHeight(); } else { drawableWidth = -1; drawableHeight = -1; } this.resize = resize; updateImageMatrix(); } }