我有一个简单的onDraw方法,如下所示(在'SurfaceView'中),'startCount'将从1到360,填充圆圈被绘制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.drawColor(0xFFEEEEEE);
if (startCount == 360) startCount= 0;
canvas.drawArc(mShadowBounds,
0, startCount, true, mPiePaint);
}
完成一个循环后。它会像雷达探测器一样从头开始重新刷新并重新绘制。
奇怪的是,有时候,在一个完整的循环之后,它不会刷新,所以会显示黑色的大圆圈。
https://youtu.be/sc56FYUqV7M中的插图,前两个周期是我的期望。然而,当它完成第二个循环时,它仍然全黑前进,这很奇怪,它没有删除以前的绘图。我希望它能像在第二个周期中一样重复自己。
这并不总是在第二个周期后发生。有时候,在发生之前的许多周期之后,应该等待一段时间,没有任何干扰(即不接触设备)。很难预测何时会发生。
它的原因是什么?如何在这个问题上进行调试?
(仅供参考。我可以在'View'类中的'onDraw'上使用相同的算法。这个问题根本不会发生。)
附在自定义表面视图的完整代码下面,它是线程代码。
public class TimerSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private Paint mPiePaint;
private RectF mShadowBounds;
private float diameter;
int startCount = 0;
private PanelThread thread;
public TimerSurfaceView(Context context) {
super(context);
init();
}
public TimerSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TimerSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(VERSION_CODES.LOLLIPOP)
public TimerSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
getHolder().addCallback(this);
mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPiePaint.setStyle(Paint.Style.FILL);
mPiePaint.setColor(0xff000000);
setZOrderOnTop(true);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Account for padding
float xpad = (float)(getPaddingLeft() + getPaddingRight());
float ypad = (float)(getPaddingTop() + getPaddingBottom());
float ww = (float)w - xpad;
float hh = (float)h - ypad;
// Figure out how big we can make the pie.
diameter = Math.min(ww, hh);
mShadowBounds = new RectF(0, 0, diameter, diameter);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.drawColor(0xFFEEEEEE);
if (startCount == 360) startCount= 0;
canvas.drawArc(mShadowBounds,
0, startCount, true, mPiePaint);
}
public void incrementCount() {
startCount++;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
int h = resolveSizeAndState(MeasureSpec.getSize(w), heightMeasureSpec, 0);
setMeasuredDimension(w, h);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()
thread = new PanelThread(getHolder(), this); //Start the thread that
thread.setRunning(true); //will make calls to
thread.start(); //onDraw()
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
if (thread != null) {
boolean retry = true;
while (retry) {
try {
thread.setRunning(false); //Tells thread to stop
thread.join(); //Removes thread from mem.
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
thread = null;
}
}
}
Thread类
class PanelThread extends Thread {
private SurfaceHolder surfaceHolder;
private TimerSurfaceView panel;
private boolean startRunning = false;
public PanelThread(SurfaceHolder surfaceHolder, TimerSurfaceView panel) {
this.surfaceHolder = surfaceHolder;
this.panel = panel;
}
public void setRunning(boolean run) { //Allow us to stop the thread
startRunning = run;
}
@Override
public void run() {
Canvas c;
while (startRunning) { //When setRunning(false) occurs, startRunning is
c = null; //set to false and loop ends, stopping thread
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//Insert methods to modify positions of items in onDraw()
panel.incrementCount();
panel.postInvalidate();
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
答案 0 :(得分:2)
你正在混合两种不同的绘画方法。
SurfaceView有两个部分,Surface和View。 View部分通常只是一个透明的矩形,可让您“透视”View UI图层。 Surface是一个独立的图层,默认情况下位于View UI图层后面。
通过继承SurfaceView并定义onDraw()
方法,您将其视为自定义视图。你正在绘制视图部分,但你似乎没有先删除它,所以它应该仍然是大部分透明的。所以你会看到你在View上绘制的任何内容上绘制的内容。
看起来你实际上并没有在Surface上绘图,所以它应该只是黑色。
您正在创建一个单独的主题并尽可能快地调用postInvalidate()
。您锁定和解锁Surface的事实并没有太大变化,只是为了让您在Surface图层上提交一个未绘制的合成缓冲区,这是一项很多工作,考虑到它真正让你获得的是60fps的速度。无效和绘制正在主UI线程上执行,它是调用onDraw()
方法的View系统。
你应该采取某种方式。您可以使用显式绘制调用从PanelThread绘制图形(在这种情况下,我建议您根本不创建SurfaceView的子类),或者抛弃SurfaceView,只需使用custom View。一旦你完成整理,事情应该开始表现得更有意义。
更新:我怀疑您的更新似乎停止的原因是因为您有竞争条件。一个线程就是这样做的:
startCount++;
和另一个执行此操作的线程:
if (startCount == 360) startCount= 0;
如果主UI线程(执行第二行代码)看到每个增量,这只能正常运行。如果主UI线程停滞并且错过了更新,它可能会在359处看到startCount
,然后在361处看到startCount
。此时startCount >= 360
将继续递增而不会被重置,并且您的绘制函数将填充整个圈子因为:
如果扫掠角度> = 360,则完全绘制椭圆。
在* {
margin: 0;
padding: 0;
}
.container {
width: 400px;
}
img {
display: block;
}
.top,
.bottom {
width: 100%;
}
.corpus {
display: block;
width: 100%;
height: 100px;
background: url('http://f.cl.ly/items/1G3o0j0e1S32301k3y18/corpus.svg');
background-repeat: repeat-y;
}
可能修复问题时将测试更改为重新启动,但最好避免在线程之间分配逻辑。