Created
March 3, 2017 22:07
-
-
Save carlonzo/a8b156c8eafb38b14ee1a95f0fa79706 to your computer and use it in GitHub Desktop.
ParallaxScrollingImages yahoo weather like
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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