使用表面视图时闪烁
Flickering while using surface view
我正在使用surface view显示一些图形,问题是当我在屏幕上移动图形时有闪烁的效果,我理解这是由于双缓冲问题,即使我经历了许多帖子,我无法解决问题,请查看我的代码并帮助我解决此问题。
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
}
}
闪烁通常是一个奇怪的问题,所以这是我对如何解决您的具体情况的最佳猜测。
我可以从您的代码中看到,您声明了一系列要应用于 canvas 的不同命令,这些命令在 canvas 中一次绘制一个,在代码的顺序,目前你的代码 lockCanvas
和我认为是你闪烁的原因的那些元素的组合。
因为:
- 在系统 VSYNC 期间未执行这些绘制(因为 SurfaceViews)
- 它是按顺序一次一个地完成的(这需要时间并且使闪烁很明显)。
我可以想到两个解决方案:
我可以看到您只在视图中调用了 drawColor
和 drawRect
。此外,您不会在其上执行任何耗时的操作。所以我真的没有看到使用 SurfaceView
的原因。将 class 重构为正常的 extends View
并在 onDraw
内执行绘制,并在必要时调用 invalidate()
重新绘制(我相信它会在触摸事件中)
如果您为简洁而省略的某些代码实际上确实使 SurfaceView
变得非常必要,您可以使用与屏幕 canvas。在此临时 canvas 上完成所有绘图,并仅使用屏幕 canvas 上的 drawBitmap
调用。下面是一个小示例代码。
.
// 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);
}
请记住,这只是一个示例代码而不是一个完整的解决方案,您必须执行所有正常检查 canvas 是否有效等
如果调用lockCanvas()
,则需要绘制脏矩形中的每个像素。由于您在没有脏矩形的情况下调用它,这意味着更新 Canvas.
上的每个像素
我认为您的代码存在问题,当 mTouched
为 false
时,您根本没有绘制任何东西。由于 Surface 是双缓冲或三缓冲的,您正在重新显示前一帧的内容,这将导致振动效果。
我认为您需要做的就是在 lockCanvas()
调用之前移动 mTouched
的测试,这样如果您不打算绘制任何东西,就不要翻转缓冲区。
如果您以前没有看过图形体系结构文档中的 SurfaceView lifecycle appendix,您可能想看看它,因为线程管理有时会产生惊喜。
使用这些行清除您的 surfaceviewholder 并在播放或绘制任何东西之前再次准备好它。
这些行将在至少使用一次 surfaceview 后清除 surface view
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw someting
mSurfaceHolder.unlockCanvasAndPost(canvas);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.setFormat(PixelFormat.OPAQUE);
这里这条线将准备好第二次播放视频
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
我正在使用surface view显示一些图形,问题是当我在屏幕上移动图形时有闪烁的效果,我理解这是由于双缓冲问题,即使我经历了许多帖子,我无法解决问题,请查看我的代码并帮助我解决此问题。
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
}
}
闪烁通常是一个奇怪的问题,所以这是我对如何解决您的具体情况的最佳猜测。
我可以从您的代码中看到,您声明了一系列要应用于 canvas 的不同命令,这些命令在 canvas 中一次绘制一个,在代码的顺序,目前你的代码 lockCanvas
和我认为是你闪烁的原因的那些元素的组合。
因为:
- 在系统 VSYNC 期间未执行这些绘制(因为 SurfaceViews)
- 它是按顺序一次一个地完成的(这需要时间并且使闪烁很明显)。
我可以想到两个解决方案:
我可以看到您只在视图中调用了
drawColor
和drawRect
。此外,您不会在其上执行任何耗时的操作。所以我真的没有看到使用SurfaceView
的原因。将 class 重构为正常的extends View
并在onDraw
内执行绘制,并在必要时调用invalidate()
重新绘制(我相信它会在触摸事件中)如果您为简洁而省略的某些代码实际上确实使
SurfaceView
变得非常必要,您可以使用与屏幕 canvas。在此临时 canvas 上完成所有绘图,并仅使用屏幕 canvas 上的drawBitmap
调用。下面是一个小示例代码。
.
// 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);
}
请记住,这只是一个示例代码而不是一个完整的解决方案,您必须执行所有正常检查 canvas 是否有效等
如果调用lockCanvas()
,则需要绘制脏矩形中的每个像素。由于您在没有脏矩形的情况下调用它,这意味着更新 Canvas.
我认为您的代码存在问题,当 mTouched
为 false
时,您根本没有绘制任何东西。由于 Surface 是双缓冲或三缓冲的,您正在重新显示前一帧的内容,这将导致振动效果。
我认为您需要做的就是在 lockCanvas()
调用之前移动 mTouched
的测试,这样如果您不打算绘制任何东西,就不要翻转缓冲区。
如果您以前没有看过图形体系结构文档中的 SurfaceView lifecycle appendix,您可能想看看它,因为线程管理有时会产生惊喜。
使用这些行清除您的 surfaceviewholder 并在播放或绘制任何东西之前再次准备好它。
这些行将在至少使用一次 surfaceview 后清除 surface view
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw someting
mSurfaceHolder.unlockCanvasAndPost(canvas);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.setFormat(PixelFormat.OPAQUE);
这里这条线将准备好第二次播放视频
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);