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 - Incorpora un elemento - AndroidViewque admita composición en la función de componibilidad de dibujo.- @Composable fun DrawingView() { Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> val rootView = FrameLayout(context) //... }, ) { } } }
- Cómo crear una instancia de InProgressStrokesView - Dentro del método - onCreate()de tu actividad, obtén una referencia a- InProgressStrokesViewy establece un objeto de escucha táctil para administrar la entrada del usuario.- class MainActivity : ComponentActivity(){ private lateinit var inProgressStrokesView: InProgressStrokesView // ... other variables override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) inProgressStrokesView = InProgressStrokesView(this) setContent { // ... DrawingView(inProgressStrokesView = inProgressStrokesView) } } } @Composable fun DrawingView( inProgressStrokesView: InProgressStrokesView, ) { Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> val rootView = FrameLayout(context) inProgressStrokesView.apply { layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, ) } val predictor = MotionEventPredictor.newInstance(rootView) val touchListener = View.OnTouchListener { view, event -> // ... (handle touch events) } rootView.setOnTouchListener(touchListener) rootView.addView(inProgressStrokesView) rootView }, ) {} } }
- 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 - @SuppressLint("ClickableViewAccessibility") @Composable fun DrawingSurface( inProgressStrokesView: InProgressStrokesView ) { val currentPointerId = remember { mutableStateOf<Int?>(null) } val currentStrokeId = remember { mutableStateOf<InProgressStrokeId?>(null) } val defaultBrush = Brush.createWithColorIntArgb( family = StockBrushes.pressurePenLatest, colorIntArgb = Color.Black.toArgb(), size = 5F, epsilon = 0.1F ) Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> val rootView = FrameLayout(context) inProgressStrokesView.apply { layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, ) } val predictor = MotionEventPredictor.newInstance(rootView) val touchListener = View.OnTouchListener { view, event -> predictor.record(event) val predictedEvent = predictor.predict() try { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // First pointer - treat it as inking. view.requestUnbufferedDispatch(event) val pointerIndex = event.actionIndex val pointerId = event.getPointerId(pointerIndex) currentPointerId.value = pointerId currentStrokeId.value = inProgressStrokesView.startStroke( event = event, pointerId = pointerId, brush = defaultBrush ) true } MotionEvent.ACTION_MOVE -> { val pointerId = checkNotNull(currentPointerId.value) val strokeId = checkNotNull(currentStrokeId.value) for (pointerIndex in 0 until event.pointerCount) { if (event.getPointerId(pointerIndex) != pointerId) continue inProgressStrokesView.addToStroke( event, pointerId, strokeId, predictedEvent ) } true } MotionEvent.ACTION_UP -> { val pointerIndex = event.actionIndex val pointerId = event.getPointerId(pointerIndex) check(pointerId == currentPointerId.value) val currentStrokeId = checkNotNull(currentStrokeId.value) inProgressStrokesView.finishStroke( event, pointerId, currentStrokeId ) view.performClick() true } MotionEvent.ACTION_CANCEL -> { val pointerIndex = event.actionIndex val pointerId = event.getPointerId(pointerIndex) check(pointerId == currentPointerId.value) val currentStrokeId = checkNotNull(currentStrokeId.value) inProgressStrokesView.cancelStroke(currentStrokeId, event) true } else -> false } } finally { predictedEvent?.recycle() } } rootView.setOnTouchListener(touchListener) rootView.addView(inProgressStrokesView) rootView }, ) { } } }
- 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 a- finishStroke(), 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 InProgressStrokesFinishedListeneren tu actividad o ViewModel, y registra el objeto de escucha conInProgressStrokesViewconaddFinishedStrokesListener.
- Usa el método getFinishedStrokes()deInProgressStrokesViewpara 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 - CanvasStrokesRendererconfirmarlos en la pantalla.- class MainActivity : ComponentActivity(), InProgressStrokesFinishedListener { private lateinit var inProgressStrokesView: InProgressStrokesView private val finishedStrokesState = MutableState<emptySet<Stroke>()> override fun onCreate(savedInstanceState: Bundle?) { inProgressStrokesView = InProgressStrokesView(this) inProgressStrokesView.addFinishedStrokesListener(this) canvasStrokeRenderer = CanvasStrokeRenderer.create() //... DrawingSurface( inProgressStrokesView = inProgressStrokesView, canvasStrokeRenderer = canvasStrokeRenderer, finishedStrokesState = finishedStrokesState ) //... } //... } @SuppressLint("ClickableViewAccessibility") @Composable fun DrawingSurface( inProgressStrokesView: InProgressStrokesView, finishedStrokesState: Set<Stroke> ) { val canvasStrokeRenderer = CanvasStrokeRenderer.create() //... Box(modifier = Modifier.fillMaxSize()) { AndroidView( //... ) Canvas(modifier = Modifier) { val canvasTransform = Matrix() drawContext.canvas.nativeCanvas.concat(canvasTransform) val canvas = drawContext.canvas.nativeCanvas finishedStrokesState.value.forEach { stroke -> canvasStrokeRenderer.draw(stroke = stroke, canvas = canvas, strokeToScreenTransform = canvasTransform) } } } }
- Implementa la interfaz 
