Skip to content

Instantly share code, notes, and snippets.

@carlonzo
Created March 3, 2017 22:07
Show Gist options
  • Save carlonzo/a8b156c8eafb38b14ee1a95f0fa79706 to your computer and use it in GitHub Desktop.
Save carlonzo/a8b156c8eafb38b14ee1a95f0fa79706 to your computer and use it in GitHub Desktop.
ParallaxScrollingImages yahoo weather like
package it.carlom.bubutranslater;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
public class ParallaxScrollingImages extends View {
/**
* Set the direction of the effect from Right to Left
*/
public static final int DIRECTION_RTOL = 0;
/**
* Set the direction of the effect from Left to Right
*/
public static final int DIRECTION_LTOR = 1;
private Bitmap mPrimaryImage;
private Bitmap mSecondaryImage;
private Rect mPrimaryRect;
private Rect mSecondaryRect;
private float mXSecondaryTranslation;
private float mXPrimaryTranslation;
private int mXPivot = 0;
private int mDirection = DIRECTION_LTOR;
private int mWidthView;
private int mHeightView;
private int mWidthContent;
private int mHeightContent;
private Paint mPaintImage;
private Paint mPaintDivider;
private float mBasePrimaryTranslation;
private float mBaseSecondaryTranslation;
private float effectValuePrimary = 0.1f;
private float effectValueSecondary = 0.2f;
private float mTranslation;
public ParallaxScrollingImages(Context context) {
this(context, null);
}
public ParallaxScrollingImages(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ParallaxScrollingImages(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ParallaxScrollingImages, defStyleAttr, 0);
int dividerColor = typedArray.getColor(R.styleable.ParallaxScrollingImages_dividerColor, Color.BLACK);
float dividerSize = typedArray.getDimensionPixelSize(R.styleable.ParallaxScrollingImages_dividerSize, 4);
effectValuePrimary = typedArray.getFloat(R.styleable.ParallaxScrollingImages_speedParallaxImage, effectValuePrimary);
effectValueSecondary = typedArray.getFloat(R.styleable.ParallaxScrollingImages_speedParallaxBackground, effectValueSecondary);
typedArray.recycle();
mPrimaryRect = new Rect();
mSecondaryRect = new Rect();
mPaintImage = new Paint();
mPaintImage.setAntiAlias(true);
mPaintDivider = new Paint();
mPaintDivider.setColor(dividerColor);
mPaintDivider.setStrokeWidth(dividerSize);
}
/**
* @param translation [0..1]
*/
public void setTranslation(float translation) {
if (mTranslation == translation) {
//avoid useless updates
return;
}
mTranslation = translation;
ViewCompat.postInvalidateOnAnimation(this);
}
public float getTranslation() {
return mTranslation;
}
public void setDividerSize(int dividerSize) {
mPaintDivider.setStrokeWidth(dividerSize);
}
public void setDividerColor(int dividerColor){
mPaintDivider.setColor(dividerColor);
}
private void updatePivot() {
if (mDirection == DIRECTION_LTOR) {
mPrimaryRect.right = mWidthView;
mPrimaryRect.left = (int) (mXPivot - mXPrimaryTranslation);
mSecondaryRect.right = (int) (mXPivot - mXSecondaryTranslation);
mSecondaryRect.left = 0;
} else if (mDirection == DIRECTION_RTOL) {
mPrimaryRect.left = 0;
mPrimaryRect.right = (int) (mXPivot - mXPrimaryTranslation);
mSecondaryRect.right = mWidthView;
mSecondaryRect.left = (int) (mXPivot - mXSecondaryTranslation);
}
}
private void calculateTranslation() {
mXPrimaryTranslation = mBasePrimaryTranslation * mTranslation;
mXSecondaryTranslation = mBaseSecondaryTranslation - (mBaseSecondaryTranslation * mTranslation);
if (mDirection == DIRECTION_LTOR) {
mXPivot = (int) (mWidthView * mTranslation);
mXSecondaryTranslation = -mXSecondaryTranslation;
} else if (mDirection == DIRECTION_RTOL) {
mXPivot = (int) (mWidthView - mWidthView * mTranslation);
mXPrimaryTranslation = -mXPrimaryTranslation;
}
updatePivot();
}
public void setImageBitmap(Bitmap bitmap) {
if (bitmap == null) {
mPrimaryImage = null;
} else {
mPrimaryImage = transformBitmapToBounds(bitmap);
}
mPrimaryRect.set(0, 0, mWidthView, mHeightView);
invalidate(); //TODO invalidate only if visible
}
public void setBaseImageDrawable(Drawable drawable) {
setImageBitmap(ParallaxUtils.drawableToBitmap(drawable));
}
public void setTransitionImageDrawable(Drawable drawable) {
setNewTransitionImage(ParallaxUtils.drawableToBitmap(drawable));
}
public void setDirection(int direction) {
mDirection = direction;
}
public void setNewTransitionImage(Bitmap bitmap) {
if (bitmap == null) {
mSecondaryImage = null;
} else {
mSecondaryImage = transformBitmapToBounds(bitmap);
}
mSecondaryRect.set(0, 0, mWidthView, mHeightView);
invalidate(); //TODO invalidate only if visible
}
/**
* @return true if the transition image is not null and has been successfully swapped
*/
public boolean swapTransitionToHeaderImage() {
if (mSecondaryImage == null) {
return false;
}
setImageBitmap(mSecondaryImage);
return true;
}
public int getWidthContent() {
return mWidthContent;
}
public int getHeightContent() {
return mHeightContent;
}
/**
* Scale the bitmap using CENTERCROP.
*/
private Bitmap transformBitmapToBounds(Bitmap bitmap) {
return ParallaxUtils.scaleCenterCrop(bitmap, mWidthContent, mHeightContent);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidthView = w;
mHeightView = h;
mWidthContent = mWidthView - getPaddingLeft() - getPaddingRight();
mHeightContent = mHeightView - getPaddingTop() - getPaddingBottom();
mBasePrimaryTranslation = mWidthView * effectValuePrimary;
mBaseSecondaryTranslation = mWidthView * effectValueSecondary;
mPrimaryRect.set(0, 0, mWidthView, mHeightView);
mSecondaryRect.set(0, 0, mWidthView, mHeightView);
//force measurements
setImageBitmap(mPrimaryImage);
setNewTransitionImage(mSecondaryImage);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
calculateTranslation();
if (mSecondaryImage != null) {
canvas.save();
canvas.translate(mXSecondaryTranslation, 0);
canvas.drawBitmap(mSecondaryImage, mSecondaryRect, mSecondaryRect, mPaintImage);
canvas.restore();
}
if (mPrimaryImage != null) {
canvas.save();
canvas.translate(mXPrimaryTranslation, 0);
canvas.drawBitmap(mPrimaryImage, mPrimaryRect, mPrimaryRect, mPaintImage);
canvas.restore();
}
if (mXPivot > 0 && mXPivot < mWidthView) {
canvas.drawLine(mXPivot, 0, mXPivot, mHeightView, mPaintDivider);
}
}
}
package it.carlom.bubutranslater;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
public class ParallaxUtils {
public static Bitmap scaleCenterCrop(Bitmap bitmap, int targetWidth, int targetHeight) {
int inWidth = bitmap.getWidth();
int inHeight = bitmap.getHeight();
if ((targetWidth == inWidth && targetHeight == inHeight) || (inWidth == 0 && inHeight == 0)) {
return bitmap;
}
int drawX = 0;
int drawY = 0;
int drawWidth = inWidth;
int drawHeight = inHeight;
float widthRatio = targetWidth / (float) inWidth;
float heightRatio = targetHeight / (float) inHeight;
float scaleX, scaleY;
if (widthRatio > heightRatio) {
int newSize = (int) Math.ceil(inHeight * (heightRatio / widthRatio));
drawY = (inHeight - newSize) / 2;
drawHeight = newSize;
scaleX = widthRatio;
scaleY = targetHeight / (float) drawHeight;
} else {
int newSize = (int) Math.ceil(inWidth * (widthRatio / heightRatio));
drawX = (inWidth - newSize) / 2;
drawWidth = newSize;
scaleX = targetWidth / (float) drawWidth;
scaleY = heightRatio;
}
final Matrix matrix = new Matrix();
matrix.preScale(scaleX, scaleY);
return Bitmap.createBitmap(bitmap, drawX, drawY, drawWidth, drawHeight, matrix, true);
}
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
Bitmap bitmap;
if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
public static ViewPager.OnPageChangeListener getParallaxPageChangeListener(ViewPager viewPager, ParallaxScrollingImages image, OnViewPagerScrolled onViewPagerScrolled) {
return new OnViewPagerListenerParallaxImages(viewPager, image, onViewPagerScrolled);
}
public interface OnViewPagerScrolled {
Bitmap getImageAtPosition(int position);
}
private static class OnViewPagerListenerParallaxImages implements ViewPager.OnPageChangeListener {
private final ViewPager mViewPager;
private final OnViewPagerScrolled mOnViewPagerScrolled;
private final ParallaxScrollingImages mImage;
private boolean mStartingDragging = true;
private int mLastMovingPosition = 0;
private int mPosNextPage;
private int mParallaxEffectDirection;
OnViewPagerListenerParallaxImages(ViewPager viewPager, ParallaxScrollingImages image, OnViewPagerScrolled onViewPagerScrolled) {
mViewPager = viewPager;
mOnViewPagerScrolled = onViewPagerScrolled;
mImage = image;
}
@Override
public final void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mStartingDragging || mLastMovingPosition != position) {
mLastMovingPosition = position;
final int currentItem = mViewPager.getCurrentItem();
final int childCount = mViewPager.getAdapter().getCount();
int mPosCurrentPageTemp;
// trying to understand the direction of the scroll
if (position >= currentItem) {
mParallaxEffectDirection = ParallaxScrollingImages.DIRECTION_RTOL;
mPosNextPage = position + 1;
mPosCurrentPageTemp = position;
} else {
mParallaxEffectDirection = ParallaxScrollingImages.DIRECTION_LTOR;
mPosNextPage = position;
mPosCurrentPageTemp = position + 1;
}
mStartingDragging = false;
mImage.setImageBitmap(mOnViewPagerScrolled.getImageAtPosition(mPosCurrentPageTemp));
if (mPosNextPage < childCount) {
mImage.setDirection(mParallaxEffectDirection);
mImage.setNewTransitionImage(mOnViewPagerScrolled.getImageAtPosition(mPosNextPage));
}
}
float ratioScrolled = mParallaxEffectDirection == ParallaxScrollingImages.DIRECTION_RTOL ? positionOffset : (1.0f - positionOffset);
mImage.setTranslation(ratioScrolled);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
mStartingDragging = true;
mImage.setImageBitmap(mOnViewPagerScrolled.getImageAtPosition(mViewPager.getCurrentItem()));
mImage.setTranslation(0f);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment