33import android .content .Context ;
44import android .content .res .TypedArray ;
55import android .graphics .Bitmap ;
6- import android .graphics .BitmapFactory ;
76import android .graphics .Canvas ;
7+ import android .graphics .Paint ;
88import android .graphics .Rect ;
9+ import android .graphics .drawable .BitmapDrawable ;
10+ import android .graphics .drawable .Drawable ;
911import android .util .AttributeSet ;
1012import android .util .TypedValue ;
1113import android .view .View ;
2123/**
2224 * Created by thijs on 08-06-15.
2325 */
26+ @ SuppressWarnings ("FieldCanBeLocal" ) // Declaring fields outside of onDraw improves performance
2427public class ScrollingImageView extends View {
2528 public static ScrollingImageViewBitmapLoader BITMAP_LOADER = new ScrollingImageViewBitmapLoader () {
2629 @ Override
27- public Bitmap loadBitmap (Context context , int resourceId ) {
28- return BitmapFactory .decodeResource (context .getResources (), resourceId );
30+ public Bitmap loadDrawable (Context context , int resourceId ) {
31+ Drawable drawable = context .getResources ().getDrawable (resourceId , context .getTheme ());
32+ if (drawable instanceof BitmapDrawable ) {
33+ return ((BitmapDrawable ) drawable ).getBitmap ();
34+ }
35+
36+ // Render any other kind of drawable to a bitmap
37+ Bitmap bitmap = Bitmap .createBitmap (drawable .getIntrinsicWidth (),
38+ drawable .getIntrinsicHeight (), Bitmap .Config .ARGB_8888 );
39+ Canvas canvas = new Canvas (bitmap );
40+ drawable .setBounds (0 , 0 , canvas .getWidth (), canvas .getHeight ());
41+ drawable .draw (canvas );
42+
43+ return bitmap ;
2944 }
3045 };
3146
47+ public Paint paint = null ;
48+
3249 private List <Bitmap > bitmaps ;
33- private float speed ;
50+ /** Pixels per second */
51+ private final double speed ;
3452 private int [] scene ;
3553 private int arrayIndex = 0 ;
3654 private int maxBitmapHeight = 0 ;
3755
38- private Rect clipBounds = new Rect ();
56+ private final Rect clipBounds = new Rect ();
3957 private float offset = 0 ;
4058
59+ private static final double NANOS_PER_SECOND = 1e9 ;
60+ /** Moment when the last call to onDraw() started */
61+ private long lastFrameInstant = -1 ;
62+ private long frameTimeNanos = -1 ;
63+
4164 private boolean isStarted ;
4265
4366 public ScrollingImageView (Context context , AttributeSet attrs ) {
4467 super (context , attrs );
45- TypedArray ta = context .obtainStyledAttributes (attrs , R .styleable .ParallaxView , 0 , 0 );
68+ TypedArray ta = context .obtainStyledAttributes (attrs , R .styleable .ScrollingImageView , 0 , 0 );
4669 int initialState = 0 ;
4770 try {
48- initialState = ta .getInt (R .styleable .ParallaxView_initialState , 0 );
49- speed = ta .getDimension (R .styleable .ParallaxView_speed , 10 );
50- int sceneLength = ta .getInt (R .styleable .ParallaxView_sceneLength , 1000 );
51- final int randomnessResourceId = ta .getResourceId (R .styleable .ParallaxView_randomness , 0 );
71+ initialState = ta .getInt (R .styleable .ScrollingImageView_initialState , 0 );
72+ speed = ta .getDimension (R .styleable .ScrollingImageView_speed , 60 );
73+ int sceneLength = ta .getInt (R .styleable .ScrollingImageView_sceneLength , 1000 );
74+ final int randomnessResourceId = ta .getResourceId (R .styleable .ScrollingImageView_randomness , 0 );
75+ // When true, randomness is ignored and bitmaps are loaded in the order as they appear in the src array */
76+ final boolean contiguous = ta .getBoolean (R .styleable .ScrollingImageView_contiguous , false );
77+
5278 int [] randomness = new int [0 ];
5379 if (randomnessResourceId > 0 ) {
5480 randomness = getResources ().getIntArray (randomnessResourceId );
5581 }
5682
57- int type = isInEditMode () ? TypedValue .TYPE_STRING : ta .peekValue (R .styleable .ParallaxView_src ).type ;
83+ int type = isInEditMode () ? TypedValue .TYPE_STRING : ta .peekValue (R .styleable .ScrollingImageView_source ).type ;
5884 if (type == TypedValue .TYPE_REFERENCE ) {
59- int resourceId = ta .getResourceId (R .styleable .ParallaxView_src , 0 );
85+ int resourceId = ta .getResourceId (R .styleable .ScrollingImageView_source , 0 );
6086 TypedArray typedArray = getResources ().obtainTypedArray (resourceId );
6187 try {
6288 int bitmapsSize = 0 ;
@@ -72,7 +98,7 @@ public ScrollingImageView(Context context, AttributeSet attrs) {
7298 multiplier = Math .max (1 , randomness [i ]);
7399 }
74100
75- Bitmap bitmap = BITMAP_LOADER .loadBitmap (getContext (), typedArray .getResourceId (i , 0 ));
101+ Bitmap bitmap = BITMAP_LOADER .loadDrawable (getContext (), typedArray .getResourceId (i , 0 ));
76102 for (int m = 0 ; m < multiplier ; m ++) {
77103 bitmaps .add (bitmap );
78104 }
@@ -83,13 +109,17 @@ public ScrollingImageView(Context context, AttributeSet attrs) {
83109 Random random = new Random ();
84110 this .scene = new int [sceneLength ];
85111 for (int i = 0 ; i < this .scene .length ; i ++) {
86- this .scene [i ] = random .nextInt (bitmaps .size ());
112+ if (contiguous ){
113+ this .scene [i ] = i % bitmaps .size ();
114+ } else {
115+ this .scene [i ] = random .nextInt (bitmaps .size ());
116+ }
87117 }
88118 } finally {
89119 typedArray .recycle ();
90120 }
91121 } else if (type == TypedValue .TYPE_STRING ) {
92- final Bitmap bitmap = BITMAP_LOADER .loadBitmap (getContext (), ta .getResourceId (R .styleable .ParallaxView_src , 0 ));
122+ final Bitmap bitmap = BITMAP_LOADER .loadDrawable (getContext (), ta .getResourceId (R .styleable .ScrollingImageView_source , 0 ));
93123 if (bitmap != null ) {
94124 bitmaps = singletonList (bitmap );
95125 scene = new int []{0 };
@@ -116,6 +146,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
116146 @ Override
117147 public void onDraw (Canvas canvas ) {
118148 if (!isInEditMode ()) {
149+ if (lastFrameInstant == -1 ){
150+ lastFrameInstant = System .nanoTime ();
151+ }
152+ frameTimeNanos = System .nanoTime () - lastFrameInstant ;
153+ lastFrameInstant = System .nanoTime ();
154+
119155 super .onDraw (canvas );
120156 if (canvas == null || bitmaps .isEmpty ()) {
121157 return ;
@@ -132,12 +168,13 @@ public void onDraw(Canvas canvas) {
132168 for (int i = 0 ; left < clipBounds .width (); i ++) {
133169 Bitmap bitmap = getBitmap ((arrayIndex + i ) % scene .length );
134170 int width = bitmap .getWidth ();
135- canvas .drawBitmap (bitmap , getBitmapLeft (width , left ), 0 , null );
171+ canvas .drawBitmap (bitmap , getBitmapLeft (width , left ), 0 , paint );
136172 left += width ;
137173 }
138174
139175 if (isStarted && speed != 0 ) {
140- offset -= abs (speed );
176+
177+ offset -= (abs (speed ) / NANOS_PER_SECOND ) * frameTimeNanos ;
141178 postInvalidateOnAnimation ();
142179 }
143180 }
@@ -161,24 +198,20 @@ private float getBitmapLeft(float layerWidth, float left) {
161198 public void start () {
162199 if (!isStarted ) {
163200 isStarted = true ;
201+ lastFrameInstant = -1 ;
164202 postInvalidateOnAnimation ();
165203 }
166204 }
167205
168206 /**
169207 * Stop the animation
170208 */
209+ @ SuppressWarnings ("unused" )
171210 public void stop () {
172211 if (isStarted ) {
173212 isStarted = false ;
213+ lastFrameInstant = -1 ;
174214 invalidate ();
175215 }
176216 }
177-
178- public void setSpeed (float speed ) {
179- this .speed = speed ;
180- if (isStarted ) {
181- postInvalidateOnAnimation ();
182- }
183- }
184- }
217+ }
0 commit comments