From e6dda3651d2b53028fff2b01f2d014ba72e46bfa Mon Sep 17 00:00:00 2001 From: Zukero Date: Mon, 12 Mar 2018 15:46:08 +0100 Subject: [PATCH] Enhanced title screen's clouds animation. --- AndorsTrail/res/layout/clouds_animator.xml | 24 +- .../AndorsTrail/activity/LoadingActivity.java | 8 +- .../activity/StartScreenActivity.java | 3 - .../AndorsTrail/view/CloudsAnimatorView.java | 218 ++++++++++-------- .../AndorsTrail/view/CustomDialogFactory.java | 23 +- 5 files changed, 152 insertions(+), 124 deletions(-) diff --git a/AndorsTrail/res/layout/clouds_animator.xml b/AndorsTrail/res/layout/clouds_animator.xml index f352effa9..ef33341fb 100644 --- a/AndorsTrail/res/layout/clouds_animator.xml +++ b/AndorsTrail/res/layout/clouds_animator.xml @@ -6,34 +6,16 @@ - - + android:layout_height="match_parent"/> - - + android:layout_height="match_parent"/> - - + android:layout_height="match_parent"/> \ No newline at end of file diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/LoadingActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/LoadingActivity.java index 7cbc8d98b..cbacf4639 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/LoadingActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/LoadingActivity.java @@ -2,7 +2,6 @@ package com.gpl.rpg.AndorsTrail.activity; import android.app.Activity; import android.app.Dialog; -import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnDismissListener; import android.content.Intent; @@ -20,7 +19,7 @@ import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory; public final class LoadingActivity extends Activity implements OnResourcesLoadedListener, OnSceneLoadedListener { private WorldSetup setup; - private ProgressDialog progressDialog; + private Dialog progressDialog; @Override public void onCreate(Bundle savedInstanceState) { @@ -35,7 +34,10 @@ public final class LoadingActivity extends Activity implements OnResourcesLoaded @Override public void onResume() { super.onResume(); - progressDialog = ProgressDialog.show(this, null, getString(R.string.dialog_loading_message)); + progressDialog = CustomDialogFactory.createDialog(this, getResources().getString(R.string.dialog_loading_message), + //TODO Create custom animation for loading. + getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal), null, null, false, false); + CustomDialogFactory.show(progressDialog); setup.setOnResourcesLoadedListener(this); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/StartScreenActivity.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/StartScreenActivity.java index b18dd7363..f3b67819a 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/StartScreenActivity.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/activity/StartScreenActivity.java @@ -73,7 +73,6 @@ public final class StartScreenActivity extends FragmentActivity implements OnNew } clouds = (CloudsAnimatorView) findViewById(R.id.ts_clouds_animator); - clouds.startAnimation(); View background = findViewById(R.id.title_bg); if (background != null) { @@ -133,8 +132,6 @@ public final class StartScreenActivity extends FragmentActivity implements OnNew @Override protected void onResume() { super.onResume(); - clouds.startAnimation(); - } @Override diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CloudsAnimatorView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CloudsAnimatorView.java index 9b947f0cf..4fbec14e4 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CloudsAnimatorView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CloudsAnimatorView.java @@ -1,32 +1,32 @@ package com.gpl.rpg.AndorsTrail.view; -import java.util.List; - -import com.gpl.rpg.AndorsTrail.R; -import com.gpl.rpg.AndorsTrail.util.L; +import java.util.concurrent.ConcurrentHashMap; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; +import android.view.animation.LinearInterpolator; +import android.view.animation.Transformation; import android.view.animation.TranslateAnimation; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; +import com.gpl.rpg.AndorsTrail.R; +import com.gpl.rpg.AndorsTrail.util.L; + public class CloudsAnimatorView extends FrameLayout { - private static final int Y_MIN = 0; - private static final int Y_MAX = 100; + private static final float Y_MIN = 0f; + private static final float Y_MAX = 0.6f; - private static final int DURATION = 12000; - private static final int SPEED_MIN = 10; - private static final int SPEED_MAX = 15; - - private static final float BELOW_SPEED_FACTOR = 0.8f; + private static final int DEFAULT_DURATION = 30000; + private static final float SPEED_VARIANCE = 0.2f; + private static final float BELOW_SPEED_FACTOR = 0.5f; private static final float CENTER_SPEED_FACTOR = 1.0f; - private static final float ABOVE_SPEED_FACTOR = 1.2f; + private static final float ABOVE_SPEED_FACTOR = 1.5f; private static final int BELOW_CLOUD_COUNT = 30; private static final int CENTER_CLOUD_COUNT = 20; @@ -37,8 +37,9 @@ public class CloudsAnimatorView extends FrameLayout { private static final int[] aboveDrawablesId = new int[]{R.drawable.ts_clouds_l_01, R.drawable.ts_clouds_l_02, R.drawable.ts_clouds_l_03, R.drawable.ts_clouds_l_04}; ViewGroup belowLayer, centerLayer, aboveLayer; - View belowStart, centerStart, aboveStart; + private int duration = DEFAULT_DURATION; + private final ConcurrentHashMap animations = new ConcurrentHashMap(BELOW_CLOUD_COUNT + CENTER_CLOUD_COUNT + ABOVE_CLOUD_COUNT); public CloudsAnimatorView(Context context) { @@ -57,128 +58,155 @@ public class CloudsAnimatorView extends FrameLayout { } public void init() { - L.log("Cloud animator created"); setFocusable(false); inflate(getContext(), R.layout.clouds_animator, this); belowLayer = (ViewGroup) findViewById(R.id.ts_clouds_below); centerLayer = (ViewGroup) findViewById(R.id.ts_clouds_center); aboveLayer = (ViewGroup) findViewById(R.id.ts_clouds_above); - - belowStart = (ViewGroup) findViewById(R.id.ts_clouds_below_start); - centerStart = (ViewGroup) findViewById(R.id.ts_clouds_center_start); - aboveStart = (ViewGroup) findViewById(R.id.ts_clouds_above_start); } - private void addCloudBelow() { - if (belowLayer == null) { - L.log("Cloud below is null. Deferring."); - postDelayed(new Runnable() { - @Override - public void run() {addCloudBelow();} - }, (int)(DURATION * Math.random())); - } else { - addCloud(belowLayer, R.id.ts_clouds_below_start, belowDrawablesId, BELOW_SPEED_FACTOR); - } - + private void createCloudBelow() { + createCloud(belowLayer, belowDrawablesId, BELOW_SPEED_FACTOR); } - private void addCloudCenter() { - if (centerLayer == null) { - L.log("Cloud center is null. Deferring."); - postDelayed(new Runnable() { - @Override - public void run() {addCloudCenter();} - }, (int)(DURATION * Math.random())); - } else { - addCloud(centerLayer, R.id.ts_clouds_center_start, centerDrawablesId, CENTER_SPEED_FACTOR); - } + private void createCloudCenter() { + createCloud(centerLayer, centerDrawablesId, CENTER_SPEED_FACTOR); } - private void addCloudAbove() { - if (aboveLayer == null) { - L.log("Cloud above is null. Deferring."); - postDelayed(new Runnable() { - @Override - public void run() {addCloudAbove();} - }, (int)(DURATION * Math.random())); - } else { - addCloud(aboveLayer, R.id.ts_clouds_above_start, aboveDrawablesId, ABOVE_SPEED_FACTOR); - } + private void createCloudAbove() { + createCloud(aboveLayer, aboveDrawablesId, ABOVE_SPEED_FACTOR); } - private void addCloud(final ViewGroup layer, final int startId, final int[] ids, final float speedFactor) { + private void createCloud(final ViewGroup layer, final int[] ids, final float speedFactor) { final ImageView iv = new ImageView(getContext()); iv.setImageDrawable(getResources().getDrawable(ids[(int)(ids.length * Math.random())])); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); - //lp.addRule(RelativeLayout.LEFT_OF, startId); -// lp.addRule(RelativeLayout.ALIGN_PARENT_TOP); -// lp.topMargin = (int) (layer.getHeight() * Math.random()); - final float y = (float) (layer.getHeight() * Math.random()); - L.log("Cloud added at y="+y); layer.addView(iv, lp); - TranslateAnimation anim = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT, -1.0f, TranslateAnimation.RELATIVE_TO_PARENT, 2.0f, + + final float y = Y_MIN + (float) (layer.getHeight() * Math.random() * (Y_MAX - Y_MIN)); + float ratio = (float)Math.random(); + final float x = (float) (((1-ratio) * (iv.getWidth() + layer.getWidth())) - iv.getWidth()); + final long d = (long)((ratio * duration) / (speedFactor + (Math.random() * SPEED_VARIANCE))); + + + prepareAnimation(iv, layer, speedFactor, x, y, d); + } + + private void resetCloud(final ViewGroup layer, final float speedFactor, final ImageView iv) { + final float y = Y_MIN + (float) (layer.getHeight() * Math.random() * (Y_MAX - Y_MIN)); + final float x = -iv.getWidth(); + final long d = (long)(duration / (speedFactor + (Math.random() * SPEED_VARIANCE))); + + prepareAnimation(iv, layer, speedFactor, x, y, d); + } + + private void prepareAnimation(final ImageView iv, final ViewGroup layer, final float speedFactor, final float x, final float y, final long d) { + PausableTranslateAnimation anim = new PausableTranslateAnimation( + TranslateAnimation.ABSOLUTE, x, TranslateAnimation.ABSOLUTE, layer.getWidth(), TranslateAnimation.ABSOLUTE, y, TranslateAnimation.ABSOLUTE, y); + anim.setAnimationListener(new Animation.AnimationListener() { - @Override public void onAnimationStart(Animation animation) { + iv.setVisibility(View.VISIBLE); } - @Override - public void onAnimationRepeat(Animation animation) { - } - + public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { - L.log("Cloud ended at y="+y); - layer.removeView(iv); - if (CloudsAnimatorView.this.getVisibility() == View.VISIBLE) { - postDelayed(new Runnable() { - @Override - public void run() {addCloud(layer, startId, ids, speedFactor);} - }, (int)(DURATION * Math.random())); - } + iv.setVisibility(View.GONE); + resetCloud(layer, speedFactor, iv); } }); - anim.setDuration((long)(DURATION / speedFactor)); + anim.setInterpolator(new LinearInterpolator()); + anim.setDuration(d); + animations.put(iv, anim); iv.startAnimation(anim); + if (!hasWindowFocus()) { + anim.pause(); + } } - /*@Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - if (changedView == this && visibility == View.VISIBLE) { - startAnimation(); - } else if (changedView == this) { - stopAll(); - } - }*/ - public void startAnimation() { - L.log("Cloud animator started"); int i = BELOW_CLOUD_COUNT; while (i-- > 0) { - postDelayed(new Runnable() { - @Override - public void run() {addCloudBelow();} - }, (int)(DURATION * Math.random())); + createCloudBelow(); } i = CENTER_CLOUD_COUNT; while (i-- > 0) { - postDelayed(new Runnable() { - @Override - public void run() {addCloudCenter();} - }, (int)(DURATION * Math.random())); + createCloudCenter(); } i = ABOVE_CLOUD_COUNT; while (i-- > 0) { - postDelayed(new Runnable() { - @Override - public void run() {addCloudAbove();} - }, (int)(DURATION * Math.random())); + createCloudAbove(); } - } + } -// private void stopAll() {} + + boolean started = false; + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + L.log("Clouds onWindowFocusChanged("+hasWindowFocus+")"); + if (hasWindowFocus) { + if (!started) { + duration = (int) (DEFAULT_DURATION * getWidth() / (1024 * getResources().getDisplayMetrics().density)); + startAnimation(); + started = true; + } else { + resumeAnimation(); + } + } else { + pauseAnimation(); + } + } + + private void resumeAnimation() { + for (PausableTranslateAnimation a : animations.values()) { + a.resume(); + } + } + private void pauseAnimation() { + for (PausableTranslateAnimation a : animations.values()) { + a.pause(); + } + } + private static class PausableTranslateAnimation extends TranslateAnimation { + + private long elapsedAtPause = 0; + private boolean paused = false; + private boolean resume = false; + + public PausableTranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, + int fromYType, float fromYValue, int toYType, float toYValue) { + super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue, toYType, toYValue); + } + + @Override + public boolean getTransformation(long currentTime, Transformation outTransformation) { + if (paused && elapsedAtPause == 0) { + elapsedAtPause = currentTime - getStartTime(); + } + if (paused) { + setStartTime(currentTime - elapsedAtPause); + if (resume) { + paused = false; + resume = false; + } + } + return super.getTransformation(currentTime, outTransformation); + } + + public void pause() { + elapsedAtPause = 0; + paused = true; + } + + public void resume() { + resume = true; + } + } + } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CustomDialogFactory.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CustomDialogFactory.java index 640dc3c84..77feae0c8 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CustomDialogFactory.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/CustomDialogFactory.java @@ -4,6 +4,7 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface.OnDismissListener; import android.graphics.Rect; +import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.view.ContextThemeWrapper; import android.view.MotionEvent; @@ -30,6 +31,10 @@ public class CustomDialogFactory { } public static CustomDialog createDialog(final Context context, String title, Drawable icon, String desc, View content, boolean hasButtons) { + return createDialog(context, title, icon, desc, content, hasButtons, true); + } + + public static CustomDialog createDialog(final Context context, String title, Drawable icon, String desc, View content, boolean hasButtons, final boolean canDismiss) { final CustomDialog dialog = new CustomDialog(new ContextThemeWrapper(context, ThemeHelper.getDialogTheme())) { @Override public boolean onTouchEvent(MotionEvent event) { @@ -39,8 +44,22 @@ public class CustomDialogFactory { if (r.contains((int)event.getX(), (int)event.getY())) { return super.onTouchEvent(event); } else { - this.dismiss(); - return true; + if (canDismiss) { + this.dismiss(); + return true; + } + return false; + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + TextView title = (TextView) this.getWindow().getDecorView().findViewById(R.id.dialog_title); + if (title != null && title.getCompoundDrawables() != null && title.getCompoundDrawables()[0] != null) { + if (title.getCompoundDrawables()[0] instanceof AnimationDrawable) { + ((AnimationDrawable)title.getCompoundDrawables()[0]).start(); + } } } };