Created
November 28, 2013 13:19
-
-
Save AnderWeb/7691721 to your computer and use it in GitHub Desktop.
A little handy drawable to automatically animate another drawable moving up and down...
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
/* | |
* Copyright (C) 2013 AnderWeb (Gustavo Claramunt) | |
* | |
* Inspiration from http://cyrilmottier.com/2012/11/27/actionbar-on-the-move/ | |
* | |
* 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. | |
*/ | |
public class ScrollDrawable extends Drawable implements Animatable { | |
public static class Builder { | |
private Drawable drawable; | |
private Interpolator interpolator; | |
private long duration; | |
private float scale; | |
public Builder(Drawable wrapped) { | |
this.drawable = wrapped; | |
} | |
public Builder setDuration(long durationMillis) { | |
this.duration = durationMillis; | |
return this; | |
} | |
public Builder setInterpolator(Interpolator interpolator) { | |
this.interpolator = interpolator; | |
return this; | |
} | |
/** | |
* This is where you set the difference between the drawable height and the total scroll height. | |
* So if this has a height of 300 and the scale is 1.5f, you'll get a 450px image scrolling up and down | |
* | |
* Note: this doesn't take proportions into consideration, so images may result stretched and/or ugly :) | |
* | |
* @param scrollScale the difference between this drawable height and the scrollable height. | |
* Values <1f are ignored. | |
* @return the instance of this Builder. | |
*/ | |
public Builder setScrollScale(float scrollScale) { | |
this.scale = scrollScale; | |
return this; | |
} | |
public ScrollDrawable build() { | |
return new ScrollDrawable(drawable, interpolator, duration, scale); | |
} | |
} | |
private static final long FRAME_DURATION = 1000 / 60; | |
private float mScrollOffset; | |
private float mScrollScale; | |
private long mScrollDuration; | |
private Interpolator mInterpolator; | |
private Drawable mWrapped; | |
private final Rect mBounds = new Rect(); | |
private int mScrollHeight; | |
private long mStartTime; | |
private boolean mReverse = false; | |
private boolean mRunning = false; | |
private ScrollDrawable(Drawable wrapped, Interpolator interpolator, long duration, float scale) { | |
mWrapped = wrapped; | |
mInterpolator = interpolator != null ? interpolator : new AccelerateDecelerateInterpolator(); | |
mScrollDuration = duration > 0 ? duration : 4000; | |
mScrollScale = scale > 1f ? scale : 1.5f; | |
} | |
@Override | |
public void draw(Canvas canvas) { | |
canvas.clipRect(mBounds); | |
float currentScroll = mScrollOffset * mScrollHeight; | |
canvas.translate(0, -currentScroll); | |
mWrapped.draw(canvas); | |
canvas.translate(0, currentScroll); | |
} | |
@Override | |
protected void onBoundsChange(Rect bounds) { | |
super.onBoundsChange(bounds); | |
mBounds.set(bounds); | |
int scrollSize = (int) (bounds.height() * mScrollScale); | |
mScrollHeight = scrollSize - bounds.height(); | |
mWrapped.setBounds(bounds.left, bounds.top, bounds.right, bounds.top + scrollSize); | |
} | |
private final Runnable mUpdater = new Runnable() { | |
@Override | |
public void run() { | |
long currentTime = SystemClock.uptimeMillis(); | |
long diff = currentTime - mStartTime; | |
if (diff < mScrollDuration) { | |
float interpolation = mInterpolator.getInterpolation((float) diff / (float) mScrollDuration); | |
if (mReverse) { | |
interpolation = 1f - interpolation; | |
} | |
mScrollOffset = interpolation; | |
} else { | |
//Reverse! | |
mReverse = !mReverse; | |
mStartTime = currentTime; | |
} | |
scheduleSelf(mUpdater, currentTime + FRAME_DURATION); | |
invalidateSelf(); | |
} | |
}; | |
@Override | |
public void start() { | |
if (isRunning()) return; | |
mRunning = true; | |
mStartTime = SystemClock.uptimeMillis(); | |
scheduleSelf(mUpdater, mStartTime + FRAME_DURATION); | |
invalidateSelf(); | |
} | |
@Override | |
public void stop() { | |
if (!isRunning()) return; | |
unscheduleSelf(mUpdater); | |
mRunning = false; | |
} | |
@Override | |
public boolean isRunning() { | |
return mRunning; | |
} | |
//Rest of Drawable methods redirect to the wrapped drawable | |
@Override | |
public int getIntrinsicWidth() { | |
return mWrapped.getIntrinsicWidth(); | |
} | |
@Override | |
public int getIntrinsicHeight() { | |
return mWrapped.getIntrinsicHeight(); | |
} | |
@Override | |
public int getMinimumWidth() { | |
return mWrapped.getMinimumWidth(); | |
} | |
@Override | |
public int getMinimumHeight() { | |
return mWrapped.getMinimumHeight(); | |
} | |
@Override | |
public boolean getPadding(Rect padding) { | |
return mWrapped.getPadding(padding); | |
} | |
@Override | |
public ConstantState getConstantState() { | |
return super.getConstantState(); | |
} | |
@Override | |
public void setChangingConfigurations(int configs) { | |
mWrapped.setChangingConfigurations(configs); | |
} | |
@Override | |
public int getChangingConfigurations() { | |
return mWrapped.getChangingConfigurations(); | |
} | |
@Override | |
public void setDither(boolean dither) { | |
mWrapped.setDither(dither); | |
} | |
@Override | |
public void setFilterBitmap(boolean filter) { | |
mWrapped.setFilterBitmap(filter); | |
} | |
@Override | |
public void setAlpha(int alpha) { | |
mWrapped.setAlpha(alpha); | |
} | |
@Override | |
public void setColorFilter(ColorFilter cf) { | |
mWrapped.setColorFilter(cf); | |
} | |
@Override | |
public void setColorFilter(int color, PorterDuff.Mode mode) { | |
mWrapped.setColorFilter(color, mode); | |
} | |
@Override | |
public void clearColorFilter() { | |
mWrapped.clearColorFilter(); | |
} | |
@Override | |
public boolean isStateful() { | |
return mWrapped.isStateful(); | |
} | |
@Override | |
public boolean setState(int[] stateSet) { | |
return mWrapped.setState(stateSet); | |
} | |
@Override | |
public int[] getState() { | |
return mWrapped.getState(); | |
} | |
@Override | |
public Drawable getCurrent() { | |
return mWrapped.getCurrent(); | |
} | |
@Override | |
public boolean setVisible(boolean visible, boolean restart) { | |
return super.setVisible(visible, restart); | |
} | |
@Override | |
public int getOpacity() { | |
return mWrapped.getOpacity(); | |
} | |
@Override | |
public Region getTransparentRegion() { | |
return mWrapped.getTransparentRegion(); | |
} | |
@Override | |
protected boolean onStateChange(int[] state) { | |
mWrapped.setState(state); | |
return super.onStateChange(state); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment