Use a new method of transition view that does not need measures.

filtering
Floens 10 years ago
parent 8036a42eba
commit 463b7e5291
  1. 11
      Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java
  2. 14
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  3. 195
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
  4. 20
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  5. 9
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  6. 24
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java
  7. 41
      Clover/app/src/main/java/org/floens/chan/ui/view/ClippingImageView.java
  8. 8
      Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java
  9. 117
      Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java
  10. 64
      Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java
  11. 2
      Clover/app/src/main/res/layout/controller_image_viewer.xml

@ -45,7 +45,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
public void onViewMeasured() {
// Pager is measured, but still invisible
callback.startPreviewInTransition();
callback.startPreviewInTransition(images.get(selectedPosition));
callback.setTitle(images.get(selectedPosition));
}
@ -64,7 +64,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
callback.setPagerVisiblity(false);
callback.setPreviewVisibility(true);
callback.startPreviewOutTransition();
callback.startPreviewOutTransition(images.get(selectedPosition));
}
@Override
@ -119,6 +119,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
private void onPageSwipedTo(int position) {
callback.setTitle(images.get(selectedPosition));
callback.scrollTo(images.get(selectedPosition));
for (PostImage other : getOther(position, false)) {
callback.setImageMode(other, MultiImageView.Mode.LOWRES);
@ -186,9 +187,9 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
}
public interface Callback {
public void startPreviewInTransition();
public void startPreviewInTransition(PostImage postImage);
public void startPreviewOutTransition();
public void startPreviewOutTransition(PostImage postImage);
public void setPreviewVisibility(boolean visible);
@ -200,6 +201,8 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
public void setTitle(PostImage postImage);
public void scrollTo(PostImage postImage);
public MultiImageView.Mode getImageMode(PostImage postImage);
}
}

