Aby uzyskać optymalną wydajność rysowania, użyj metod startStroke()
, addToStroke()
i finishStroke()
klasy InProgressStrokesView
, przekazując jako dane wejściowe obiekty MotionEvent
.
Konfigurowanie komponentu interfejsu użytkownika
Integracja
InProgressStrokesView
w hierarchii widoku.<FrameLayout> <ScrollView android:id="@+id/my_content" android:width="match_parent" android:height="match_parent" > <!-- Your content here. --> </ScrollView> <androidx.ink.authoring.InProgressStrokesView android:id="@+id/in_progress_strokes_view" android:width="match_parent" android:height="match_parent" /> </FrameLayout>
Tworzenie obiektu InProgressStrokesView
W metodzie [
onCreate()
][ink-draw-include6] aktywności lub fragmentu uzyskaj odwołanie doInProgressStrokesView
i ustaw odbiornik dotyku do zarządzania danymi wejściowymi użytkownika.class MyActivity : View.OnTouchListener { private lateinit var contentView: ScrollView private lateinit var inProgressStrokesView: InProgressStrokesView private lateinit var predictor: MotionEventPredictor // ... other variables override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) predictor = MotionEventPredictor.newInstance(contentView) contentView = findViewById(R.id.my_content) contentView.setOnTouchListener(touchListener) inProgressStrokesView = findViewById(R.id.in_progress_strokes_view) } // ... (touchListener implementation) }
Obsługa zdarzeń dotyku
Po utworzeniu komponentów interfejsu użytkownika możesz teraz rozpocząć rysowanie na podstawie zdarzeń dotyku.
MotionEvent
działanieInProgressStrokesView
metodaOpis
Rozpoczęcie renderowania obrysu
Kontynuuj renderowanie obrysu
Zakończ renderowanie obrysu
Zaimplementuj odrzucenie dłoni; anuluj ruch
class MyActivity : View.OnTouchListener { private lateinit var contentView: ScrollView private lateinit var inProgressStrokesView: InProgressStrokesView private var pointerId = -1 private var strokeId: InProgressStrokeId? = null private lateinit var predictor: MotionEventPredictor override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) contentView = findViewById(R.id.my_content) predictor = MotionEventPredictor.create(contentView) contentView.setOnTouchListener(touchListener) inProgressStrokesView = findViewById(R.id.in_progress_strokes_view) } private val touchListener = { view: View, event: MotionEvent -> predictor.record(event) when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // First pointer - treat it as inking. view.requestUnbufferedDispatch(event) val pointerIndex = event.actionIndex pointerIdToStrokeId[event.getPointerId(pointerIndex)] = inProgressStrokesView.startStroke(event, pointerId) return true } MotionEvent.ACTION_POINTER_DOWN -> { val stroke = strokeId ?: return false inProgressStrokesView.cancelStroke(stroke, event) strokeId = null pointerId = -1 return false } MotionEvent.ACTION_MOVE -> { val predictedEvent = predictor.predict() try { for (pointerIndex in 0 until pointerCount) { val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: continue inProgressStrokesView.addToStroke(event, pointerId, strokeId, predictedEvent) } finally { predictedEvent?.recycle() } } } MotionEvent.ACTION_UP -> { val pointerIndex = event.actionIndex val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: return false inProgressStrokesView.finishStroke(event, pointerId, strokeId) return true } MotionEvent.ACTION_CANCEL -> { val pointerIndex = event.actionIndex val strokeId = pointerIdToStrokeId[event.getPointerId(pointerIndex)] ?: return false inProgressStrokesView.cancelStroke(strokeId, event) return true } } return false } }
Obsługa zakończonych ruchów pędzla
Po wywołaniu
finishStroke()
obrys jest oznaczony jako ukończony. Proces finalizacji nie jest jednak natychmiastowy. Zarys jest w pełni przetwarzany i staje się dostępny dla aplikacji wkrótce po wywołaniu funkcjifinishStroke()
, zwłaszcza gdy nie ma żadnych innych zarysów w trakcie. Dzięki temu wszystkie operacje rysowania są zakończone, zanim ślad zostanie przekazany klientowi jako ukończony.Aby pobrać zakończone pociągnięcia, masz 2 opcje:
- Zaimplementuj interfejs
InProgressStrokesFinishedListener
w swojej aktywności lub w ViewModel i zarejestruj listenera wInProgressStrokesView
zaddFinishedStrokesListener
. - Użyj metody
getFinishedStrokes()
InProgressStrokesView
, aby uzyskać wszystkie zakończone ścieżki bezpośrednio.
class MyActivity : ComponentActivity(), InProgressStrokesFinishedListener { ... private val finishedStrokesState = mutableStateOf(emptySet<Stroke>()) override fun onCreate(savedInstanceState: Bundle?) { ... inProgressStrokesView.addFinishedStrokesListener(this) } // ... (handle touch events) @UiThread override fun onStrokesFinished(strokes: Map<InProgressStrokeId, Stroke>) { finishedStrokesState.value += strokes.values inProgressStrokesView.removeFinishedStrokes(strokes.keys) } }
Po pobraniu gotowych linii możesz użyć
ViewStrokeRenderer
jako abstrakcji wyższego poziomu utworzonej na podstawieCanvasStrokeRenderer
. Może to jeszcze bardziej uprościć proces renderowania w hierarchii widoków.class DrawingView(context: Context) : View(context) { private val viewStrokeRenderer = ViewStrokeRenderer(myCanvasStrokeRenderer, this) override fun onDraw(canvas: Canvas) { viewStrokeRenderer.drawWithStrokes(canvas) { scope -> canvas.scale(myZoomLevel) canvas.rotate(myRotation) canvas.translate(myPanX, myPanY) scope.drawStroke(myStroke) // Draw other objects including more strokes, apply more transformations, ... } } }
- Zaimplementuj interfejs