/*
* Copyright 2014 Prateek Srivastava
*
* 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.f2prateek.couchpotato.ui.widget;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import butterknife.ButterKnife;
import butterknife.InjectView;
import com.f2prateek.couchpotato.R;
import com.squareup.picasso.Picasso;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class KenBurnsView extends FrameLayout {
private static final int SWAP_DURATION = 10000;
private static final int FADE_DURATION = 500;
private static final float MAX_SCALE_FACTOR = 1.5F;
private static final float MIN_SCALE_FACTOR = 1.2F;
private final Random random = new Random();
private final Handler handler = new Handler();
@InjectView(R.id.image0) ImageView activeImageView;
@InjectView(R.id.image1) ImageView inactiveImageView;
private List<String> images;
private int currentImageIndex = 0;
private Picasso picasso;
private Runnable swapImageRunnable = new Runnable() {
@Override
public void run() {
swapImage();
handler.postDelayed(this, SWAP_DURATION - FADE_DURATION * 2);
}
};
public KenBurnsView(Context context) {
this(context, null);
}
public KenBurnsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KenBurnsView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private void swapImage() {
// Swap our active and inactive image views for transition
final ImageView swapper = inactiveImageView;
inactiveImageView = activeImageView;
activeImageView = swapper;
// load the next image
currentImageIndex = (1 + currentImageIndex) % images.size();
loadImage(inactiveImageView, images.get(currentImageIndex));
// Animate the active ImageView
animate(activeImageView);
// Fade in the active ImageView and fade out the inactive ImageView
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(FADE_DURATION);
animatorSet.playTogether(ObjectAnimator.ofFloat(activeImageView, "alpha", 0.0f, 1.0f),
ObjectAnimator.ofFloat(inactiveImageView, "alpha", 1.0f, 0.0f));
animatorSet.start();
}
/**
* Animate the view with a KenBurns effect.
*/
public void animate(View view) {
float fromScale = pickScale();
float toScale = pickScale();
float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
float toTranslationX = pickTranslation(view.getWidth(), toScale);
float toTranslationY = pickTranslation(view.getHeight(), toScale);
start(view, SWAP_DURATION, fromScale, toScale, fromTranslationX, fromTranslationY,
toTranslationX, toTranslationY);
}
private float pickScale() {
return MIN_SCALE_FACTOR + random.nextFloat() * (MAX_SCALE_FACTOR - MIN_SCALE_FACTOR);
}
private float pickTranslation(int value, float ratio) {
return value * (ratio - 1.0f) * (random.nextFloat() - 0.5f);
}
private void start(View view, long duration, float fromScale, float toScale,
float fromTranslationX, float fromTranslationY, float toTranslationX, float toTranslationY) {
view.setScaleX(fromScale);
view.setScaleY(fromScale);
view.setTranslationX(fromTranslationX);
view.setTranslationY(fromTranslationY);
ViewPropertyAnimator propertyAnimator = view.animate()
.translationX(toTranslationX)
.translationY(toTranslationY)
.scaleX(toScale)
.scaleY(toScale)
.setDuration(duration);
propertyAnimator.start();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
handler.removeCallbacks(swapImageRunnable);
}
private void startKenBurnsAnimation() {
handler.post(swapImageRunnable);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
inflate(getContext(), R.layout.kenburns_view, this);
ButterKnife.inject(this);
}
/** Initial load of images, we only have one image that we can display. */
public void load(Picasso picasso, String image) {
this.picasso = picasso;
this.images = Arrays.asList(image);
loadImage(activeImageView, image);
loadImage(inactiveImageView, image);
startKenBurnsAnimation();
}
public void update(List<String> images) {
this.images = images;
if (images.size() > 1) {
currentImageIndex = 1;
loadImage(inactiveImageView, images.get(currentImageIndex));
}
}
private void loadImage(ImageView imageView, String image) {
// no error images here
picasso.load(image).fit().centerCrop().noFade().into(imageView);
}
}