Aby stworzyć prostą grę, użyłem szablonu, który rysuje płótno z bitmapami w następujący sposób:
private void doDraw(Canvas canvas) {
for (int i=0;i<8;i++)
for (int j=0;j<9;j++)
for (int k=0;k<7;k++) {
canvas.drawBitmap(mBits[allBits[i][j][k]], i*50 -k*7, j*50 -k*7, null); } }
(Kanwa jest zdefiniowana w „run ()” / SurfaceView znajduje się w GameThread).
Moje pierwsze pytanie brzmi: jak wyczyścić (lub przerysować) całe płótno dla nowego układu?
Po drugie, jak mogę zaktualizować tylko część ekranu?
// This is the routine that calls "doDraw":
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c); }
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c); } } } }
źródło
mSurfaceHolder.unlockCanvasAndPost(c)
a potemc = mSurfaceHolder.lockCanvas(null)
, to nowyc
nie zawiera tego samego, co poprzednic
. Nie można zaktualizować tylko części SurfaceView, o co chyba pytał OP.Canvas
zachowuje stare rysunki.Rysowanie przezroczystego koloru za pomocą trybu czyszczenia PorterDuff załatwia to, co chciałem.
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
źródło
Bitmap#eraseColor(Color.TRANSPARENT)
, jak w odpowiedzi HeMaca poniżej.Color.TRANSPARENT
jest niepotrzebne.PorterDuff.Mode.CLEAR
jest w zupełności wystarczający dlaARGB_8888
mapy bitowej, co oznacza ustawienie alfa i koloru na [0, 0]. Innym sposobem jest użycieColor.TRANSPARENT
zPorterDuff.Mode.SRC
.Znalazłem to w grupach Google i to zadziałało.
Paint clearPaint = new Paint(); clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawRect(0, 0, width, height, clearPaint);
Spowoduje to usunięcie prostokątów rysunków itp. Przy zachowaniu ustawionej mapy bitowej.
źródło
użyj metody resetowania klasy Path
Path.reset();
źródło
Wypróbowałem odpowiedź @mobistry:
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
Ale to nie zadziałało.
Rozwiązaniem dla mnie było:
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
Może ktoś ma ten sam problem.
źródło
mBitmap.eraseColor(Color.TRANSPARENT); canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
źródło
proszę wkleić poniższy kod do konstruktora klasy rozszerzającej surfaceview .............
kodowanie konstruktorów
SurfaceHolder holder = getHolder(); holder.addCallback(this); SurfaceView sur = (SurfaceView)findViewById(R.id.surfaceview); sur.setZOrderOnTop(true); // necessary holder = sur.getHolder(); holder.setFormat(PixelFormat.TRANSPARENT);
kodowanie xml
<com.welcome.panelview.PanelViewWelcomeScreen android:id="@+id/one" android:layout_width="600px" android:layout_height="312px" android:layout_gravity="center" android:layout_marginTop="10px" android:background="@drawable/welcome" />
wypróbuj powyższy kod ...
źródło
Oto kod minimalnego przykładu pokazującego, że zawsze musisz przerysować każdy piksel kanwy w każdej klatce.
To działanie rysuje nową mapę bitową co sekundę na SurfaceView, bez wcześniejszego czyszczenia ekranu. Jeśli to przetestujesz, zobaczysz, że mapa bitowa nie zawsze jest zapisywana w tym samym buforze, a ekran będzie przełączał się między dwoma buforami.
Przetestowałem to na swoim telefonie (Nexus S, Android 2.3.3) oraz na emulatorze (Android 2.2).
public class TestCanvas extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new TestView(this)); } } class TestView extends SurfaceView implements SurfaceHolder.Callback { private TestThread mThread; private int mWidth; private int mHeight; private Bitmap mBitmap; private SurfaceHolder mSurfaceHolder; public TestView(Context context) { super(context); mThread = new TestThread(); mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon); mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mWidth = width; mHeight = height; mThread.start(); } @Override public void surfaceCreated(SurfaceHolder holder) {/* Do nothing */} @Override public void surfaceDestroyed(SurfaceHolder holder) { if (mThread != null && mThread.isAlive()) mThread.interrupt(); } class TestThread extends Thread { @Override public void run() { while (!isInterrupted()) { Canvas c = null; try { c = mSurfaceHolder.lockCanvas(null); synchronized (mSurfaceHolder) { c.drawBitmap(mBitmap, (int) (Math.random() * mWidth), (int) (Math.random() * mHeight), null); } } finally { if (c != null) mSurfaceHolder.unlockCanvasAndPost(c); } try { sleep(1000); } catch (InterruptedException e) { interrupt(); } } } } }
źródło
SurfaceHolder.Callback
, a nie tylkoCallback
.Canvas
jeśli chcemy w pełni przerysować cały ekran. To sprawia, że moje stwierdzenie o „poprzednich rysunkach” jest prawdziwe.Canvas
Utrzymuje wcześniejsze rysunki.Canvas
zachowuje poprzednie rysunki. Ale to jest kompletnie bezużyteczne, problem w tym, że kiedy używaszlockCanvas()
, nie wiesz, jakie są te „poprzednie rysunki” i nie możesz nic o nich założyć. Być może, jeśli istnieją dwa działania z SurfaceView o tym samym rozmiarze, będą one współużytkować tę samą kanwę. Być może dostaniesz kawałek niezainicjowanej pamięci RAM z losowymi bajtami. Być może, że zawsze na płótnie jest wypisane logo Google. Nie możesz wiedzieć. Każda aplikacja, która nie rysuje po każdym pikselu,lockCanvas(null)
jest zepsuta.Dla mnie dzwonienie
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
lub coś podobnego działałoby dopiero po dotknięciu ekranu. Więc nazwałbym powyższą linię kodu, ale ekran byłby jasny dopiero po tym, jak dotknąłem ekranu. Więc to, co zadziałało, to wywołanie,invalidate()
a następnie,init()
które jest wywoływane w momencie tworzenia, aby zainicjować widok.private void init() { setFocusable(true); setFocusableInTouchMode(true); setOnTouchListener(this); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(Color.BLACK); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(6); mCanvas = new Canvas(); mPaths = new LinkedList<>(); addNewPath(); }
źródło
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
źródło
Wymazywanie na kanwie w java Android jest podobne do kasowania HTML Canvas za pomocą javascript z globalCompositeOperation. Logika była podobna.
U wybierze logikę DST_OUT (Destination Out).
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
Uwaga: DST_OUT jest bardziej przydatne, ponieważ może wymazać 50%, jeśli kolor farby ma 50% alfa. Tak więc, aby całkowicie przejść do przezroczystości, alfa koloru musi wynosić 100%. Zaleca się zastosowanie paint.setColor (Color.WHITE). I upewnij się, że format obrazu płótna to RGBA_8888.
Po wymazaniu wróć do normalnego rysowania za pomocą SRC_OVER (Source Over).
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
Aktualizacja małego obszaru wyświetlania będzie wymagała dosłownie dostępu do sprzętu graficznego i może nie być obsługiwana.
Najbardziej zbliżona do najwyższej wydajności jest użycie wielu warstw obrazu.
źródło
Nie zapomnij wywołać invalidate ();
canvas.drawColor(backgroundColor); invalidate(); path.reset();
źródło
invalidate
po narysowaniu czegoś?Spróbuj usunąć widok przy onPause () działania i dodaj onRestart ()
LayoutYouAddedYourView.addView (YourCustomView); LayoutYouAddedYourView.removeView (YourCustomView);
W momencie dodania widoku zostanie wywołana metoda onDraw ().
YourCustomView, to klasa, która rozszerza klasę View.
źródło
W moim przypadku rysuję płótno w linearnym układzie. Aby ponownie wyczyścić i przerysować:
LinearLayout linearLayout = findViewById(R.id.myCanvas); linearLayout.removeAllViews();
a następnie dzwonię do klasy z nowymi wartościami:
Lienzo fondo = new Lienzo(this,items); linearLayout.addView(fondo);
To jest klasa Lienzo:
class Lienzo extends View { Paint paint; RectF contenedor; Path path; ArrayList<Items>elementos; public Lienzo(Context context,ArrayList<Items> elementos) { super(context); this.elementos=elementos; init(); } private void init() { path=new Path(); paint = new Paint(); contenedor = new RectF(); paint.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); contenedor.left = oneValue; contenedor.top = anotherValue; contenedor.right = anotherValue; contenedor.bottom = anotherValue; float angulo = -90; //starts drawing at 12 o'clock //total= sum of all element values for (int i=0;i<elementos.size();i++){ if (elementos.get(i).angulo!=0 && elementos.get(i).visible){ paint.setColor(elementos.get(i).backColor); canvas.drawArc(contenedor,angulo,(float)(elementos.get(i).value*360)/total,true,paint); angulo+=(float)(elementos.get(i).value*360)/total; } } //for example } }
źródło
W następujący sposób możesz wyczyścić całe płótno lub tylko jego część.
Nie zapomnij wyłączyć akceleracji sprzętowej, ponieważ PorterDuff.Mode.CLEAR nie działa z akceleracją sprzętową i na koniec wywołaj,
setWillNotDraw(false)
ponieważ nadpisujemyonDraw
metodę.//view's constructor setWillNotDraw(false); setLayerType(LAYER_TYPE_SOFTWARE, null); //view's onDraw Paint TransparentPaint = new Paint(); TransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawRect(0, 0, width, height, TransparentPaint);
źródło
LAYER_TYPE_HARDWARE
naLAYER_TYPE_SOFTWARE
Twoje pierwsze wymaganie, jak wyczyścić lub przerysować całe płótno - Odpowiedź - użyj metody canvas.drawColor (color.Black), aby wyczyścić ekran kolorem czarnym lub jakimkolwiek innym.
Twoje drugie wymaganie, jak zaktualizować część ekranu - Odpowiedź - na przykład, jeśli chcesz, aby wszystkie inne rzeczy na ekranie pozostały niezmienione, ale w małym obszarze ekranu, aby wyświetlić liczbę całkowitą (powiedzmy licznik), która rośnie co pięć sekund. następnie użyj metody canvas.drawrect, aby narysować ten mały obszar, określając lewy górny prawy dół i malując. następnie oblicz wartość swojego licznika (używając postdalayed przez 5 sekund itp., l jak Handler.postDelayed (Runnable_Object, 5000);), przekonwertuj go na ciąg tekstowy, oblicz współrzędne xiy w tym małym prostokącie i użyj widoku tekstu, aby wyświetlić zmieniające się wartość licznika.
źródło
Musiałem użyć osobnego przebiegu rysowania, aby wyczyścić płótno (zablokować, narysować i odblokować):
Canvas canvas = null; try { canvas = holder.lockCanvas(); if (canvas == null) { // exit drawing thread break; } canvas.drawColor(colorToClearFromCanvas, PorterDuff.Mode.CLEAR); } finally { if (canvas != null) { holder.unlockCanvasAndPost(canvas); } }
źródło
Zadzwoń
canvas.drawColor (Color.TRANSPARENT)
źródło
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
. Jeśli się nie mylę, kolor może być właściwie dowolny (nie musi być PRZEZROCZYSTY), ponieważPorterDuff.Mode.CLEAR
po prostu wyczyści bieżący klip (jak przebicie dziury w płótnie).