diff --git a/app/src/main/java/com/hippo/animation/ArgbEvaluator.java b/app/src/main/java/com/hippo/animation/ArgbEvaluator.java
new file mode 100644
index 000000000..b575fb129
--- /dev/null
+++ b/app/src/main/java/com/hippo/animation/ArgbEvaluator.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.hippo.animation;
+
+import android.animation.TypeEvaluator;
+
+/**
+ * This evaluator can be used to perform type interpolation between integer
+ * values that represent ARGB colors.
+ */
+public class ArgbEvaluator implements TypeEvaluator {
+ private static final ArgbEvaluator sInstance = new ArgbEvaluator();
+
+ /**
+ * Returns an instance of ArgbEvaluator that may be used in
+ * {@link android.animation.ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
+ * be used in multiple Animators because it holds no state.
+ * @return An instance of ArgbEvalutor.
+ */
+ public static ArgbEvaluator getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * This function returns the calculated in-between value for a color
+ * given integers that represent the start and end values in the four
+ * bytes of the 32-bit int. Each channel is separately linearly interpolated
+ * and the resulting calculated values are recombined into the return value.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue A 32-bit int value representing colors in the
+ * separate bytes of the parameter
+ * @param endValue A 32-bit int value representing colors in the
+ * separate bytes of the parameter
+ * @return A value that is calculated to be the linearly interpolated
+ * result, derived by separating the start and end values into separate
+ * color channels and interpolating each one separately, recombining the
+ * resulting values in the same way.
+ */
+ public Object evaluate(float fraction, Object startValue, Object endValue) {
+ int startInt = (Integer) startValue;
+ int startA = (startInt >> 24) & 0xff;
+ int startR = (startInt >> 16) & 0xff;
+ int startG = (startInt >> 8) & 0xff;
+ int startB = startInt & 0xff;
+
+ int endInt = (Integer) endValue;
+ int endA = (endInt >> 24) & 0xff;
+ int endR = (endInt >> 16) & 0xff;
+ int endG = (endInt >> 8) & 0xff;
+ int endB = endInt & 0xff;
+
+ return ((startA + (int)(fraction * (endA - startA))) << 24) |
+ ((startR + (int)(fraction * (endR - startR))) << 16) |
+ ((startG + (int)(fraction * (endG - startG))) << 8) |
+ ((startB + (int)(fraction * (endB - startB))));
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
index bd86d8747..c352f48e4 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
@@ -62,15 +62,14 @@ public class GalleryListScene extends Scene implements SearchLayout.SearhLayoutH
private PagerAdapter mPagerAdapter;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mActivity = (ContentActivity) getStageActivity();
- mResources = mActivity.getResources();
- }
-
@SuppressLint("InflateParams")
@Override
- public View onCreateSceneView(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActivity = (ContentActivity) getStageActivity();
+ mResources = mActivity.getResources();
+
LayoutInflater inflater = mActivity.getLayoutInflater();
// Search View
@@ -94,9 +93,9 @@ public class GalleryListScene extends Scene implements SearchLayout.SearhLayoutH
});
// Main View
- View sceneView = inflater.inflate(R.layout.scene_gallery_list, null);
- mAppbar = (Appbar) sceneView.findViewById(R.id.appbar);
- mViewPager = (ViewPager) sceneView.findViewById(R.id.viewPager);
+ setContentView(R.layout.scene_gallery_list);
+ mAppbar = (Appbar) findViewById(R.id.appbar);
+ mViewPager = (ViewPager) findViewById(R.id.viewPager);
mAppbar.setTitle(mResources.getString(R.string.app_name));
@@ -110,8 +109,6 @@ public class GalleryListScene extends Scene implements SearchLayout.SearhLayoutH
if (fitPaddingBottom != -1) {
setFitPaddingBottom(fitPaddingBottom);
}
-
- return sceneView;
}
private void setFitPaddingBottom(int fitPaddingBottom) {
@@ -195,5 +192,4 @@ public class GalleryListScene extends Scene implements SearchLayout.SearhLayoutH
view.removeView((View) object);
}
}
-
}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/TestScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/TestScene.java
index 8f58c26f9..805e77072 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/TestScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/TestScene.java
@@ -17,8 +17,6 @@ package com.hippo.ehviewer.ui.scene;
import android.content.res.Resources;
import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.ContentActivity;
@@ -31,14 +29,10 @@ public class TestScene extends Scene {
@Override
public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.scene_test);
+
mActivity = (ContentActivity) getStageActivity();
mResources = mActivity.getResources();
}
-
- @SuppressWarnings("InflateParams")
- public View onCreateSceneView(Bundle savedInstanceState) {
- LayoutInflater inflater = mActivity.getLayoutInflater();
-
- return inflater.inflate(R.layout.scene_test, null);
- }
}
diff --git a/app/src/main/java/com/hippo/scene/Scene.java b/app/src/main/java/com/hippo/scene/Scene.java
index 55b76113c..4e36ad693 100644
--- a/app/src/main/java/com/hippo/scene/Scene.java
+++ b/app/src/main/java/com/hippo/scene/Scene.java
@@ -23,7 +23,9 @@ import android.support.annotation.Nullable;
import android.support.v4.app.FragmentManager;
import android.util.SparseArray;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewParent;
+import android.widget.FrameLayout;
import com.hippo.util.AssertUtils;
@@ -35,7 +37,9 @@ import com.hippo.util.AssertUtils;
*/
public abstract class Scene {
- private @Nullable View mSceneView;
+ private @Nullable FrameLayout mSceneView;
+
+ private int mBackgroundColor = 0xffeeeeee; // TODO Need a better to set background color
private static SceneManager sSceneManager;
@@ -50,7 +54,12 @@ public abstract class Scene {
return stageActivity;
}
- @Nullable View getSceneView() {
+ /**
+ * You will get null before onCreate and after onDestroy mostly
+ *
+ * @return Null or nonull
+ */
+ public @Nullable View getSceneView() {
return mSceneView;
}
@@ -73,7 +82,11 @@ public abstract class Scene {
void create(Bundle savedInstanceState) {
onCreate(savedInstanceState);
- mSceneView = onCreateSceneView(savedInstanceState);
+
+ if (mSceneView == null) {
+ mSceneView = new FrameLayout(getStageActivity());
+ initBackground(mSceneView);
+ }
// Make sure scene view is attach from stage
attachToStage();
@@ -104,15 +117,45 @@ public abstract class Scene {
protected void onCreate(Bundle savedInstanceState) {
}
- /**
- * It is called after {@link #onCreate(android.os.Bundle)}.
- * Create view of the scene.
- *
- * @param savedInstanceState null for first time, non null for recrearte
- * @return the view of the scene
- */
- protected @Nullable View onCreateSceneView(Bundle savedInstanceState) {
- return null;
+ public void setBackgroundColor(int bgColor) {
+ if (mBackgroundColor != bgColor) {
+ mBackgroundColor = bgColor;
+ View sceneView = getSceneView();
+ if (sceneView != null) {
+ sceneView.setBackgroundColor(bgColor);
+ }
+ }
+ }
+
+ public int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+
+
+ protected void setContentView(int resId) {
+ AssertUtils.assertNull("Only call setContentView once", mSceneView);
+
+ StageActivity sa = getStageActivity();
+ mSceneView = new FrameLayout(sa);
+ initBackground(mSceneView);
+ mSceneView.setBackgroundColor(mBackgroundColor);
+ sa.getLayoutInflater().inflate(resId, mSceneView);
+ }
+
+ protected void setContentView(View view) {
+ AssertUtils.assertNull("Only call setContentView once", mSceneView);
+
+ StageActivity sa = getStageActivity();
+ mSceneView = new FrameLayout(sa);
+ initBackground(mSceneView);
+ mSceneView.addView(view, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+
+ private void initBackground(@NonNull View bg) {
+ bg.setBackgroundColor(mBackgroundColor);
+ bg.setClickable(true);
}
protected void onResume() {
@@ -125,6 +168,23 @@ public abstract class Scene {
}
+ /**
+ * Finds a view that was identified by the id attribute from the XML that
+ * was processed in {@link #onCreate}.
+ *
+ * @param resId the Id
+ * @return The view if found or null otherwise.
+ */
+ public View findViewById(int resId) {
+ View sceneView = getSceneView();
+ if (sceneView != null) {
+ return sceneView.findViewById(resId);
+ } else {
+ return null;
+ }
+ }
+
+
/**
* Is scene view attached to stage
*
diff --git a/app/src/main/java/com/hippo/scene/SceneManager.java b/app/src/main/java/com/hippo/scene/SceneManager.java
index e1147e01e..180d95f25 100644
--- a/app/src/main/java/com/hippo/scene/SceneManager.java
+++ b/app/src/main/java/com/hippo/scene/SceneManager.java
@@ -85,7 +85,7 @@ class SceneManager {
scene.destroy(false);
if (previousState != null) {
- curtain.open(previousState, scene);
+ curtain.close(previousState, scene);
}
} else {
scene.destroy(true);
@@ -138,7 +138,8 @@ class SceneManager {
protected void onSaveInstanceState(Bundle outState) {
for (Pair p : mSceneStack) {
- p.first.saveInstanceState(outState);
+ Scene scene = p.first;
+ scene.saveInstanceState(outState);
}
}
diff --git a/app/src/main/java/com/hippo/scene/TransitionCurtain.java b/app/src/main/java/com/hippo/scene/TransitionCurtain.java
index c1a1a0486..8fb69f575 100644
--- a/app/src/main/java/com/hippo/scene/TransitionCurtain.java
+++ b/app/src/main/java/com/hippo/scene/TransitionCurtain.java
@@ -15,17 +15,21 @@
package com.hippo.scene;
+import android.animation.Animator;
import android.animation.ObjectAnimator;
+import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewTreeObserver;
+import com.hippo.animation.ArgbEvaluator;
import com.hippo.util.ViewUtils;
public class TransitionCurtain extends Curtain {
- private static long ANIMATE_TIME = 300L;
+ private static long ANIMATE_DELAY = 500L;
+ private static long ANIMATE_TIME = 800L;
private ViewPair[] mViewPairArray;
@@ -33,14 +37,28 @@ public class TransitionCurtain extends Curtain {
mViewPairArray = viewPairArray;
}
+
+ // Make background from transport to default.
+ // Invisible other
@Override
public void open(final @NonNull Scene enter, final @NonNull Scene exit) {
+ // Handle background
+ final int bgColor = enter.getBackgroundColor();
+ enter.setBackgroundColor(Color.TRANSPARENT);
+
+ ObjectAnimator colorAnim = ObjectAnimator.ofInt(enter, "backgroundColor", bgColor);
+ colorAnim.setEvaluator(ArgbEvaluator.getInstance());
+ colorAnim.setDuration(ANIMATE_TIME);
+
+ colorAnim.start();
+
+ // Handle transit part
for (ViewPair pair : mViewPairArray) {
final View enterView = pair.getToView(enter);
final View exitView = pair.getFromView(exit);
if (enterView == null || exitView == null) {
- // Can't get to view
+ // Can't get view
continue;
}
@@ -53,9 +71,9 @@ public class TransitionCurtain extends Curtain {
ViewUtils.removeOnGlobalLayoutListener(enterView.getViewTreeObserver(), this);
int[] startloaction = new int[2];
- ViewUtils.getLocationInAncestor(exitView, startloaction, exit.getStageActivity().getStageLayout());
+ ViewUtils.getLocationInAncestor(exitView, startloaction, exit.getSceneView());
int[] endloaction = new int[2];
- ViewUtils.getLocationInAncestor(enterView, endloaction, enter.getStageActivity().getStageLayout());
+ ViewUtils.getLocationInAncestor(enterView, endloaction, enter.getSceneView());
int startWidth = exitView.getWidth();
int startHeight = exitView.getHeight();
int endWidth = enterView.getWidth();
@@ -63,6 +81,8 @@ public class TransitionCurtain extends Curtain {
ViewUtils.setVisibility(exitView, View.INVISIBLE);
ViewUtils.setVisibility(enterView, View.VISIBLE);
+ enterView.setPivotX(0);
+ enterView.setPivotY(0);
// Start animation
ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(enterView, "scaleX", (float) startWidth / endWidth, 1f);
@@ -74,18 +94,93 @@ public class TransitionCurtain extends Curtain {
ObjectAnimator yAnim = ObjectAnimator.ofFloat(enterView, "y", startloaction[1], endloaction[1]);
yAnim.setDuration(ANIMATE_TIME);
+ yAnim.addListener(new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewUtils.setVisibility(exitView, View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ ViewUtils.setVisibility(exitView, View.VISIBLE);
+ }
+ });
+
scaleXAnim.start();
scaleYAnim.start();
xAnim.start();
yAnim.start();
+
+ // TODO show other part progressively
}
});
}
}
@Override
- public void close(@NonNull Scene enter, @NonNull Scene exit) {
- dispatchDetachFromeStage(exit);
+ public void close(@NonNull Scene enter, final @NonNull Scene exit) {
+
+ // Handle background
+ ObjectAnimator colorAnim = ObjectAnimator.ofInt(exit, "backgroundColor", Color.TRANSPARENT);
+ colorAnim.setEvaluator(ArgbEvaluator.getInstance());
+ colorAnim.setDuration(ANIMATE_TIME);
+
+ colorAnim.start();
+
+ // Handle transit part
+ for (ViewPair pair : mViewPairArray) {
+ final View enterView = pair.getFromView(enter);
+ final View exitView = pair.getToView(exit);
+ if (enterView == null || exitView == null) {
+ // Can't get view
+ continue;
+ }
+
+ int[] startloaction = new int[2];
+ ViewUtils.getLocationInAncestor(exitView, startloaction, exit.getSceneView());
+ int[] endloaction = new int[2];
+ ViewUtils.getLocationInAncestor(enterView, endloaction, enter.getSceneView());
+ int startWidth = exitView.getWidth();
+ int startHeight = exitView.getHeight();
+ int endWidth = enterView.getWidth();
+ int endHeight = enterView.getHeight();
+
+ ViewUtils.setVisibility(exitView, View.VISIBLE);
+ ViewUtils.setVisibility(enterView, View.INVISIBLE);
+ exitView.setPivotX(0);
+ exitView.setPivotY(0);
+
+ // Start animation
+ ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(exitView, "scaleX", (float) endWidth / startWidth);
+ scaleXAnim.setDuration(ANIMATE_TIME);
+ ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(exitView, "scaleY", (float) endHeight / startHeight);
+ scaleYAnim.setDuration(ANIMATE_TIME);
+ ObjectAnimator xAnim = ObjectAnimator.ofFloat(exitView, "x", endloaction[0]);
+ xAnim.setDuration(ANIMATE_TIME);
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(exitView, "y", endloaction[1]);
+ yAnim.setDuration(ANIMATE_TIME);
+
+ yAnim.addListener(new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchDetachFromeStage(exit);
+ ViewUtils.setVisibility(enterView, View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ dispatchDetachFromeStage(exit);
+ ViewUtils.setVisibility(enterView, View.VISIBLE);
+ }
+ });
+
+ scaleXAnim.start();
+ scaleYAnim.start();
+ xAnim.start();
+ yAnim.start();
+
+ // TODO show other part progressively
+ }
}
public static class ViewPair {
@@ -132,4 +227,23 @@ public class TransitionCurtain extends Curtain {
public abstract View getToView(@Nullable View sceneView);
}
+
+ public class SimpleAnimatorListener implements Animator.AnimatorListener {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
}
diff --git a/app/src/main/java/com/hippo/util/AssertUtils.java b/app/src/main/java/com/hippo/util/AssertUtils.java
index 55518e762..51aeb382e 100644
--- a/app/src/main/java/com/hippo/util/AssertUtils.java
+++ b/app/src/main/java/com/hippo/util/AssertUtils.java
@@ -17,6 +17,12 @@ package com.hippo.util;
public class AssertUtils {
+ public static void assertNull(String message, Object object) {
+ if (object != null) {
+ throw new AssertError(message);
+ }
+ }
+
public static void assertNotNull(String message, Object object) {
if (object == null) {
throw new AssertError(message);
@@ -29,6 +35,13 @@ public class AssertUtils {
}
}
+ public static void assertNullEx(String message, Object object)
+ throws AssertException {
+ if (object != null) {
+ throw new AssertException(message);
+ }
+ }
+
public static void assertNotNullEx(String message, Object object)
throws AssertException {
if (object == null) {
diff --git a/app/src/main/res/layout/fragment_list.xml b/app/src/main/res/layout/fragment_list.xml
index f0613fda1..9d3f3aeca 100644
--- a/app/src/main/res/layout/fragment_list.xml
+++ b/app/src/main/res/layout/fragment_list.xml
@@ -30,6 +30,6 @@
android:layout_height="wrap_content"
android:text="Click"
android:id="@+id/test_button"
- android:layout_gravity="center_horizontal|bottom"/>
+ android:layout_gravity="right|center_vertical"/>
diff --git a/app/src/main/res/layout/scene_test.xml b/app/src/main/res/layout/scene_test.xml
index ebb8a065a..d8660791e 100644
--- a/app/src/main/res/layout/scene_test.xml
+++ b/app/src/main/res/layout/scene_test.xml
@@ -23,7 +23,7 @@
android:id="@+id/haha"
android:layout_width="120dp"
android:layout_height="120dp"
- android:layout_gravity="bottom|right"
+ android:layout_gravity="right|top"
android:src="@drawable/ic_happy"
android:scaleType="fitXY"/>