package org.solovyev.android.views; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.TextView; import static android.graphics.Matrix.MSCALE_Y; public class Adjuster { private static final float[] MATRIX = new float[9]; @NonNull private static Helper<TextView> textViewHelper = new Helper<TextView>() { @Override public void apply(@NonNull TextView view, float textSize) { view.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); } @Override public float getTextSize(@NonNull TextView view) { return view.getTextSize(); } }; public static void adjustText(@NonNull final TextView view, final float percentage) { adjustText(view, textViewHelper, percentage, 0); } public static void adjustText(@NonNull final TextView view, final float percentage, int minTextSizePxs) { adjustText(view, textViewHelper, percentage, minTextSizePxs); } public static <V extends View> void adjustText(@NonNull final V view, @NonNull Helper<V> helper, final float percentage, final float minTextSizePxs) { ViewTreeObserver treeObserver = getTreeObserver(view); if (treeObserver == null) { return; } treeObserver.addOnPreDrawListener(new TextViewAdjuster<V>(view, helper, percentage, minTextSizePxs)); } @Nullable public static ViewTreeObserver getTreeObserver(@NonNull View view) { final ViewTreeObserver treeObserver = view.getViewTreeObserver(); if (treeObserver == null) { return null; } if (!treeObserver.isAlive()) { return null; } return treeObserver; } public static void adjustImage(@NonNull final ImageView view, final float percentage) { final ViewTreeObserver treeObserver = getTreeObserver(view); if (treeObserver == null) { return; } treeObserver.addOnPreDrawListener(new ImageViewAdjuster(view, percentage)); } public static void maxWidth(@NonNull View view, int maxWidth) { final ViewTreeObserver treeObserver = getTreeObserver(view); if (treeObserver == null) { return; } treeObserver.addOnPreDrawListener(new MaxWidthAdjuster(view, maxWidth)); } public interface Helper<V extends View> { void apply(@NonNull V view, float textSize); float getTextSize(@NonNull V view); } private static abstract class BaseViewAdjuster<V extends View> implements ViewTreeObserver.OnPreDrawListener { @NonNull protected final V view; protected final boolean oneShot; private int usedWidth; private int usedHeight; protected BaseViewAdjuster(@NonNull V view, boolean oneShot) { this.view = view; this.oneShot = oneShot; } @Override public final boolean onPreDraw() { final int width = view.getWidth(); final int height = view.getHeight(); if (!ViewCompat.isLaidOut(view) || height <= 0 || width <= 0) { return true; } if (usedWidth == width && usedHeight == height) { return true; } usedWidth = width; usedHeight = height; if (oneShot) { final ViewTreeObserver treeObserver = getTreeObserver(view); if (treeObserver != null) { treeObserver.removeOnPreDrawListener(this); } } return adjust(width, height); } protected abstract boolean adjust(int width, int height); } private static class TextViewAdjuster<V extends View> extends BaseViewAdjuster<V> { private final float percentage; private final float minTextSizePxs; private final Helper<V> helper; public TextViewAdjuster(@NonNull V view, @NonNull Helper<V> helper, float percentage, float minTextSizePxs) { super(view, true); this.helper = helper; this.percentage = percentage; this.minTextSizePxs = minTextSizePxs; } @Override protected boolean adjust(int width, int height) { final float oldTextSize = Math.round(helper.getTextSize(view)); final float newTextSize = Math.max(minTextSizePxs, Math.round(height * percentage)); if (oldTextSize == newTextSize) { return true; } helper.apply(view, newTextSize); return false; } } private static class MaxWidthAdjuster extends BaseViewAdjuster<View> { private final int maxWidth; public MaxWidthAdjuster(@NonNull View view, int maxWidth) { super(view, true); this.maxWidth = maxWidth; } @Override protected boolean adjust(int width, int height) { if (width <= maxWidth) { return true; } final ViewGroup.LayoutParams lp = view.getLayoutParams(); lp.width = maxWidth; view.setLayoutParams(lp); return false; } } private static class ImageViewAdjuster extends BaseViewAdjuster<ImageView> { private final float percentage; public ImageViewAdjuster(@NonNull ImageView view, float percentage) { super(view, false); this.percentage = percentage; } @Override protected boolean adjust(int width, int height) { final Drawable d = view.getDrawable(); if (d == null) { return true; } view.getImageMatrix().getValues(MATRIX); final int oldImageHeight = Math.round(d.getIntrinsicHeight() * MATRIX[MSCALE_Y]); final int newImageHeight = Math.round(height * percentage); if (oldImageHeight == newImageHeight) { return true; } final int newPaddings = Math.max(0, height - newImageHeight) / 2; view.setPadding(0, newPaddings, 0, newPaddings); return false; } } }