问题
I am using surface view to show some graphics, the problem is that there is a flickering effect when I am moving the figure in the screen, I understand that this is due to double buffering problem, even though I went through many posts, I am unable to fix the problem, please take a look at my code and help me get this fixed.
public class CustomSurfaceView extends SurfaceView implements Runnable{
Thread mThread = null;
SurfaceHolder mSurfaceHolder;
volatile boolean mRunning = false;
Bitmap mBitmap;
boolean mTouched;
float mTouched_x,mTouched_y;
Context mContext;
float mCurrentPosOfRect1x1,mCurrentPosOfRect1y1,mCurrentPosOfRect1x2,mCurrentPosOfRect1y2;
float mCurrentPosOfRect2x1,mCurrentPosOfRect2y1,mCurrentPosOfRect2x2,mCurrentPosOfRect2y2;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
boolean isInitialized = false;
/**
* Constructor..
*/
public CustomSurfaceView(Context context) {
super(context);
mSurfaceHolder = getHolder();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
mContext = context;
mCurrentPosOfRect1x1 = 100;
mCurrentPosOfRect1y1 = 100;
mCurrentPosOfRect1x2 = 300;
mCurrentPosOfRect1y2 = 300;
mCurrentPosOfRect2x1 = 300;
mCurrentPosOfRect2y1 = 300;
mCurrentPosOfRect2x2 = 500;
mCurrentPosOfRect2y2 = 500;
}
public void onResumeMySurfaceView(){
mRunning = true;
mThread = new Thread(this);
mThread.start();
}
public void onPauseMySurfaceView(){
boolean retry = true;
mRunning = false;
while(retry){
try {
mThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
while(mRunning){
if(mSurfaceHolder.getSurface().isValid()){
Canvas canvas = mSurfaceHolder.lockCanvas();
//... actual drawing on canvas
mPaint.setStyle(Paint.Style.STROKE);
if(mTouched){
canvas.drawColor(Color.BLACK);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setStrokeWidth(0);
mPaint.setColor(Color.CYAN);
//Left,top
//Right bottom.
if(!isInitialized){
canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2,mPaint);
isInitialized = true;
}
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setStrokeWidth(0);
mPaint.setColor(Color.BLUE);
//Left,top
//Right bottom.
if(!isInitialized){
canvas.drawRect(mCurrentPosOfRect2x1, mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2,mPaint);
isInitialized = true;
}
if(isInitialized){
//Check whether the touch points are inside the rectangle & then move...
if((mTouched_x>mCurrentPosOfRect1x1) && (mTouched_x<mCurrentPosOfRect1x2) && (mTouched_y>mCurrentPosOfRect1y1) && (mTouched_y<mCurrentPosOfRect1y2)){
mCurrentPosOfRect1x1 = mTouched_x-100;
mCurrentPosOfRect1x2 = mTouched_x+100;
mCurrentPosOfRect1y1 = mTouched_y-100;
mCurrentPosOfRect1y2 = mTouched_y+100;
}else if((mTouched_x>mCurrentPosOfRect2x1) && (mTouched_x<mCurrentPosOfRect2x2) && (mTouched_y>mCurrentPosOfRect2y1) && (mTouched_y<mCurrentPosOfRect2y2)){
mCurrentPosOfRect2x1 = mTouched_x-100;
mCurrentPosOfRect2x2 = mTouched_x+100;
mCurrentPosOfRect2y1 = mTouched_y-100;
mCurrentPosOfRect2y2 = mTouched_y+100;
}
}
canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2, mPaint);
mPaint.setColor(Color.RED);
canvas.drawRect(mCurrentPosOfRect2x1, mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2, mPaint);
mPaint.setColor(Color.BLUE);
Paint paint = new Paint() {
{
setStyle(Paint.Style.STROKE);
setStrokeCap(Paint.Cap.ROUND);
setStrokeWidth(3.0f);
setAntiAlias(true);
}
};
paint.setColor(Color.YELLOW);
final Path path = new Path();
final float x1 = mCurrentPosOfRect1x1+ 100;
final float y1 = mCurrentPosOfRect1y1 + 100;
final float x3 = (mCurrentPosOfRect2x1+ 100) ;
final float y3 = (mCurrentPosOfRect2y1 + 100);
final float x2 = (x1 +200);
final float y2 = (y1 -100);
final float x4 = (x3-100);
final float y4 = (y3+200);
path.moveTo(x1, y1);
path.cubicTo(x2,y2,x4,y4,x3,y3);
canvas.drawPath(path, paint);
}
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event){
mTouched_x = event.getX();
mTouched_y = event.getY();
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
mTouched = true;
break;
case MotionEvent.ACTION_MOVE:
mTouched = true;
break;
case MotionEvent.ACTION_UP:
mTouched = false;
break;
case MotionEvent.ACTION_CANCEL:
mTouched = false;
break;
case MotionEvent.ACTION_OUTSIDE:
mTouched = false;
break;
default:
}
return true; //processed
}
}
回答1:
If you call lockCanvas()
, you need to draw on every pixel in the dirty rect. Since you're calling it without a dirty rect, that means updating every pixel on the Canvas.
I believe the problem with your code is that, when mTouched
is false
, you're not drawing anything at all. Because the Surface is double- or triple-buffered, you're re-displaying the contents of a previous frame, which is going to cause a vibration effect.
I think all you need to do is move the test for mTouched
before the lockCanvas()
call, so you don't flip the buffers if you're not going to draw anything.
You may want to look through the SurfaceView lifecycle appendix in the graphics architecture doc if you haven't seen it before, as the thread management sometimes yields surprises.
回答2:
Clear your surfaceviewholder with these lines and make it ready again before playing or drawing any thing on it.
These lines will clear the surface view after using surfaceview at least once
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw someting
mSurfaceHolder.unlockCanvasAndPost(canvas);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.setFormat(PixelFormat.OPAQUE);
Here this line will make it ready for second time playing the video
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
回答3:
flickering is usually a weird issue, so that's my best guess on how to solve on your specific case.
I can see from your code, you're declaring a series of different commands to be applied to the canvas, those are being drawn one at a time in the canvas, in the order of the code, at the moment that your code lockCanvas
and the combination of those elements that I believe is the reason for your flickering.
Because:
- those draws are not being performed during the system VSYNC (because SurfaceViews)
- it's done one at a time in sequence (which takes time and makes the flicker noticeable).
I can think of two solutions for this:
I can see you're only calling
drawColor
anddrawRect
on your view. Also, you're not performing any time consuming on it. So I really don't see a reason for the usage ofSurfaceView
. Refactor the class to a normalextends View
and perform them drawing insideonDraw
and callinvalidate()
whenever necessary to re-draw (I believe it will be inside the touch events)If there's some code you omit for brevity that actually does make the
SurfaceView
really necessary, you can allocate a temporary canvas with a bitmap using the same size of the screen canvas. Do all the drawing on this temporary canvas and use only thedrawBitmap
call on your on-screen canvas. A small sample code for this follows.
.
// init your objects inside the `surfaceCreated` callback
Bitmap tempBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas(tempBitmap);
// then on your thread.
while(mRunning){
tempCanvas. // ... do all your drawing operations here
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawBitmap(tempBitmap, 0, 0, null);
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
remember that's just a sample code and not a complete solution, you'll have to do all the normal checks for canvas is valid, etc.
来源:https://stackoverflow.com/questions/29348022/flickering-while-using-surface-view