Android:SurfaceView 闪烁
Android: SurfaceView Flickering
我在绘制 SurfaceView
时遇到问题,当移动 SurfaceView
上的位图图像时,它们会闪烁(或撕裂)。我在之前的代码迭代中没有遇到过这个问题。但是现在我终于通过为每个位图使用单独的 Canvas
使位图正确缩放,这个问题开始出现。这些是我自定义的重要部分 SurfaceView
class:
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
public DrawingSurface(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
initialize(context);
}
private void initialize(Context context) {
mContext = context;
getHolder().addCallback(this);
setFocusable(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mHost = (EditorActivity) mContext;
Log.d("DrawingSurface", "SURFACE CREATED");
mDrawingThread = new DrawingThread(this, REFRESH_RATE);
mDrawingThread.setRunning(true);
mDrawingThread.start();
onConfigurationChanged(getResources().getConfiguration());
}
public Bitmap createBitmap() {
Bitmap bitmap = Bitmap.createBitmap(mBitmap);
Canvas canvas = new Canvas(bitmap);
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for (Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), new Matrix(), mPaint);
} return bitmap;
}
public void drawSurface(Canvas canvas) {
Bitmap bitmap = createBitmap();
mMatrix.setScale(1.0f / mScaleFactor, 1.0f / mScaleFactor);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
}
private void setScaleFactor() {
mScaleFactor = ((float) mBitmap.getWidth()) / getWidth();
}
public void addSticker(int drawableId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Sticker sticker = new Sticker(
BitmapFactory.decodeResource(getResources(), drawableId, options),
new PointF(mBitmap.getWidth(), mBitmap.getHeight()),
mScaleFactor);
mActiveSticker = sticker;
mStickers.add(sticker);
}
}
这是我的习惯Thread
:
public class DrawingThread extends Thread {
private volatile boolean mRunning = false;
private long mRefreshRate;
private DrawingSurface mSurface;
public DrawingThread (DrawingSurface surface, long time) {
super();
mSurface = surface;
mRefreshRate = time;
}
public void setRunning (boolean run) {
mRunning = run;
Log.d("DrawingThread", "Running: " + mRunning);
}
@Override
public void run() {
while (mRunning) {
try {
sleep(mRefreshRate);
onSurfaceUpdate();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
public void onSurfaceChanged(Configuration config, Point fit, float ratio) {
float width, height;
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
width = fit.y * ratio;
height = fit.y;
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
width = fit.x;
height = fit.x / ratio;
} else {
width = fit.x;
height = fit.x / ratio;
} mSurface.getHolder().setFixedSize((int) width, (int) height);
}
private void onSurfaceUpdate() {
Canvas canvas = null;
try {
canvas = mSurface.getHolder().lockCanvas();
synchronized (mSurface.getHolder()) {
if (canvas != null) {
mSurface.drawSurface(canvas);
}
}
} finally {
if (canvas != null) {
mSurface.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
和贴纸 class 用来分别固定 Canvas
和 Bitmap
:
public class Sticker {
private static final float START_SCALE = 0.5f;
private static final float MIN_SCALE = 0.3f;
private static final float MAX_SCALE = 7f;
private float mScale = 1f;
private float mScaleFactor;
private Canvas mCanvas = new Canvas();
private Bitmap mSticker;
private Bitmap mSurface;
private PointF mCenter = new PointF();
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Matrix mMatrix = new Matrix();
public Sticker(Bitmap sticker, PointF size, float scaleFactor) {
mSticker = sticker;
mSurface = Bitmap.createBitmap((int) size.x, (int) size.y, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mSurface);
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
setScaleFactor(scaleFactor);
postSticker();
}
private void postSticker() {
mMatrix.postScale(START_SCALE, START_SCALE);
setCenter();
setTranslate(mCenter.x, mCenter.y);
}
public boolean collider(PointF point) {
int x = (int) (point.x * mScaleFactor);
int y = (int) (point.y * mScaleFactor);
int color = mSurface.getPixel(x, y);
if(color != Color.TRANSPARENT) {
return true;
} else {
return false;
}
}
public Bitmap getBitmap() {
return mSurface;
}
public void flipSticker() {
Matrix matrix = new Matrix();
matrix.preScale(-1, 1);
mSticker = Bitmap.createBitmap(mSticker, 0, 0,
mSticker.getWidth(), mSticker.getHeight(), matrix, false);
}
public void setScaleFactor(float scaleFactor) {
mScaleFactor = scaleFactor;
}
public void setTranslate(float deltaX, float deltaY) {
mMatrix.postTranslate(deltaX * mScaleFactor, deltaY * mScaleFactor);
draw();
}
public void setScale(float deltaScale, PointF midpoint) {
mScale *= deltaScale;
if(MIN_SCALE < mScale && mScale < MAX_SCALE) {
mMatrix.postScale(deltaScale, deltaScale,
midpoint.x * mScaleFactor, midpoint.y * mScaleFactor);
} draw();
}
public void setRotate(float deltaRotate, PointF midpoint) {
mMatrix.postRotate(deltaRotate, midpoint.x * mScaleFactor, midpoint.y * mScaleFactor);
draw();
}
private void setCenter() {
float width = (mSurface.getWidth() / 2) - ((mSticker.getWidth() / 2) * START_SCALE);
float height = (mSurface.getHeight() / 2) - ((mSticker.getHeight() / 2) * START_SCALE);
mCenter.set(width, height);
}
private void draw() {
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
mCanvas.drawBitmap(mSticker, mMatrix, mPaint);
}
}
我在 Whosebug 以及其他网站上发现了关于同一主题的各种讨论。他们都得出结论,闪烁是由于 SurfaceView
如何处理缓冲,使用两个表面交换。解决方案包括使用单独的位图首先绘制所有图像,然后将该位图绘制到 SurfaceView
。如您所见,我已在 createBitmap
方法中完成此操作,但闪烁仍然存在。
我认为闪烁是由于 Canvas
在 createBitmap()
中使用位图之前未完成位图绘制。我将方法 getBitmap()
更改为 return mSticker
而不是更大的 mSurface
(用于检测用户触摸的位置),因此绘制的位图是没那么大。我将方法 getMatrix()
添加到每个 Sticker
的 return 和 mMatrix
,并将其用于 createBitmap()
中的 canvas.drawBitmap()
方法。这阻止了闪烁,即使我用于贴纸的 png 文件相当大(大多数是 1024 x 1024)。
我在绘制 SurfaceView
时遇到问题,当移动 SurfaceView
上的位图图像时,它们会闪烁(或撕裂)。我在之前的代码迭代中没有遇到过这个问题。但是现在我终于通过为每个位图使用单独的 Canvas
使位图正确缩放,这个问题开始出现。这些是我自定义的重要部分 SurfaceView
class:
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
public DrawingSurface(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
initialize(context);
}
private void initialize(Context context) {
mContext = context;
getHolder().addCallback(this);
setFocusable(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mHost = (EditorActivity) mContext;
Log.d("DrawingSurface", "SURFACE CREATED");
mDrawingThread = new DrawingThread(this, REFRESH_RATE);
mDrawingThread.setRunning(true);
mDrawingThread.start();
onConfigurationChanged(getResources().getConfiguration());
}
public Bitmap createBitmap() {
Bitmap bitmap = Bitmap.createBitmap(mBitmap);
Canvas canvas = new Canvas(bitmap);
Sticker[] stickers = mStickers.toArray(new Sticker[mStickers.size()]);
for (Sticker sticker : stickers) {
canvas.drawBitmap(sticker.getBitmap(), new Matrix(), mPaint);
} return bitmap;
}
public void drawSurface(Canvas canvas) {
Bitmap bitmap = createBitmap();
mMatrix.setScale(1.0f / mScaleFactor, 1.0f / mScaleFactor);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
}
private void setScaleFactor() {
mScaleFactor = ((float) mBitmap.getWidth()) / getWidth();
}
public void addSticker(int drawableId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Sticker sticker = new Sticker(
BitmapFactory.decodeResource(getResources(), drawableId, options),
new PointF(mBitmap.getWidth(), mBitmap.getHeight()),
mScaleFactor);
mActiveSticker = sticker;
mStickers.add(sticker);
}
}
这是我的习惯Thread
:
public class DrawingThread extends Thread {
private volatile boolean mRunning = false;
private long mRefreshRate;
private DrawingSurface mSurface;
public DrawingThread (DrawingSurface surface, long time) {
super();
mSurface = surface;
mRefreshRate = time;
}
public void setRunning (boolean run) {
mRunning = run;
Log.d("DrawingThread", "Running: " + mRunning);
}
@Override
public void run() {
while (mRunning) {
try {
sleep(mRefreshRate);
onSurfaceUpdate();
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
public void onSurfaceChanged(Configuration config, Point fit, float ratio) {
float width, height;
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
width = fit.y * ratio;
height = fit.y;
} else if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
width = fit.x;
height = fit.x / ratio;
} else {
width = fit.x;
height = fit.x / ratio;
} mSurface.getHolder().setFixedSize((int) width, (int) height);
}
private void onSurfaceUpdate() {
Canvas canvas = null;
try {
canvas = mSurface.getHolder().lockCanvas();
synchronized (mSurface.getHolder()) {
if (canvas != null) {
mSurface.drawSurface(canvas);
}
}
} finally {
if (canvas != null) {
mSurface.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
和贴纸 class 用来分别固定 Canvas
和 Bitmap
:
public class Sticker {
private static final float START_SCALE = 0.5f;
private static final float MIN_SCALE = 0.3f;
private static final float MAX_SCALE = 7f;
private float mScale = 1f;
private float mScaleFactor;
private Canvas mCanvas = new Canvas();
private Bitmap mSticker;
private Bitmap mSurface;
private PointF mCenter = new PointF();
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Matrix mMatrix = new Matrix();
public Sticker(Bitmap sticker, PointF size, float scaleFactor) {
mSticker = sticker;
mSurface = Bitmap.createBitmap((int) size.x, (int) size.y, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mSurface);
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
setScaleFactor(scaleFactor);
postSticker();
}
private void postSticker() {
mMatrix.postScale(START_SCALE, START_SCALE);
setCenter();
setTranslate(mCenter.x, mCenter.y);
}
public boolean collider(PointF point) {
int x = (int) (point.x * mScaleFactor);
int y = (int) (point.y * mScaleFactor);
int color = mSurface.getPixel(x, y);
if(color != Color.TRANSPARENT) {
return true;
} else {
return false;
}
}
public Bitmap getBitmap() {
return mSurface;
}
public void flipSticker() {
Matrix matrix = new Matrix();
matrix.preScale(-1, 1);
mSticker = Bitmap.createBitmap(mSticker, 0, 0,
mSticker.getWidth(), mSticker.getHeight(), matrix, false);
}
public void setScaleFactor(float scaleFactor) {
mScaleFactor = scaleFactor;
}
public void setTranslate(float deltaX, float deltaY) {
mMatrix.postTranslate(deltaX * mScaleFactor, deltaY * mScaleFactor);
draw();
}
public void setScale(float deltaScale, PointF midpoint) {
mScale *= deltaScale;
if(MIN_SCALE < mScale && mScale < MAX_SCALE) {
mMatrix.postScale(deltaScale, deltaScale,
midpoint.x * mScaleFactor, midpoint.y * mScaleFactor);
} draw();
}
public void setRotate(float deltaRotate, PointF midpoint) {
mMatrix.postRotate(deltaRotate, midpoint.x * mScaleFactor, midpoint.y * mScaleFactor);
draw();
}
private void setCenter() {
float width = (mSurface.getWidth() / 2) - ((mSticker.getWidth() / 2) * START_SCALE);
float height = (mSurface.getHeight() / 2) - ((mSticker.getHeight() / 2) * START_SCALE);
mCenter.set(width, height);
}
private void draw() {
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
mCanvas.drawBitmap(mSticker, mMatrix, mPaint);
}
}
我在 Whosebug 以及其他网站上发现了关于同一主题的各种讨论。他们都得出结论,闪烁是由于 SurfaceView
如何处理缓冲,使用两个表面交换。解决方案包括使用单独的位图首先绘制所有图像,然后将该位图绘制到 SurfaceView
。如您所见,我已在 createBitmap
方法中完成此操作,但闪烁仍然存在。
我认为闪烁是由于 Canvas
在 createBitmap()
中使用位图之前未完成位图绘制。我将方法 getBitmap()
更改为 return mSticker
而不是更大的 mSurface
(用于检测用户触摸的位置),因此绘制的位图是没那么大。我将方法 getMatrix()
添加到每个 Sticker
的 return 和 mMatrix
,并将其用于 createBitmap()
中的 canvas.drawBitmap()
方法。这阻止了闪烁,即使我用于贴纸的 png 文件相当大(大多数是 1024 x 1024)。