/*
* Copyright (C) 2015 Thomas Robert Altstidl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tr4android.support.extension.widget;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.animation.Interpolator;
import com.tr4android.support.extension.animation.AnimationUtils;
public class CollapsingDrawableHelper {
private final View mView;
private boolean mDrawIcon;
private float mExpandedFraction;
private final Rect mExpandedBounds;
private final Rect mCollapsedBounds;
private final RectF mCurrentBounds;
private float mExpandedIconSize;
private float mCollapsedIconSize;
private Drawable mDrawable;
private Interpolator mIconSizeInterpolator;
private Interpolator mPositionInterpolator;
public CollapsingDrawableHelper(View view) {
mView = view;
mCollapsedBounds = new Rect();
mExpandedBounds = new Rect();
mCurrentBounds = new RectF();
}
void setIconSizeInterpolator(Interpolator interpolator) {
mIconSizeInterpolator = interpolator;
recalculate();
}
void setExpandedIconSize(float iconSize) {
if (mExpandedIconSize != iconSize) {
mExpandedIconSize = iconSize;
recalculate();
}
}
void setCollapsedIconSize(float iconSize) {
if (mCollapsedIconSize != iconSize) {
mCollapsedIconSize = iconSize;
recalculate();
}
}
void setExpandedBounds(int left, int top, int right, int bottom) {
if (!rectEquals(mExpandedBounds, left, top, right, bottom)) {
mExpandedBounds.set(left, top, right, bottom);
onBoundsChanged();
}
}
void setCollapsedBounds(int left, int top, int right, int bottom) {
if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) {
mCollapsedBounds.set(left, top, right, bottom);
onBoundsChanged();
}
}
void onBoundsChanged() {
mDrawIcon = mCollapsedBounds.width() > 0 && mCollapsedBounds.height() > 0
&& mExpandedBounds.width() > 0 && mExpandedBounds.height() > 0;
}
/**
* Set the value indicating the current scroll value. This decides how much of the
* background will be displayed, as well as the title metrics/positioning.
*
* A value of {@code 0.0} indicates that the layout is fully expanded.
* A value of {@code 1.0} indicates that the layout is fully collapsed.
*/
void setExpansionFraction(float fraction) {
fraction = constrain(fraction, 0f, 1f);
if (fraction != mExpandedFraction) {
mExpandedFraction = fraction;
calculateCurrentOffsets();
}
}
float getExpansionFraction() {
return mExpandedFraction;
}
float getCollapsedIconSize() {
return mCollapsedIconSize;
}
float getExpandedIconSize() {
return mExpandedIconSize;
}
private void calculateCurrentOffsets() {
calculateOffsets(mExpandedFraction);
}
private void calculateOffsets(final float fraction) {
interpolateBounds(fraction);
if (mDrawable != null) {
// Set new bounds for the drawable
mDrawable.setBounds(Math.round(mCurrentBounds.left), Math.round(mCurrentBounds.top),
Math.round(mCurrentBounds.right), Math.round(mCurrentBounds.bottom));
}
ViewCompat.postInvalidateOnAnimation(mView);
}
private void interpolateBounds(float fraction) {
mCurrentBounds.left = lerp(mExpandedBounds.left, mCollapsedBounds.left,
fraction, mPositionInterpolator);
mCurrentBounds.top = lerp(mExpandedBounds.top, mCollapsedBounds.top,
fraction, mPositionInterpolator);
mCurrentBounds.right = lerp(mExpandedBounds.right, mCollapsedBounds.right,
fraction, mPositionInterpolator);
mCurrentBounds.bottom = lerp(mExpandedBounds.bottom, mCollapsedBounds.bottom,
fraction, mPositionInterpolator);
}
public void draw(Canvas canvas) {
final int saveCount = canvas.save();
if (mDrawable != null && mDrawIcon) {
// Let the drawable draw to the canvas (bounds are already set)
mDrawable.draw(canvas);
}
canvas.restoreToCount(saveCount);
}
public void recalculate() {
if (mView.getHeight() > 0 && mView.getWidth() > 0) {
// If we've already been laid out, calculate everything now otherwise we'll wait
// until a layout
calculateCurrentOffsets();
}
}
/**
* Set the drawable to display
*
* @param drawable
*/
void setDrawable(Drawable drawable) {
if (drawable == null || !drawable.equals(mDrawable)) {
mDrawable = drawable;
recalculate();
}
}
Drawable getDrawable() {
return mDrawable;
}
private static float lerp(float startValue, float endValue, float fraction,
Interpolator interpolator) {
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
return AnimationUtils.lerp(startValue, endValue, fraction);
}
private static boolean rectEquals(Rect r, int left, int top, int right, int bottom) {
return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom);
}
private static float constrain(float amount, float low, float high) {
return amount < low ? low : (amount > high ? high : amount);
}
}