2016-04-02 26 views
2

Aşağıdaki gibi basit bir onDraw yöntemim var ('SurfaceView' içinde), 'startCount' 1'den 360'a geçecek, dolu daire çizilecektir. Bir döngü tamamlandıktan sonra ve tamamlandıktan sonra. Radar dedektörün hareket etmesi gibi, yeniden başlayıp tekrar tekrar kendini yeniden çizecek.SurfaceView onDraw, zaman zaman çizmeden önce kendini yenilemez ... neden?

Garip şeyler, tam bir döngüden sonra, zaman zaman yenilenmez, dolayısıyla büyük siyah daire bunun yerine gösterilir.

https://youtu.be/sc56FYUqV7M biçiminde, ilk 2 döngü beklediğim şeydir. Ancak ikinci döngüyü tamamladığında, ileriye doğru hareket eden tüm siyah kalır, bu da önceki çizimi kaldırmadığı için gariptir. İkinci devrede yaptığı gibi kendini tekrar etmeyi bekliyorum.

Bu, her zaman ikinci döngüden sonra gerçekleşmez. Bazen, herhangi bir müdahale olmaksızın (yani cihaza dokunmamak), meydana gelmeden önce birçok döngüden sonra bir süre beklemelidir. Ne zaman olacağını tahmin etmek zor.

Bunun nedeni nedir? Bu konuda nasıl hata ayıklanır?

Özel Yüzey Görünümü için komple kodunun altına Ekli (Bilginize. Ben 'Görünüm' sınıfında 'onDraw'da' aynı algoritmayı kullanabilir. Bu sorun. Hiç olmaz) ve Konu bulunuyor kodu.

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; 
     } 
    } 
} 

Ve Konu sınıfı

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); 
       } 
      } 

     } 
    } 
} 

cevap

2

Sen çizim için iki farklı yaklaşım yukarı karıştırma ediyoruz.

Yüzey Görünümü'nde, Yüzey ve Görünüm olmak üzere iki bölüm vardır. Görünüm kısmı normalde yalnızca View UI katmanını "görmenizi" sağlayan şeffaf bir dikdörtgendir. Yüzey, varsayılan olarak Görünüm UI katmanının arkasında bulunan bağımsız bir katmandır.

SurfaceView alt sınıfını ve bir onDraw() yöntemini tanımlayarak, özel bir Görünüm gibi davranıyorsunuz. Görünüm bölümünden çizim yapıyorsunuz, ancak önce silme gibi görünmüyor, bu yüzden hala çok şeffaf olmalı. Böylece, Yüzeyde görünen her şeyin üzerine View (Resim) üzerinde çizdiğiniz her şeyi görürsünüz.

Aslında Yüzeyde çizdiğinize benzemiyor, bu yüzden sadece siyah olmalı.

Ayrı bir iş parçacığı oluşturup postInvalidate() numaralı telefonu arayabiliyorsunuz. Yüzeyi kilitlediğiniz ve kilidini açtığınız gerçeği, yüzey tabakası üzerinde kompozisyon için çizilmemiş bir tampon göndermenize neden olmaktan çok fazla değişmez, ki bu sizin gerçekten aldığınız herşeyi hesaba katarak bir çok iş olan bir 60 fps hızdır. . Geçersizleştirme ve çizme, ana UI iş parçacığı üzerinde gerçekleştirilir ve bu sizin onDraw() yönteminizi çağıran Görünüm sistemidir.

Tek yön ya da önden gitmelisiniz. Çiziminizi PanelThread'den açık bir çizim çağrısı ile yapın (bu durumda SurfaceView'ı hiç alt sınıf olarak kullanmamanızı tavsiye ederim), ya da SurfaceView'u çivilemenizi ve bunun yerine bir custom View kullanın. Bunu sıraya koyduktan sonra işler daha anlamlı hale gelecek şekilde davranmaya başlamalıdır.

Güncelleme: Güncellemenizin durma nedeninin nedeni, bir yarış durumunuz olduğu için şüpheleniyorum. Bir iplik yapar:

startCount++; 

ve yapar başka bir iş parçacığı:

if (startCount == 360) startCount= 0; 

Bu yalnızca (kod ikinci satırı yürütme) Ana UI parçacığı her artış görürse doğru hareket eder. Ana UI iş parçacığı durdu ve bir güncelleme kaçırırsa edilirse, o 359 de startCount görebilirsiniz ve daha sonra bu noktada 361. en startCount sadece resetlemekten olmadan artan tutmak olursunuz ve beraberlik işlevi, tüm daireyi dolduracak çünkü:

Tarama açısı> = 360 ise, oval tamamen çizilir. startCount >= 360 muhtemelen şeyleri düzeltmek, ancak parçacıkları arasında mantık dağıtarak kaçınmak için en iyi olacağını zaman yeniden başlatmak için testi değiştirilmesi

.

+0

Teşekkürler fadden. Size iyi bir açıklama yapabilmeniz için size bir vesile veriyorum ve şu anda yüzey ve görünüm var. SurfaceView'ı kullandığımızda, geçersiz kılmayı ve onDraw'u geçersiz kılmanın önüne geçmemeliyiz? ... Ve her bir yüzeyin her onDraw'ın başlangıcında manuel olarak temizlenmesi gerekiyor mu? – Elye

+0

Üzgünüz, size birkaç tıklamadan sonra bu görünümün yenilenmemesiyle ilgili olarak hala bilmediğim için size bir kene vermedim. – Elye

+0

Invalidate/onDraw, Çizim üzerinde çizim yapmak içindir. Yüzey için lockCanvas/unlockCanvasAndPost'u kullanırsınız. Yüzeyde çizerken, her pikseli kirli doğrultuda (lockCanvas'a ilettiğiniz argüman) güncellemeniz gerekir; null geçiyorsunuz, bu da tüm Canvas'ları silmeniz gerektiği anlamına geliyor. Bu, Yüzey iki veya üç kez tamponlanmış olduğundan gereklidir. Bence güncellemenizin neden başarısız olduğunu tespit ettim; Cevabımı ekleyeceğim. – fadden

İlgili konular