Para lograr un rendimiento de dibujo óptimo, usa los métodos startStroke()
, addToStroke()
y finishStroke()
de la clase InProgressStrokesView
y pasa objetos MotionEvent
como entrada.
Configura el componente de IU
Integra
InProgressStrokesView
en tu jerarquía de vistas.<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>
Cómo crear una instancia de InProgressStrokesView
Dentro del método [
onCreate()
][ink-draw-include6] de tu actividad o fragmento, obtén una referencia aInProgressStrokesView
y establece un objeto de escucha táctil para administrar la entrada del usuario.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) }
Cómo controlar eventos táctiles
Una vez establecidos los componentes de la IU, puedes iniciar el dibujo en función de los eventos táctiles.
Acción
MotionEvent
Método
InProgressStrokesView
Descripción
Cómo comenzar el renderizado de trazo
Continúa renderizando el trazo
Cómo finalizar la renderización del trazo
Implementa el rechazo de la palma y cancela el trazo
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 } }
Controla los trazos terminados
Cuando se llama a
finishStroke()
, el trazo se marca como completo. Sin embargo, el proceso de finalización no es instantáneo. El trazo se procesa por completo y tu aplicación puede acceder a él poco después de que se llame afinishStroke()
, en especial cuando no hay otros trazos en curso. Esto garantiza que todas las operaciones de dibujo se concluyan antes de que el trazo se entregue al cliente como terminado.Para recuperar los trazos terminados, tienes dos opciones:
- Implementa la interfaz
InProgressStrokesFinishedListener
en tu actividad o ViewModel, y registra el objeto de escucha conInProgressStrokesView
conaddFinishedStrokesListener
. - Usa el método
getFinishedStrokes()
deInProgressStrokesView
para obtener directamente todos los trazos terminados.
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) } }
Una vez que hayas recuperado los trazos terminados, puedes usar
ViewStrokeRenderer
como una abstracción de nivel superior compilada sobreCanvasStrokeRenderer
. Esto puede simplificar aún más el proceso de renderización dentro de tu jerarquía de vistas.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, ... } } }
- Implementa la interfaz