Put imageviewer in a navigationcontroller and some cleanup

tempwork
Floens 10 years ago
parent 2ae40e97f7
commit cf7af07382
  1. 27
      Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java
  2. 9
      Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java
  3. 180
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
  4. 43
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java
  5. 5
      Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java
  6. 19
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  7. 4
      Clover/app/src/main/java/org/floens/chan/ui/view/ClippingImageView.java
  8. 1
      Clover/app/src/main/res/layout/controller_image_viewer.xml
  9. 0
      Clover/app/src/main/res/layout/controller_navigation_drawer.xml
  10. 37
      Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml

@ -19,7 +19,6 @@ package org.floens.chan.controller;
import android.content.Context;
import android.content.res.Configuration;
import android.support.v4.widget.DrawerLayout;
import android.widget.FrameLayout;
import org.floens.chan.ui.toolbar.Toolbar;
@ -30,8 +29,6 @@ import java.util.List;
public abstract class NavigationController extends Controller implements ControllerTransition.Callback, Toolbar.ToolbarCallback {
public Toolbar toolbar;
public FrameLayout container;
public DrawerLayout drawerLayout;
public FrameLayout drawer;
private List<Controller> controllerList = new ArrayList<>();
private ControllerTransition controllerTransition;
@ -41,6 +38,15 @@ public abstract class NavigationController extends Controller implements Control
super(context);
}
@Override
public void onDestroy() {
super.onDestroy();
while (controllerList.size() > 0) {
popController(false);
}
}
public boolean pushController(final Controller to) {
return pushController(to, true);
}
@ -92,12 +98,12 @@ public abstract class NavigationController extends Controller implements Control
throw new IllegalArgumentException("Cannot pop controller while a transition is in progress.");
}
if (controllerList.size() == 1) {
throw new IllegalArgumentException("Cannot pop with 1 controller left");
if (controllerList.size() == 0) {
throw new IllegalArgumentException("Cannot pop with no controllers left");
}
final Controller from = controllerList.get(controllerList.size() - 1);
final Controller to = controllerList.get(controllerList.size() - 2);
final Controller to = controllerList.size() > 1 ? controllerList.get(controllerList.size() - 2) : null;
if (controllerTransition != null) {
blockingInput = true;
@ -105,17 +111,18 @@ public abstract class NavigationController extends Controller implements Control
controllerTransition.setCallback(this);
ControllerLogic.startTransition(from, to, true, false, container, controllerTransition);
if (to != null) {
toolbar.setNavigationItem(true, false, to.navigationItem);
}
} else {
ControllerLogic.transition(from, to, true, false, container);
if (to != null) {
toolbar.setNavigationItem(false, false, to.navigationItem);
}
return true;
controllerList.remove(from);
}
public void setControllerList(List<Controller> controllers) {
return true;
}
@Override

@ -88,10 +88,19 @@ public class BoardActivity extends Activity {
@Override
public void onBackPressed() {
if (!stackTop().onBack()) {
// Don't destroy the view, let Android do that or it'll create artifacts
stackTop().onHide();
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stackTop().onDestroy();
}
private Controller stackTop() {
return stack.get(stack.size() - 1);
}

@ -20,23 +20,24 @@ import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.ui.view.ClippingImageView;
import org.floens.chan.utils.AnimationUtils;
import org.floens.chan.utils.Logger;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation;
public class ImageViewController extends Controller implements View.OnClickListener {
private static final int DURATION = 165;
private static final float CLIP_DURATION_PERCENTAGE = 0.40f;
private static final float FINAL_ALPHA = 0.80f;
public class ImageViewerController extends Controller implements View.OnClickListener {
private static final int TRANSITION_DURATION = 200; //165;
private static final int TRANSITION_CLIP_DURATION = (int) (TRANSITION_DURATION * 0.5f);
private static final float TRANSITION_FINAL_ALPHA = 0.80f;
private ClippingImageView imageView;
private ClippingImageView previewImage;
private Callback callback;
private int statusBarColorPrevious;
private AnimatorSet startAnimation;
private AnimatorSet startPreviewAnimation;
private AnimatorSet endAnimation;
public ImageViewController(Context context) {
public ImageViewerController(Context context) {
super(context);
}
@ -44,62 +45,59 @@ public class ImageViewController extends Controller implements View.OnClickListe
public void onCreate() {
super.onCreate();
view = inflateRes(R.layout.controller_view_image);
imageView = (ClippingImageView) view.findViewById(R.id.image);
view = inflateRes(R.layout.controller_image_viewer);
previewImage = (ClippingImageView) view.findViewById(R.id.image);
view.setOnClickListener(this);
}
@Override
public boolean onBack() {
removeImage();
return true;
public void onClick(View v) {
startPreviewOutTransition();
}
@Override
public void onClick(View v) {
removeImage();
public boolean onBack() {
startPreviewOutTransition();
return true;
}
public void setImage(Callback callback, final ImageView startImageView) {
public void startPreviewInTransition(Callback callback, final ImageView previewImageView) {
this.callback = callback;
imageView.setImageDrawable(startImageView.getDrawable());
previewImage.setImageDrawable(previewImageView.getDrawable());
Rect startBounds = getStartImageViewBounds(startImageView);
Rect startBounds = getImageViewBounds(previewImageView);
final Rect endBounds = new Rect();
final Point globalOffset = new Point();
view.getGlobalVisibleRect(endBounds, globalOffset);
float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset);
imageView.setPivotX(0f);
imageView.setPivotY(0f);
imageView.setX(startBounds.left);
imageView.setY(startBounds.top);
imageView.setScaleX(startScale);
imageView.setScaleY(startScale);
previewImage.setPivotX(0f);
previewImage.setPivotY(0f);
previewImage.setX(startBounds.left);
previewImage.setY(startBounds.top);
previewImage.setScaleX(startScale);
previewImage.setScaleY(startScale);
Rect clipStartBounds = new Rect(0, 0, (int) (startImageView.getWidth() / startScale), (int) (startImageView.getHeight() / startScale));
Rect clipStartBounds = new Rect(0, 0, (int) (previewImageView.getWidth() / startScale), (int) (previewImageView.getHeight() / startScale));
Window window = ((Activity) context).getWindow();
if (Build.VERSION.SDK_INT >= 21) {
statusBarColorPrevious = window.getStatusBarColor();
statusBarColorPrevious = getWindow().getStatusBarColor();
}
startAnimation(startBounds, endBounds, startScale, clipStartBounds);
doPreviewInTransition(startBounds, endBounds, startScale, clipStartBounds);
}
public void removeImage() {
if (startAnimation != null || endAnimation != null) {
public void startPreviewOutTransition() {
if (startPreviewAnimation != null || endAnimation != null) {
return;
}
endAnimation();
// endAnimationEmpty();
doPreviewOutAnimation();
}
private void startAnimation(Rect startBounds, Rect finalBounds, float startScale, final Rect clipStartBounds) {
startAnimation = new AnimatorSet();
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() {
@ -114,42 +112,39 @@ public class ImageViewController extends Controller implements View.OnClickListe
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue());
imageView.clip(clipRect);
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue());
previewImage.clip(clipRect);
}
});
startAnimation
.play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left, finalBounds.left).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top, finalBounds.top).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, startScale, 1f).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, startScale, 1f).setDuration(DURATION))
.with(backgroundAlpha.setDuration(DURATION))
.with(clip.setDuration((long) (DURATION * CLIP_DURATION_PERCENTAGE)));
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));
startAnimation.setInterpolator(new DecelerateInterpolator());
startAnimation.addListener(new AnimatorListenerAdapter() {
startPreviewAnimation.setInterpolator(new DecelerateInterpolator());
startPreviewAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
startAnimationEnd();
startAnimation = null;
previewImage.setX(0f);
previewImage.setY(0f);
previewImage.setScaleX(1f);
previewImage.setScaleY(1f);
previewImage.clip(null);
startPreviewAnimation = null;
}
});
startAnimation.start();
}
private void startAnimationEnd() {
imageView.setX(0f);
imageView.setY(0f);
imageView.setScaleX(1f);
imageView.setScaleY(1f);
startPreviewAnimation.start();
}
private void endAnimation() {
private void doPreviewOutAnimation() {
ImageView startImage = getStartImageView();
Rect startBounds = null;
if (startImage != null) {
startBounds = getStartImageViewBounds(startImage);
startBounds = getImageViewBounds(startImage);
}
if (startBounds == null) {
endAnimation = new AnimatorSet();
@ -163,16 +158,16 @@ public class ImageViewController extends Controller implements View.OnClickListe
});
endAnimation
.play(ObjectAnimator.ofFloat(imageView, View.Y, imageView.getTop(), imageView.getTop() + dp(20)))
.with(ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f, 0f))
.play(ObjectAnimator.ofFloat(previewImage, View.Y, previewImage.getTop(), previewImage.getTop() + dp(20)))
.with(ObjectAnimator.ofFloat(previewImage, View.ALPHA, 1f, 0f))
.with(backgroundAlpha);
endAnimation.setDuration(DURATION);
endAnimation.setDuration(TRANSITION_DURATION);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
endAnimationEnd();
previewOutAnimationEnded();
}
});
endAnimation.start();
@ -198,75 +193,84 @@ public class ImageViewController extends Controller implements View.OnClickListe
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue());
imageView.clip(clipRect);
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue());
previewImage.clip(clipRect);
}
});
long clipDuration = (long) (DURATION * CLIP_DURATION_PERCENTAGE);
clip.setStartDelay(DURATION - clipDuration);
clip.setDuration(clipDuration);
clip.setStartDelay(TRANSITION_DURATION - TRANSITION_CLIP_DURATION);
clip.setDuration(TRANSITION_CLIP_DURATION);
endAnimation
.play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, 1f, startScale).setDuration(DURATION))
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, 1f, startScale).setDuration(DURATION))
.with(backgroundAlpha.setDuration(DURATION))
.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) {
endAnimationEnd();
previewOutAnimationEnded();
}
});
endAnimation.start();
}
}
private void endAnimationEnd() {
Window window = ((Activity) context).getWindow();
if (Build.VERSION.SDK_INT >= 21) {
window.setStatusBarColor(statusBarColorPrevious);
}
private void previewOutAnimationEnded() {
setStatusBarColor(statusBarColorPrevious);
callback.onImageViewLayoutDestroy(this);
stopPresenting(false);
callback.onPreviewDestroy(this);
navigationController.stopPresenting(false);
}
private void setBackgroundAlpha(float alpha) {
alpha = alpha * FINAL_ALPHA;
alpha *= TRANSITION_FINAL_ALPHA;
view.setBackgroundColor(Color.argb((int) (alpha * 255f), 0, 0, 0));
if (Build.VERSION.SDK_INT >= 21) {
Window window = ((Activity) context).getWindow();
int r = (int) ((1f - alpha) * Color.red(statusBarColorPrevious));
int g = (int) ((1f - alpha) * Color.green(statusBarColorPrevious));
int b = (int) ((1f - alpha) * Color.blue(statusBarColorPrevious));
window.setStatusBarColor(Color.argb(255, r, g, b));
setStatusBarColor(Color.argb(255, r, g, b));
}
}
private void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(color);
}
}
private Rect getStartImageViewBounds(ImageView image) {
private Rect getImageViewBounds(ImageView image) {
Rect startBounds = new Rect();
if (image.getGlobalVisibleRect(startBounds)) {
if (image.getGlobalVisibleRect(startBounds) && !startBounds.isEmpty()) {
AnimationUtils.adjustImageViewBoundsToDrawableBounds(image, startBounds);
if (!startBounds.isEmpty()) {
Logger.test(startBounds.toShortString());
return startBounds;
} else {
return null;
}
} else {
return null;
}
}
private ImageView getStartImageView() {
return callback.getImageView(this);
return callback.getPreviewImageStartView(this);
}
private Window getWindow() {
return ((Activity) context).getWindow();
}
public interface Callback {
public ImageView getImageView(ImageViewController imageViewController);
public ImageView getPreviewImageStartView(ImageViewerController imageViewerController);
public void onImageViewLayoutDestroy(ImageViewController imageViewController);
public void onPreviewDestroy(ImageViewerController imageViewerController);
}
}

@ -0,0 +1,43 @@
package org.floens.chan.ui.controller;
import android.content.Context;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import org.floens.chan.R;
import org.floens.chan.controller.NavigationController;
import org.floens.chan.ui.toolbar.Toolbar;
import org.floens.chan.utils.AndroidUtils;
public class ImageViewerNavigationController extends NavigationController {
private ImageViewerController imageViewerController;
public ImageViewerNavigationController(Context context) {
super(context);
}
@Override
public void onCreate() {
super.onCreate();
view = inflateRes(R.layout.controller_navigation_image_viewer);
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
container = (FrameLayout) view.findViewById(R.id.container);
toolbar.setCallback(this);
imageViewerController = new ImageViewerController(context);
pushController(imageViewerController, false);
}
public void setImage(final ImageViewerController.Callback callback, final ImageView startImageView) {
AndroidUtils.waitForMeasure(imageViewerController.view, new AndroidUtils.OnMeasuredCallback() {
@Override
public boolean onMeasured(View view) {
imageViewerController.startPreviewInTransition(callback, startImageView);
return true;
}
});
}
}

@ -31,6 +31,9 @@ import org.floens.chan.utils.AndroidUtils;
import static org.floens.chan.utils.AndroidUtils.dp;
public class RootNavigationController extends NavigationController {
public DrawerLayout drawerLayout;
public FrameLayout drawer;
public RootNavigationController(Context context) {
super(context);
}
@ -39,7 +42,7 @@ public class RootNavigationController extends NavigationController {
public void onCreate() {
super.onCreate();
view = inflateRes(R.layout.root_layout);
view = inflateRes(R.layout.controller_navigation_drawer);
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
container = (FrameLayout) view.findViewById(R.id.container);
drawerLayout = (DrawerLayout) view.findViewById(R.id.drawer_layout);

@ -7,11 +7,10 @@ import android.widget.ImageView;
import org.floens.chan.controller.Controller;
import org.floens.chan.core.model.PostImage;
import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.utils.AndroidUtils;
import java.util.List;
public abstract class ThreadController extends Controller implements ThreadLayout.ThreadLayoutCallback, ImageViewController.Callback {
public abstract class ThreadController extends Controller implements ThreadLayout.ThreadLayoutCallback, ImageViewerController.Callback {
protected ThreadLayout threadLayout;
private ImageView presentingImageView;
@ -28,24 +27,18 @@ public abstract class ThreadController extends Controller implements ThreadLayou
presentingImageView = thumbnail;
presentingImageView.setVisibility(View.INVISIBLE);
final ImageViewController imageViewController = new ImageViewController(context);
presentController(imageViewController, false);
AndroidUtils.waitForMeasure(imageViewController.view, new AndroidUtils.OnMeasuredCallback() {
@Override
public boolean onMeasured(View view) {
imageViewController.setImage(ThreadController.this, thumbnail);
return true;
}
});
final ImageViewerNavigationController imageViewerNavigationController = new ImageViewerNavigationController(context);
presentController(imageViewerNavigationController, false);
imageViewerNavigationController.setImage(this, thumbnail);
}
@Override
public ImageView getImageView(ImageViewController imageViewController) {
public ImageView getPreviewImageStartView(ImageViewerController imageViewerController) {
return presentingImageView;
}
@Override
public void onImageViewLayoutDestroy(ImageViewController imageViewController) {
public void onPreviewDestroy(ImageViewerController imageViewerController) {
presentingImageView.setVisibility(View.VISIBLE);
presentingImageView = null;
}

@ -31,7 +31,11 @@ public class ClippingImageView extends ImageView {
}
public void clip(Rect rect) {
if (rect == null) {
clipRect.setEmpty();
} else {
clipRect.set(rect);
}
invalidate();
}
}

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?><!--
Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<org.floens.chan.ui.toolbar.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@color/primary"
android:elevation="4dp" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Loading…
Cancel
Save