New: XDA launches forum for app developers. Discuss coding, tools, marketing, and more.
XDA Developers Android and Mobile Development Forum
Forgot your password?
 
Post Reply+
Tip us?
 
Nesh_
Old
#1  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Default Android - SurfaceView flickers and sometimes does not apply the proper paint

I have been experiencing this issue for quite a while and even after googling a while, I havent found a reasonable solution for my problem.

The issue I am experiencing is that the SurfaceView I am using flickers. Once in a while, the objects I draw flicker and they change their size or colour.

It looks like that sometimes the application skips my calls to 'Paint.setColor()' and the one to 'Paint.setStrokeWidth()'.

I have made methods which change the used paint and then, in order to try to fix this issue, to set it back to the default paint values. Still the issue persists. I have also read that the problem might be due to the double buffering. Is it the case? I am using this code for the DrawingThread:

PS. u can notice that I also tried to use a dirty Rectangle to try to see if the issue can be fixed, but still nothing. [I might not have understood what it actually does.

Code:
class DrawingThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private CustomView _cv;
    private boolean _run = false;

    public DrawingThread(SurfaceHolder surfaceHolder, CustomView cv) {
        super();
        _surfaceHolder = surfaceHolder;
        _cv = cv;
    }

    public void setRunning(boolean run) {

            _run = run;

    }

    public boolean isRunning() {

        return _run;

}

    public SurfaceHolder getSurfaceHolder() {
        return _surfaceHolder;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            c = null;
            try {
                //c = _surfaceHolder.lockCanvas(new Rect(lon - range, lon + range, lat - range, lat + range));
                c = _surfaceHolder.lockCanvas();


                synchronized (_surfaceHolder) {

                    _cv.onDraw(c);                  

                }
            } finally {
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

PS. I have read somewhere I should draw on a single bitmap and then draw the bitmap on the canvas. I have tried several times but I cannot manage to do so.


Thanks in advance,

N.
 
Nesh_
Old
#2  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Is there anyone who can help me?
 
Nesh_
Old
#3  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Could someone give me a hand?
I cannot find the reason why I cannot fix this issue.

I have tried to draw the items on a bitmap first and then adding it to the canvas, but I didnt manage and I either get a white canvas or a completely black one.


I believe the issue is quite easy to solve after you do it once.
 
Nesh_
Old
#4  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Not even someone who could give some suggestions?
 
The_R
Old
#5  
Senior Member
Thanks Meter 42
Posts: 144
Join Date: Jan 2011

 
DONATE TO ME
Hey!

As far as I can see the code you posted looks fine. Can you please post the code of your CustomView class? I think there could be an issue there.

Are you trying to make a game?
 
Nesh_
Old
#6  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Hello!!!!

Finally someone


Here is the code I use for the onDraw(canvas):

Code:
@Override
	public void onDraw(Canvas canvas) {
		
		try {	
			canvas.drawColor(Color.WHITE);
		
			pt.setAntiAlias(true);
			
			canvas.rotate(-90, 0, 0);
			canvas.translate(translateX, translateY);
			canvas.scale(scale, scale);
			canvas.rotate(-rotation, lat, lon);
			
			// Drawing Points
			
			Renderer.drawStreet(gaia.getStreets(), scale, canvas, pt);
			Renderer.drawPolygon(gaia.getPolygons(), canvas, pt);
			Renderer.drawPOI(myContext, gaia.getPOIs(), canvas, pt, scale, rotation);
			Renderer.drawCustomPOI(myContext, gaia.getCustomPOI(), canvas, pt, scale, rotation);
			
			if (showLocation) {

				drawMarker(canvas);
			}
			
			if(drivingMode)
			{
				 Renderer.drawRoute(gaia.getRoute(), canvas, pt);
			}

		} catch (Exception e) {

			e.printStackTrace();
			
		}
		
		
	


	}

This is actually a map renderer.

I know the issue is very likely to be due to the double buffering, but I cannot find a way to fix it.

Thanks!

N.
 
The_R
Old
#7  
Senior Member
Thanks Meter 42
Posts: 144
Join Date: Jan 2011

 
DONATE TO ME
Can you please post the code for the callback methods (onSurfaceCreated, onSurfaceChanged, onSurfaceDestroyed) and maybe the constructor for the view too? I think the problem could be due to the setting up and not the actual rendering.

How are you accessing the surfaceHolder. Are you using the getHolder() method for that? And are you sending the reference you get to the surfaceHolder from getHolder() to the DrawingThread?
 
Nesh_
Old
(Last edited by Nesh_; 24th April 2012 at 08:51 PM.)
#8  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Here is the light version of the code:

Code:
public class CustomMapView extends SurfaceView implements
		SurfaceHolder.Callback, LocationListener {

	private DrawingThread _thread;
	private RenderingThread _threadStreets;

	Context myContext;
	Paint pt = new Paint();

	final int SCREEN_HEIGHT = 600;
	final int SCREEN_WIDTH = 480;

	float scale;

	int range = 100000;
	static public int rotation = 0;

	int sensibility = 100;

	int lat;
	int lon;

	float translateX = -lat * scale - SCREEN_HEIGHT / 2;
	float translateY = -lon * scale + SCREEN_WIDTH / 2;
	
	boolean followLocation = true;
	boolean isZooming = false;
	boolean showLocation = true;
	boolean followRotation = false;

	static boolean drivingMode = false;
	
	boolean DEBUG = true;
	Handler mHandler;

	public static GaiaApp gaia;

	

	public CustomMapView(Context context, AttributeSet attrs) {
		super(context, attrs);
		getHolder().addCallback(this);

		gaia = ((GaiaApp) context.getApplicationContext());
		
		scale = gaia.getCurScale();
		
		lat = (int) gaia.getCurPosition().getLatitude();
		lon = (int) gaia.getCurPosition().getLongitude();
		pf = new PathFinder();

		startupDB();
		myContext = context;
		mHandler = new Handler();
		_thread = new DrawingThread(getHolder(), this);
		_threadStreets = new RenderingThread(this);

		setFocusable(true);
		
		pt.setFlags(Paint.DITHER_FLAG);
		pt.setFilterBitmap(true);

		this.invalidate();
		
	}

	@Override
	public void onDraw(Canvas canvas) {
		
		try {	
			canvas.drawColor(Color.WHITE);
		
			pt.setAntiAlias(true);
		
			canvas.rotate(-90, 0, 0);
			canvas.translate(translateX, translateY);

			canvas.scale(scale, scale);
			
			
			canvas.rotate(-rotation, lat, lon);
			
			// Drawing Points
			
			Renderer.drawStreet(gaia.getStreets(), scale, canvas, pt);
			Renderer.drawPolygon(gaia.getPolygons(), canvas, pt);
			Renderer.drawPOI(myContext, gaia.getPOIs(), canvas, pt, scale, rotation);
			Renderer.drawCustomPOI(myContext, gaia.getCustomPOI(), canvas, pt, scale, rotation);
			
			if (showLocation) {

				drawMarker(canvas);
			}
			
			if(drivingMode)
			{
				 Renderer.drawRoute(gaia.getRoute(), canvas, pt);
			}

		} catch (Exception e) {

			e.printStackTrace();
			
		}
		


	}


	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {


		if (!_thread.isRunning()) {
			_thread.setRunning(true);
			_thread.start();
		}

		if (!_threadStreets.isRunning()) {
			_threadStreets.setRunning(true);
			_threadStreets.start();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
    
		boolean retry = true;
		_thread.setRunning(false);
		_threadStreets.setRunning(false);

		while (retry) {
			try {
				_thread.join();
				_threadStreets.join();
				retry = false;
			} catch (InterruptedException e) {
				// we will try it again and again...
			}
		}

	}

	class DrawingThread extends Thread {
		private SurfaceHolder _surfaceHolder;
		private CustomMapView _cmv;
		private boolean _run = false;

		public DrawingThread(SurfaceHolder surfaceHolder, CustomMapView cmv) {
			super();
			_surfaceHolder = surfaceHolder;
			_cmv = cmv;
		}

		public void setRunning(boolean run) {

			_run = run;

		}

		public boolean isRunning() {

			return _run;

		}

		public SurfaceHolder getSurfaceHolder() {
			return _surfaceHolder;
		}

		@Override
		public void run() {
			Canvas c;
			while (_run) {
				c = null;
				try {
					c = _surfaceHolder.lockCanvas();

					synchronized (_surfaceHolder) {

						_cmv.onDraw(c);

					}
				} finally {
					if (c != null) {
						_surfaceHolder.unlockCanvasAndPost(c);
					}
				}
			}
		}
	}

	class RenderingThread extends Thread {

		CustomMapView _cmv;
		Boolean _run = false;

		public RenderingThread(CustomMapView cmv) {
			super();
			_cmv = cmv;

		}

		public void setRunning(boolean run) {

			_run = run;

		}

		public boolean isRunning() {

			return _run;

		}

		@Override
		public void run() {
			while (_run) {
				try {
					
					Log.d("CustomMapView", "Retrieving...");
					new StreetRunnable(_cmv).run();
					Log.d("CustomMapView", "Retrieving");

					if(!followRotation)
						rotation = 0;
					else
						rotation = gaia.getOrientation();
					
					Log.d("CustomMapView", "Updating...");
					mHandler.post(new Runnable() {
						@Override
						public void run() {
							_cmv.invalidate();
						}

					});

					Log.d("CustomMapView", "Updated");
				} finally {
				}
			}
		}

	}


	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub

	}

	
	


}
 
Nesh_
Old
#9  
Junior Member - OP
Thanks Meter 0
Posts: 17
Join Date: Dec 2008
Hello,

have you had the chance to check it out?

Thanks

N.
 
The_R
Old
#10  
Senior Member
Thanks Meter 42
Posts: 144
Join Date: Jan 2011

 
DONATE TO ME
Sorry about that. I did not have access to the internet for the last 2 - 3 days.

Anyways I went through your code...If that is what you call light I wouldn't want to know whats heavy :P

I noticed that you have two threads which are responsible for drawing to the same view. You are using the DrawingThread to call the onDraw method of your CustomMapView object and the RenderingThread to post the invalidate method of the same CustomMapView object, which also results in a call to the onDraw method of the same view.

I am not really sure if doing this is a good thing and maybe this is what is causing the problems with the rendering? As a test, you could comment out one of the Thread.start() calls to one of the two rendering threads in your onSurfaceCreated method and check if it stops flickering.

I know this isn't how you want your app to function but if it fixes the flickering you'd know its the the two drawing threads that are causing the problem.

Code:
@Override
	public void surfaceCreated(SurfaceHolder holder) {

                // Comment out either one of the following two if blocks

		if (!_thread.isRunning()) {
			_thread.setRunning(true);
			_thread.start();
		}

		if (!_threadStreets.isRunning()) {
			_threadStreets.setRunning(true);
			_threadStreets.start();
		}
	}


XDA PORTAL POSTS

Responses From Ubuntu Pouring Into Ubuntu Touch Q&A Thread

Ubuntu has become the most successful *nix distribution for a number of reasons, … more

Heimdall Suite 1.4.0 Released

The release version of Heimdall Suite 1.4.0 is now available after an epic wait. You may remember hearing about … more

A Guide to Paid Work on XDA-Developers

For the longest time, XDA has been a bastion, a pillar in the world of development. This is … more