Android
Canvas ritning med SurfaceView
Sök…
Anmärkningar
Det är viktigt att förstå det grundläggande konceptet för ytvyn innan du använder:
- Det är egentligen bara ett hål i det aktuella fönstret
- Native UI kan placeras ovanpå det
- Ritning görs med en dedicerad tråd som inte är UI
- Ritning är inte maskinvaruaccelererad
- Använder två buffertar: En visas för närvarande, en används för ritning.
-
unlockCanvasAndPost()
byter buffertar.
Dödlås kan lätt inträffa om lockCanvas()
och unlockCanvasAndPost()
inte kallas i rätt ordning.
SurfaceView med rittråd
Detta exempel beskriver hur man skapar en SurfaceView med en dedikerad rittråd. Denna implementering hanterar också kantfall såsom tillverkning av specifika problem samt att starta / stoppa tråden för att spara CPU-tid.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
/**
* Defines a custom SurfaceView class which handles the drawing thread
**/
public class BaseSurface extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener, Runnable
{
/**
* Holds the surface frame
*/
private SurfaceHolder holder;
/**
* Draw thread
*/
private Thread drawThread;
/**
* True when the surface is ready to draw
*/
private boolean surfaceReady = false;
/**
* Drawing thread flag
*/
private boolean drawingActive = false;
/**
* Paint for drawing the sample rectangle
*/
private Paint samplePaint = new Paint();
/**
* Time per frame for 60 FPS
*/
private static final int MAX_FRAME_TIME = (int) (1000.0 / 60.0);
private static final String LOGTAG = "surface";
public BaseSurface(Context context, AttributeSet attrs)
{
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setOnTouchListener(this);
// red
samplePaint.setColor(0xffff0000);
// smooth edges
samplePaint.setAntiAlias(true);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if (width == 0 || height == 0)
{
return;
}
// resize your UI
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
this.holder = holder;
if (drawThread != null)
{
Log.d(LOGTAG, "draw thread still active..");
drawingActive = false;
try
{
drawThread.join();
} catch (InterruptedException e)
{ // do nothing
}
}
surfaceReady = true;
startDrawThread();
Log.d(LOGTAG, "Created");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
// Surface is not used anymore - stop the drawing thread
stopDrawThread();
// and release the surface
holder.getSurface().release();
this.holder = null;
surfaceReady = false;
Log.d(LOGTAG, "Destroyed");
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
// Handle touch events
return true;
}
/**
* Stops the drawing thread
*/
public void stopDrawThread()
{
if (drawThread == null)
{
Log.d(LOGTAG, "DrawThread is null");
return;
}
drawingActive = false;
while (true)
{
try
{
Log.d(LOGTAG, "Request last frame");
drawThread.join(5000);
break;
} catch (Exception e)
{
Log.e(LOGTAG, "Could not join with draw thread");
}
}
drawThread = null;
}
/**
* Creates a new draw thread and starts it.
*/
public void startDrawThread()
{
if (surfaceReady && drawThread == null)
{
drawThread = new Thread(this, "Draw thread");
drawingActive = true;
drawThread.start();
}
}
@Override
public void run()
{
Log.d(LOGTAG, "Draw thread started");
long frameStartTime;
long frameTime;
/*
* In order to work reliable on Nexus 7, we place ~500ms delay at the start of drawing thread
* (AOSP - Issue 58385)
*/
if (android.os.Build.BRAND.equalsIgnoreCase("google") && android.os.Build.MANUFACTURER.equalsIgnoreCase("asus") && android.os.Build.MODEL.equalsIgnoreCase("Nexus 7"))
{
Log.w(LOGTAG, "Sleep 500ms (Device: Asus Nexus 7)");
try
{
Thread.sleep(500);
} catch (InterruptedException ignored)
{
}
}
try
{
while (drawingActive)
{
if (holder == null)
{
return;
}
frameStartTime = System.nanoTime();
Canvas canvas = holder.lockCanvas();
if (canvas != null)
{
// clear the screen using black
canvas.drawARGB(255, 0, 0, 0);
try
{
// Your drawing here
canvas.drawRect(0, 0, getWidth() / 2, getHeight() / 2, samplePaint);
} finally
{
holder.unlockCanvasAndPost(canvas);
}
}
// calculate the time required to draw the frame in ms
frameTime = (System.nanoTime() - frameStartTime) / 1000000;
if (frameTime < MAX_FRAME_TIME) // faster than the max fps - limit the FPS
{
try
{
Thread.sleep(MAX_FRAME_TIME - frameTime);
} catch (InterruptedException e)
{
// ignore
}
}
}
} catch (Exception e)
{
Log.w(LOGTAG, "Exception while locking/unlocking");
}
Log.d(LOGTAG, "Draw thread finished");
}
}
Denna layout innehåller bara den anpassade SurfaceView och maximerar den till skärmstorleken.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="sample.devcore.org.surfaceviewsample.MainActivity">
<sample.devcore.org.surfaceviewsample.BaseSurface
android:id="@+id/baseSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Aktiviteten som använder SurfaceView ansvarar för att starta och stoppa rittråden. Denna strategi sparar batteri när ritningen stoppas så snart aktiviteten kommer i bakgrunden.
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
/**
* Surface object
*/
private BaseSurface surface;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surface = (BaseSurface) findViewById(R.id.baseSurface);
}
@Override
protected void onResume()
{
super.onResume();
// start the drawing
surface.startDrawThread();
}
@Override
protected void onPause()
{
// stop the drawing to save cpu time
surface.stopDrawThread();
super.onPause();
}
}
Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow