I\'m trying do do a infinite scrolling background with a still image in the forefront, similar too a parallax effect. This effect will be in used only once, before the user pres
I ended up using an imageView (but i think any view can be used). The view uses a handler to call invalidate on the view according to a predefined FRAME_RATE. it doesn't appear here but i used a callback to the activity to get the speed wich was based on the accelerometer. The view has 3 bitmaps as the scrolling background and 6 clouds that are randomly generated from 3 bitmaps. The clouds scroll at half the background speed.
NOTE: it's better to use functions to get the screen width and height instead of the view's height and width because when you are setting up the images the view might not have been inflated and has width and height equal to 0.
public class ScenarioView extends ImageView {
Bitmap city1;
Bitmap city2;
Bitmap city3;
Bitmap mCloud1;
Bitmap mCloud2;
Bitmap mCloud3;
private class CloudPosition{
int BitmapId;
int mCloudX;
int mCloudY;
}
CloudPosition[] clouds;
private static final int MAX_CLOUDS = 6;
private int FRAME_RATE = 30;
private Handler mHandler;
private mSpeed1 = 5;
private mSpeed2 = mSpeed1 / 2;
public ScenarioView(Context context, AttributeSet attrs){
super(context, attrs);
setBackgroundResource(R.drawable.animation_sky);
mHandler = new Handler();
clouds = new CloudPosition[MAX_CLOUDS];
}
private Runnable r = new Runnable() {
@Override
public void run() {
invalidate();
}
};
public void setCity1Bitmap(Bitmap city){
city1 = city;
}
public void setCity2Bitmap(Bitmap city){
city2 = city;
}
public void setCity3Bitmap(Bitmap city){
city3 = city;
}
private void generateCloudPositions() {
int rowSeparator = getHeight() / 2;
Random r = new Random(System.currentTimeMillis());
int minY = 0;
int maxY = rowSeparator;
int minX = 0;
int maxX = getWidth();
// Generate 1st row
int y = r.nextInt(maxY - minY + 1) + minY;
int x = r.nextInt(maxX - minX + 1) + minX;
int cloudId = r.nextInt(3);
setupCloud(0, x, y, cloudId);
y = r.nextInt(maxY - minY + 1) + minY;
x = r.nextInt(maxX - minX + 1) + minX;
cloudId = r.nextInt(3);
setupCloud(1, x, y, cloudId);
y = r.nextInt(maxY - minY + 1) + minY;
x = r.nextInt(maxX - minX + 1) + minX;
cloudId = r.nextInt(3);
setupCloud(2, x, y, cloudId);
minY = rowSeparator;
maxY = getHeight();
// Generate 2nd row
y = r.nextInt(maxY - minY + 1) + minY;
x = r.nextInt(maxX - minX + 1) + minX;
cloudId = r.nextInt(3);
setupCloud(3, x, y, cloudId);
y = r.nextInt(maxY - minY + 1) + minY;
x = r.nextInt(maxX - minX + 1) + minX;
cloudId = r.nextInt(3);
setupCloud(4, x, y, cloudId);
y = r.nextInt(maxY - minY + 1) + minY;
x = r.nextInt(maxX - minX + 1) + minX;
cloudId = r.nextInt(3);
setupCloud(5, x, y, cloudId);
}
public void setCloudsBitmaps(Bitmap cloud1, Bitmap cloud2, Bitmap cloud3){
mCloud1 = cloud1;
mCloud2 = cloud2;
mCloud3 = cloud3;
generateCloudPositions();
}
private void setupCloud(int cloudNum, int x, int y, int cloudId){
CloudPosition cp = new CloudPosition();
cp.mCloudX = x;
cp.mCloudY = y;
cp.BitmapId = cloudId;
clouds[cloudNum] = cp;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mSpeed1 = mCallbacks.getSpeed();
mSpeed2 = mSpeed1 / 2;
mBGFarMoveX = mBGFarMoveX - mSpeed1;
// decrement the near background
mBGNearMoveX = mBGNearMoveX - mSpeed1;
if(city1 != null && city2 != null && city3 != null){
if(mCloud1 != null && mCloud2 != null && mCloud3 != null){
drawClouds(canvas);
}
drawLandscape(canvas);
}
if(mCar != null){
canvas.drawBitmap(mCar, mCarX, mCarY, null);
}
if(mWheel != null){
drawWheels(canvas);
}
if(mBridge != null){
drawBridge(canvas);
}
mHandler.postDelayed(r, FRAME_RATE);
}
private void drawLandscape(Canvas canvas) {
// calculate the wrap factor for matching image draw
int newFarX = city1.getWidth() + mBGFarMoveX;
// if we have scrolled all the way, reset to start
int bgY = getHeight() - city1.getHeight();
if(mSpeed1 > 0 && newFarX <= 0){
mBGFarMoveX = 0;
increseLandscape();
}
if(mSpeed1 < 0 && mBGFarMoveX >= getWidth()){
mBGFarMoveX = (mBGFarMoveX - city1.getWidth());
decreseLandscape();
}
if(newFarX >= 0 && newFarX <= getWidth()){
switch (currCity) {
case 0:
canvas.drawBitmap(city1, mBGFarMoveX, bgY, null);
canvas.drawBitmap(city2, newFarX, bgY, null);
break;
case 1:
canvas.drawBitmap(city2, mBGFarMoveX, bgY, null);
canvas.drawBitmap(city3, newFarX, bgY, null);
break;
case 2:
canvas.drawBitmap(city3, mBGFarMoveX, bgY, null);
canvas.drawBitmap(city1, newFarX, bgY, null);
break;
}
}
if(mBGFarMoveX >= 0 && mBGFarMoveX <= getWidth()){
switch (currCity) {
case 0:
canvas.drawBitmap(city3, mBGFarMoveX - city3.getWidth(), bgY, null);
canvas.drawBitmap(city1, mBGFarMoveX, bgY, null);
break;
case 1:
canvas.drawBitmap(city1, mBGFarMoveX - city1.getWidth(), bgY, null);
canvas.drawBitmap(city2, mBGFarMoveX, bgY, null);
break;
case 2:
canvas.drawBitmap(city2, mBGFarMoveX - city2.getWidth(), bgY, null);
canvas.drawBitmap(city3, mBGFarMoveX, bgY, null);
break;
}
}
if(mBGFarMoveX <= 0 && newFarX >= getWidth()){
switch (currCity) {
case 0:
canvas.drawBitmap(city1, mBGFarMoveX, bgY, null);
break;
case 1:
canvas.drawBitmap(city2, mBGFarMoveX, bgY, null);
break;
case 2:
canvas.drawBitmap(city3, mBGFarMoveX, bgY, null);
break;
}
}
}
private void drawClouds(Canvas canvas) {
int width = getWidth();
for(int i = 0; i < MAX_CLOUDS; i++){
Bitmap cloud;
clouds[i].mCloudX = clouds[i].mCloudX - mSpeed2;
switch (clouds[i].BitmapId) {
case 0:
cloud = mCloud1;
break;
case 1:
cloud = mCloud2;
break;
case 2:
cloud = mCloud3;
break;
default:
cloud = mCloud1;
break;
}
int cloudX1 = clouds[i].mCloudX;
int cloudX2 = cloudX1 + cloud.getWidth();
if (cloudX2 <= 0 && mSpeed2 > 0) {
clouds[i].mCloudX = clouds[i].mCloudX + (5 * cloud.getWidth());
cloudX1 = clouds[i].mCloudX;
cloudX2 = cloudX1 + cloud.getWidth();
}
if (cloudX1 >= width && mSpeed2 < 0) {
clouds[i].mCloudX = clouds[i].mCloudX - (5 * cloud.getWidth());
cloudX1 = clouds[i].mCloudX;
cloudX2 = cloudX1 + cloud.getWidth();
}
if(cloudX1 < width && cloudX2 > 0){
canvas.drawBitmap(cloud, clouds[i].mCloudX, clouds[i].mCloudY, null);
}
}
}
}
Then i used the view like this
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.animation.ScenarioView
android:id="@+id/bg_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAlignBottom="true">
</com.animation.ScenarioView>
</FrameLayout>
Here's a thread which might be of interest to you: How to scroll "infinitely" wide view in Android?
One more thing that I managed to find is this one: http://github.com/commonsguy/cwac-endless Those two can be of big help to you.
One more thing I managed to find is this infinite gallery (seems to me more like a workaround in your case): http://code.google.com/p/infinite-gallery/
And a suggestion regarding the last link:
Have your adapter return some absurdly large number for it's count so the system thinks it's huge and will continue to scroll. Set your initial starting position to the halfway point. You will have a long way to scroll before you actually hit either end. Meanwhile you use a mod operation to keep within your real bounds for accessing your items.