NullPointerException
NullPointerException

Reputation: 37721

Smooth endless scrolling background with canvas?

I have a background image and what I want is for it to be moving slowly to the right and when the image has reach the right end of the screen with the end of the left starting side of the image, the image must start again showing the start of the right side, as an infinite horizontal scroll.

How can this be achieved without generating bitmap overflow memory exceptions?

I tried it drawing two times the bitmap with canvas... but it is not smooth, it is very creepy with jumps and not optimized:

universeBitmap = Bitmap.createScaledBitmap(universeBitmap, sw, sh, false); 

    universeView = new View(this){

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            
            if (paused==true){
                canvas.drawBitmap(universeBitmap, universeX, 0, null);          
                return;
            }
            long currentTime=System.currentTimeMillis();
            if ((currentTime-lastdrawTime)>100){                    
                if (universeX>sw){
                    universeX=0;
                }
                lastdrawTime=currentTime;
                universeX+=1;
            }
            canvas.drawBitmap(universeBitmap, universeX, 0, null);
            canvas.drawBitmap(universeBitmap, universeX-sw, 0, null);
            invalidate();
        }   
    };

I also tried without invalidating the view each 100ms, but with a thread and a handler, and same result.... creepy non smooth movement:

universeBitmap = Bitmap.createScaledBitmap(universeBitmap, sw, sh, false); 
            
            universeView = new View(this){
    
                @Override
                protected void onDraw(Canvas canvas) {
                    super.onDraw(canvas);               
                    canvas.drawBitmap(universeBitmap, universeX, 0, null);
                    canvas.drawBitmap(universeBitmap, universeX-sw, 0, null);
                }   
            };
.
.
.
        Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (universeX>sw){
                    universeX=0;
                }
                universeView.invalidate();
                universeX+=1;
            }
        };  

.
.
.
    
     public void run() {
            try {
               while( !backgroundThread.interrupted() ) {
                   synchronized(this){  
                       handler.sendEmptyMessage(0);   
                       wait(100);
                   }    
               }
    }

Upvotes: 0

Views: 1086

Answers (2)

CHENIKI Faraj
CHENIKI Faraj

Reputation: 1

You can try this code, i think it is really well optimized:

public class ScrollingBackgroundView extends SurfaceView implements SurfaceHolder.Callback {
    private Bitmap universeBitmap;
    private int universeX = 0;
    private int sw, sh; // Screen width and height
    private SurfaceHolder holder;
    private long lastDrawTime = 0;

    public ScrollingBackgroundView(Context context) {
        super(context);
        holder = getHolder();
        holder.addCallback(this);
        universeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.universe_image);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // Get screen width and height
        sw = getWidth();
        sh = getHeight();
        // Scale bitmap only once
        universeBitmap = Bitmap.createScaledBitmap(universeBitmap, sw * 2, sh, false);  // Double width to ensure continuous scroll
        startScrolling();
    }

    private void startScrolling() {
        // Use a ValueAnimator for smooth scrolling
        ValueAnimator animator = ValueAnimator.ofInt(0, sw);
        animator.setDuration(3000);  // Adjust duration for speed
        animator.setRepeatCount(ValueAnimator.INFINITE);  // Infinite scrolling
        animator.setInterpolator(new LinearInterpolator());  // Smooth constant speed
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                universeX = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Draw the background image at the current position
        canvas.drawBitmap(universeBitmap, universeX, 0, null);
        canvas.drawBitmap(universeBitmap, universeX - sw, 0, null);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // Handle surface changes (e.g., orientation change)
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Cleanup if needed
    }
}

Upvotes: 0

snachmsm
snachmsm

Reputation: 19263

assuming you don't want for user to scroll this background by own you may use TranslateAnimation with INFINITE param for repeat mode, it will be smooth. create 2 ImageViews horizontally (or even simple Views and setting Bitmap as background) and move their parent. when first get off the screen remove it and add another on other side. this is for background photo/image filling whole screen, if you have other size background just add more ImageViews to create 2x or more summary length than width of the screen (which can be easly measured on in onCreate or wherever. Just create something like Adapter for background image or even you may tray existing projects/libs usually named smth like HorizontalScrollView with Adapter returning some large numer in getCount, e.g. Integer.MAX_VALUE

about Bitmap errors - note that Adapter don't destroy lost/lefting Views, but use them as next (recreates) - this is convertView in getView(...) method. use ViewHolder pattern and you will use only two Views with single Bitmap setting (same refrence). you may create just once your static background Bitmap in constructor and keep in LruCache preventing from recycle it. every device can handle bitmap with same size as screen

Upvotes: 0

Related Questions