@ -119,7 +119,19 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
@Override
public void scrollTo(int position) {
threadPresenterCallback.scrollTo(position);
}
public void scrollTo(PostImage postImage) {
int position = -1;
for (int i = 0; i < chanLoader.getThread().posts.size(); i++) {
Post post = chanLoader.getThread().posts.get(i);
if (post.hasImage && post.imageUrl.equals(postImage.imageUrl)) {
position = i;
break;
}
}
scrollTo(position);
}
/*
@ -328,5 +340,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
public void showPostsPopup(Post forPost, List<Post> posts);
public void showImages(List<PostImage> images, int index, ImageView thumbnail);
public void scrollTo(int position);
}
}

@ -7,35 +7,39 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.presenter.ImageViewerPresenter;
import org.floens.chan.ui.adapter.ImageViewerAdapter;
import org.floens.chan.ui.toolbar.Toolbar;
import org.floens.chan.ui.view.ClippingImageView;
import org.floens.chan.ui.view.OptionalSwipeViewPager;
import org.floens.chan.ui.view.MultiImageView;
import org.floens.chan.ui.view.OptionalSwipeViewPager;
import org.floens.chan.ui.view.TransitionImageView;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.AnimationUtils;
import java.util.List;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation;
public class ImageViewerController extends Controller implements View.OnClickListener, ImageViewerPresenter.Callback {
private static final int TRANSITION_DURATION = 200; //165;
private static final int TRANSITION_CLIP_DURATION = (int) (TRANSITION_DURATION * 0.5f);
private static final String TAG = "ImageViewerController";
private static final int TRANSITION_DURATION = 200;
private static final float TRANSITION_FINAL_ALPHA = 0.80f;
private int statusBarColorPrevious;
@ -46,7 +50,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
private ImageViewerPresenter presenter;
private final Toolbar toolbar;
private ClippingImageView previewImage;
private TransitionImageView previewImage;
private OptionalSwipeViewPager pager;
public ImageViewerController(Context context, Toolbar toolbar) {
@ -62,7 +66,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
view = inflateRes(R.layout.controller_image_viewer);
view.setOnClickListener(this);
previewImage = (ClippingImageView) view.findViewById(R.id.preview_image);
previewImage = (TransitionImageView) view.findViewById(R.id.preview_image);
pager = (OptionalSwipeViewPager) view.findViewById(R.id.pager);
pager.setOnPageChangeListener(presenter);
@ -95,7 +99,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
}
public void setPreviewVisibility(boolean visible) {
previewImage.setVisibility(visible ? View.VISIBLE : View.GONE);
previewImage.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
public void setPagerVisiblity(boolean visible) {
@ -122,69 +126,34 @@ public class ImageViewerController extends Controller implements View.OnClickLis
toolbar.setNavigationItem(false, false, navigationItem);
}
public void startPreviewInTransition() {
ImageView previewImageView = previewCallback.getPreviewImageStartView(this);
previewImage.setImageDrawable(previewImageView.getDrawable());
Rect startBounds = getImageViewBounds(previewImageView);
final Rect endBounds = new Rect();
final Point globalOffset = new Point();
view.getGlobalVisibleRect(endBounds, globalOffset);
float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset);
public void scrollTo(PostImage postImage) {
previewCallback.scrollTo(postImage);
}
previewImage.setPivotX(0f);
previewImage.setPivotY(0f);
previewImage.setX(startBounds.left);
previewImage.setY(startBounds.top);
previewImage.setScaleX(startScale);
previewImage.setScaleY(startScale);
public void startPreviewInTransition(PostImage postImage) {
ImageView startImageView = getTransitionImageView(postImage);
Rect clipStartBounds = new Rect(0, 0, (int) (previewImageView.getWidth() / startScale), (int) (previewImageView.getHeight() / startScale));
if (!setTransitionViewData(startImageView)) {
return; // TODO
}
if (Build.VERSION.SDK_INT >= 21) {
statusBarColorPrevious = getWindow().getStatusBarColor();
}
doPreviewInTransition(startBounds, endBounds, startScale, clipStartBounds);
}
public void startPreviewOutTransition() {
if (startPreviewAnimation != null || endAnimation != null) {
return;
}
doPreviewOutAnimation();
}
private void doPreviewInTransition(Rect startBounds, Rect finalBounds, float startScale, final Rect clipStartBounds) {
startPreviewAnimation = new AnimatorSet();
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(0f, 1f);
backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
ValueAnimator progress = ValueAnimator.ofFloat(0f, 1f);
progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setBackgroundAlpha((float) animation.getAnimatedValue());
previewImage.setProgress((float) animation.getAnimatedValue());
}
});
final Rect clipRect = new Rect();
ValueAnimator clip = ValueAnimator.ofFloat(1f, 0f);
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue());
previewImage.clip(clipRect);
}
});
startPreviewAnimation
.play(ObjectAnimator.ofFloat(previewImage, View.X, startBounds.left, finalBounds.left).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.Y, startBounds.top, finalBounds.top).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_X, startScale, 1f).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_Y, startScale, 1f).setDuration(TRANSITION_DURATION))
.with(backgroundAlpha.setDuration(TRANSITION_DURATION))
.with(clip.setDuration(TRANSITION_CLIP_DURATION));
startPreviewAnimation.play(progress);
startPreviewAnimation.setDuration(TRANSITION_DURATION);
startPreviewAnimation.setInterpolator(new DecelerateInterpolator());
startPreviewAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@ -194,11 +163,6 @@ public class ImageViewerController extends Controller implements View.OnClickLis
@Override
public void onAnimationEnd(Animator animation) {
previewImage.setX(0f);
previewImage.setY(0f);
previewImage.setScaleX(1f);
previewImage.setScaleY(1f);
previewImage.clip(null);
startPreviewAnimation = null;
presenter.onInTransitionEnd();
}
@ -206,13 +170,30 @@ public class ImageViewerController extends Controller implements View.OnClickLis
startPreviewAnimation.start();
}
private void doPreviewOutAnimation() {
ImageView startImage = getStartImageView();
Rect startBounds = null;
if (startImage != null) {
startBounds = getImageViewBounds(startImage);
public void startPreviewOutTransition(final PostImage postImage) {
if (startPreviewAnimation != null || endAnimation != null) {
return;
}
if (startBounds == null) {
// Should definitely be loaded
ChanApplication.getVolleyImageLoader().get(postImage.thumbnailUrl, new ImageLoader.ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "onErrorResponse for preview out transition in ImageViewerController, cannot show correct transition bitmap");
doPreviewOutAnimation(postImage, null);
}
@Override
public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
doPreviewOutAnimation(postImage, response.getBitmap());
}
}, previewImage.getWidth(), previewImage.getHeight());
}
private void doPreviewOutAnimation(PostImage postImage, Bitmap bitmap) {
ImageView startImage = getTransitionImageView(postImage);
if (!setTransitionViewData(startImage) || bitmap == null) {
endAnimation = new AnimatorSet();
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f);
@ -238,44 +219,26 @@ public class ImageViewerController extends Controller implements View.OnClickLis
});
endAnimation.start();
} else {
final Rect endBounds = new Rect();
final Point globalOffset = new Point();
view.getGlobalVisibleRect(endBounds, globalOffset);
float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset);
endAnimation = new AnimatorSet();
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f);
backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
ValueAnimator progress = ValueAnimator.ofFloat(1f, 0f);
progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setBackgroundAlpha((float) animation.getAnimatedValue());
previewImage.setProgress((float) animation.getAnimatedValue());
}
});
final Rect clipStartBounds = new Rect(0, 0, (int) (startImage.getWidth() / startScale), (int) (startImage.getHeight() / startScale));
final Rect clipRect = new Rect();
ValueAnimator clip = ValueAnimator.ofFloat(0f, 1f);
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
endAnimation.play(progress);
endAnimation.setDuration(TRANSITION_DURATION);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue());
previewImage.clip(clipRect);
public void onAnimationStart(Animator animation) {
previewCallback.onPreviewCreate(ImageViewerController.this);
}
});
clip.setStartDelay(TRANSITION_DURATION - TRANSITION_CLIP_DURATION);
clip.setDuration(TRANSITION_CLIP_DURATION);
endAnimation
.play(ObjectAnimator.ofFloat(previewImage, View.X, startBounds.left).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.Y, startBounds.top).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_X, 1f, startScale).setDuration(TRANSITION_DURATION))
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_Y, 1f, startScale).setDuration(TRANSITION_DURATION))
.with(backgroundAlpha.setDuration(TRANSITION_DURATION))
.with(clip);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previewOutAnimationEnded();
@ -292,6 +255,24 @@ public class ImageViewerController extends Controller implements View.OnClickLis
navigationController.stopPresenting(false);
}
private boolean setTransitionViewData(ImageView startView) {
if (startView == null || startView.getWindowToken() == null) {
return false;
}
Bitmap bitmap = ((BitmapDrawable) startView.getDrawable()).getBitmap();
if (bitmap == null) {
return false;
}
int[] loc = new int[2];
startView.getLocationInWindow(loc);
Point windowLocation = new Point(loc[0], loc[1]);
Point size = new Point(startView.getWidth(), startView.getHeight());
previewImage.setSourceImageView(windowLocation, size, bitmap);
return true;
}
private void setBackgroundAlpha(float alpha) {
view.setBackgroundColor(Color.argb((int) (alpha * TRANSITION_FINAL_ALPHA * 255f), 0, 0, 0));
@ -319,22 +300,8 @@ public class ImageViewerController extends Controller implements View.OnClickLis
toolbar.setAlpha(alpha);
}
private Rect getImageViewBounds(ImageView image) {
Rect startBounds = new Rect();
if (image.getGlobalVisibleRect(startBounds) && !startBounds.isEmpty()) {
AnimationUtils.adjustImageViewBoundsToDrawableBounds(image, startBounds);
if (!startBounds.isEmpty()) {
return startBounds;
} else {
return null;
}
} else {
return null;
}
}
private ImageView getStartImageView() {
return previewCallback.getPreviewImageStartView(this);
private ImageView getTransitionImageView(PostImage postImage) {
return previewCallback.getPreviewImageTransitionView(this, postImage);
}
private Window getWindow() {
@ -342,10 +309,12 @@ public class ImageViewerController extends Controller implements View.OnClickLis
}
public interface PreviewCallback {
public ImageView getPreviewImageStartView(ImageViewerController imageViewerController);
public ImageView getPreviewImageTransitionView(ImageViewerController imageViewerController, PostImage postImage);
public void onPreviewCreate(ImageViewerController imageViewerController);
public void onPreviewDestroy(ImageViewerController imageViewerController);
public void scrollTo(PostImage postImage);
}
}

@ -1,7 +1,6 @@
package org.floens.chan.ui.controller;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import org.floens.chan.controller.Controller;
@ -12,7 +11,6 @@ import java.util.List;
public abstract class ThreadController extends Controller implements ThreadLayout.ThreadLayoutCallback, ImageViewerController.PreviewCallback {
protected ThreadLayout threadLayout;
private ImageView presentingImageView;
public ThreadController(Context context) {
super(context);
@ -25,9 +23,7 @@ public abstract class ThreadController extends Controller implements ThreadLayou
@Override
public void showImages(List<PostImage> images, int index, final ImageView thumbnail) {
// Just ignore the showImages request when the image is not loaded
if (thumbnail.getDrawable() != null) {
presentingImageView = thumbnail;
if (thumbnail.getDrawable() != null && thumbnail.getDrawable().getIntrinsicWidth() > 0 && thumbnail.getDrawable().getIntrinsicHeight() > 0) {
final ImageViewerNavigationController imageViewerNavigationController = new ImageViewerNavigationController(context);
presentController(imageViewerNavigationController, false);
imageViewerNavigationController.showImages(images, index, this);
@ -35,17 +31,21 @@ public abstract class ThreadController extends Controller implements ThreadLayou
}
@Override
public ImageView getPreviewImageStartView(ImageViewerController imageViewerController) {
return presentingImageView;
public ImageView getPreviewImageTransitionView(ImageViewerController imageViewerController, PostImage postImage) {
return threadLayout.getThumbnail(postImage);
}
public void onPreviewCreate(ImageViewerController imageViewerController) {
presentingImageView.setVisibility(View.INVISIBLE);
// presentingImageView.setVisibility(View.INVISIBLE);
}
@Override
public void onPreviewDestroy(ImageViewerController imageViewerController) {
presentingImageView.setVisibility(View.VISIBLE);
presentingImageView = null;
// presentingImageView.setVisibility(View.VISIBLE);
// presentingImageView = null;
}
public void scrollTo(PostImage postImage) {
threadLayout.getPresenter().scrollTo(postImage);
}
}

@ -168,6 +168,15 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres
callback.showImages(images, index, thumbnail);
}
@Override
public void scrollTo(int position) {
threadListLayout.scrollTo(position);
}
public ImageView getThumbnail(PostImage postImage) {
return threadListLayout.getThumbnail(postImage);
}
private void switchVisible(boolean visible) {
if (this.visible != visible) {
this.visible = visible;

@ -19,12 +19,16 @@ package org.floens.chan.ui.layout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import com.android.volley.VolleyError;
import org.floens.chan.core.model.ChanThread;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.view.PostView;
@ -93,4 +97,24 @@ public class ThreadListLayout extends RelativeLayout {
public void showError(VolleyError error) {
}
public ImageView getThumbnail(PostImage postImage) {
ImageView thumbnail = null;
for (int i = 0; i < listView.getChildCount(); i++) {
View view = listView.getChildAt(i);
if (view instanceof PostView) {
PostView postView = (PostView) view;
Post post = postView.getPost();
if (post.hasImage && post.imageUrl.equals(postImage.imageUrl)) {
thumbnail = postView.getThumbnail();
break;
}
}
}
return thumbnail;
}
public void scrollTo(int position) {
listView.smoothScrollToPosition(position);
}
}

@ -1,41 +0,0 @@
package org.floens.chan.ui.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.ImageView;
public class ClippingImageView extends ImageView {
private Rect clipRect = new Rect();
public ClippingImageView(Context context) {
super(context);
}
public ClippingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ClippingImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
if (!clipRect.isEmpty() && (clipRect.width() < getWidth() || clipRect.height() < getHeight())) {
canvas.clipRect(clipRect);
}
super.onDraw(canvas);
}
public void clip(Rect rect) {
if (rect == null) {
clipRect.setEmpty();
} else {
clipRect.set(rect);
}
invalidate();
}
}

@ -254,6 +254,14 @@ public class PostView extends LinearLayout implements View.OnClickListener {
}
}
public Post getPost() {
return post;
}
public ImageView getThumbnail() {
return imageView;
}
public void setHighlightQuotesWithNo(int no) {
highlightQuotesNo = no;
}

@ -0,0 +1,117 @@
package org.floens.chan.ui.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class TransitionImageView extends View {
private static final String TAG = "TransitionImageView";
private Bitmap bitmap;
private Matrix matrix = new Matrix();
private Paint paint = new Paint();
private RectF bitmapRect = new RectF();
private RectF destRect = new RectF();
private RectF sourceImageRect = new RectF();
private PointF sourceOverlap = new PointF();
private RectF destClip = new RectF();
private float progress;
public TransitionImageView(Context context) {
super(context);
init();
}
public TransitionImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TransitionImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setSourceImageView(Point windowLocation, Point viewSize, Bitmap bitmap) {
this.bitmap = bitmap;
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
int[] myLoc = new int[2];
getLocationInWindow(myLoc);
float globalOffsetX = windowLocation.x - myLoc[0];
float globalOffsetY = windowLocation.y - myLoc[1];
// Get the coords in the image view with the center crop method
float scaleX = (float) viewSize.x / (float) bitmap.getWidth();
float scaleY = (float) viewSize.y / (float) bitmap.getHeight();
float scale = scaleX > scaleY ? scaleX : scaleY;
float scaledX = bitmap.getWidth() * scale;
float scaledY = bitmap.getHeight() * scale;
float offsetX = (scaledX - viewSize.x) * 0.5f;
float offsetY = (scaledY - viewSize.y) * 0.5f;
sourceOverlap.set(offsetX, offsetY);
sourceImageRect.set(
-offsetX + globalOffsetX,
-offsetY + globalOffsetY,
scaledX - offsetX + globalOffsetX,
scaledY - offsetY + globalOffsetY);
}
public void setProgress(float progress) {
this.progress = progress;
// Center inside method
float destScale = Math.min(
(float) getWidth() / (float) bitmap.getWidth(),
(float) getHeight() / (float) bitmap.getHeight());
float destOffsetX = (getWidth() - bitmap.getWidth() * destScale) * 0.5f;
float destOffsetY = (getHeight() - bitmap.getHeight() * destScale) * 0.5f;
float destRight = bitmap.getWidth() * destScale + destOffsetX;
float destBottom = bitmap.getHeight() * destScale + destOffsetY;
float left = sourceImageRect.left + (destOffsetX - sourceImageRect.left) * progress;
float top = sourceImageRect.top + (destOffsetY - sourceImageRect.top) * progress;
float right = sourceImageRect.right + (destRight - sourceImageRect.right) * progress;
float bottom = sourceImageRect.bottom + (destBottom - sourceImageRect.bottom) * progress;
destRect.set(left, top, right, bottom);
destClip.set(
left + sourceOverlap.x * (1f - progress),
top + sourceOverlap.y * (1f - progress),
right - sourceOverlap.x * (1f - progress),
bottom - sourceOverlap.y * (1f - progress)
);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
matrix.setRectToRect(bitmapRect, destRect, Matrix.ScaleToFit.FILL);
canvas.save();
if (progress < 1f) {
canvas.clipRect(destClip);
}
canvas.drawBitmap(bitmap, matrix, paint);
canvas.restore();
}
}
private void init() {
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
}
}

@ -17,76 +17,12 @@
*/
package org.floens.chan.utils;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import org.floens.chan.ui.animation.HeightAnimation;
public class AnimationUtils {
/**
* On your start view call startView.getGlobalVisibleRect(startBounds)
* and on your end container call endContainer.getGlobalVisibleRect(endBounds, globalOffset);<br>
* startBounds and endBounds will be adjusted appropriately and the starting scale will be returned.
*
* @param startBounds your startBounds
* @param endBounds your endBounds
* @param globalOffset your globalOffset
* @return the starting scale
*/
public static float calculateBoundsAnimation(Rect startBounds, Rect endBounds, Point globalOffset) {
startBounds.offset(-globalOffset.x, -globalOffset.y);
endBounds.offset(-globalOffset.x, -globalOffset.y);
float startScale;
if ((float) endBounds.width() / endBounds.height() > (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
startScale = (float) startBounds.height() / endBounds.height();
float startWidth = startScale * endBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
} else {
// Extend start bounds vertically
startScale = (float) startBounds.width() / endBounds.width();
float startHeight = startScale * endBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}
return startScale;
}
public static void getClippingBounds(Rect clipStartBounds, ImageView view, Rect out, float progress) {
float[] f = new float[9];
view.getImageMatrix().getValues(f);
float imageWidth = view.getDrawable().getIntrinsicWidth() * f[Matrix.MSCALE_X];
float imageHeight = view.getDrawable().getIntrinsicHeight() * f[Matrix.MSCALE_Y];
float imageWidthDiff = (view.getWidth() - imageWidth) / 2f;
float imageHeightDiff = (view.getHeight() - imageHeight) / 2f;
float offsetWidth = ((imageWidth - clipStartBounds.right) / 2f) * progress;
float offsetHeight = ((imageHeight - clipStartBounds.bottom) / 2f) * progress;
out.set((int) (offsetWidth + imageWidthDiff),
(int) (offsetHeight + imageHeightDiff),
(int) (view.getWidth() - offsetWidth - imageWidthDiff),
(int) (view.getHeight() - offsetHeight - imageHeightDiff));
}
public static void adjustImageViewBoundsToDrawableBounds(ImageView imageView, Rect bounds) {
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
bounds.left += f[Matrix.MTRANS_X];
bounds.top += f[Matrix.MTRANS_Y];
bounds.right = (bounds.left + (int) (imageView.getDrawable().getIntrinsicWidth() * f[Matrix.MSCALE_X]));
bounds.bottom = (bounds.top + (int) (imageView.getDrawable().getIntrinsicHeight() * f[Matrix.MSCALE_Y]));
}
public static void setHeight(View view, boolean expand, boolean animated) {
setHeight(view, expand, animated, -1);
}

@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.floens.chan.ui.view.ClippingImageView
<org.floens.chan.ui.view.TransitionImageView
android:id="@+id/preview_image"
android:layout_width="match_parent"
android:layout_height="match_parent" />

Loading…
Cancel
Save