To achieve optimal drawing performance, use the
[`startStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#startStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.brush.Brush,android.graphics.Matrix,android.graphics.Matrix))(),
[`addToStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#addToStroke(androidx.ink.strokes.StrokeInputBatch,androidx.ink.authoring.InProgressStrokeId,androidx.ink.strokes.StrokeInputBatch))(),
and
[`finishStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#finishStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.authoring.InProgressStrokeId))
methods of the
[`InProgressStrokesView`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView) class, passing
[`MotionEvent`](https://developer.android.com/reference/android/view/MotionEvent) objects as input.

1. **Set up UI component**


   Integrate the [`InProgressStrokesView`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView) into your
   view hierarchy.


       <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>

   <br />

2. **Instantiate InProgressStrokesView**


   Within your activity or fragment's \[`onCreate()`\]\[ink-draw-include6\] method,
   obtain a reference to the [`InProgressStrokesView`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView) and establish a
   touch listener for managing user input.  

       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)
       }

   <br />

   | **Note:** Employing the [`MotionEventPredictor`](https://developer.android.com/reference/kotlin/androidx/input/motionprediction/MotionEventPredictor) interface improves stroke latency by predicting upcoming touch events. Use `MotionEventPredictor` for optimal performance.
3. **Handle touch events**

   Having established the UI components, you can now initiate drawing based on
   touch events.

   |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|
   | **`MotionEvent` action**                                                                                                                                                                               | **`InProgressStrokesView` method**                                                                                                                                                                                                                | **Description**                             |
   | [`ACTION_DOWN`](https://developer.android.com/reference/android/view/MotionEvent#ACTION_DOWN)                                                                                                          | [`startStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#startStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.brush.Brush,android.graphics.Matrix,android.graphics.Matrix))          | Begin stroke rendering                      |
   | [`ACTION_MOVE`](https://developer.android.com/reference/android/view/MotionEvent#ACTION_MOVE)                                                                                                          | [`addToStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#addToStroke(androidx.ink.strokes.StrokeInputBatch,androidx.ink.authoring.InProgressStrokeId,androidx.ink.strokes.StrokeInputBatch)) | Continue rendering the stroke               |
   | [`ACTION_UP`](https://developer.android.com/reference/android/view/MotionEvent#ACTION_UP)                                                                                                              | [`finishStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#finishStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.authoring.InProgressStrokeId))                                       | Finalize the stroke rendering               |
   | [`ACTION_CANCEL`](https://developer.android.com/reference/android/view/MotionEvent#ACTION_CANCEL) or [`FLAG_CANCELED`](https://developer.android.com/reference/android/view/MotionEvent#FLAG_CANCELED) | [`cancelStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#cancelStroke(androidx.ink.authoring.InProgressStrokeId,android.view.MotionEvent))                                                  | Implement palm rejection; cancel the stroke |


       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
         }
       }

   <br />

4. **Handle finished strokes**

   Upon calling
   [`finishStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#finishStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.authoring.InProgressStrokeId)),
   the stroke is marked for completion. However, the finalization process isn't
   instantaneous. The stroke is fully processed and becomes accessible
   to your application shortly after
   [`finishStroke()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#finishStroke(android.view.MotionEvent,kotlin.Int,androidx.ink.authoring.InProgressStrokeId))
   is called, specifically when there are no other strokes in progress. This
   ensures that all drawing operations are concluded before the stroke is handed
   off to the client as *finished*.

   To retrieve finished strokes, you have two options:
   - Implement the [`InProgressStrokesFinishedListener`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesFinishedListener) interface within your activity or [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel), and register the listener with the `InProgressStrokesView` with [`addFinishedStrokesListener`](https://developer.android.com/reference/androidx/ink/authoring/InProgressStrokesView#addFinishedStrokesListener(androidx.ink.authoring.InProgressStrokesFinishedListener)).
   - Use the [`getFinishedStrokes()`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView#getFinishedStrokes()) method of [`InProgressStrokesView`](https://developer.android.com/reference/kotlin/androidx/ink/authoring/InProgressStrokesView) to obtain all finished strokes directly.

       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)
         }
       }


   Once you have retrieved the finished strokes, you can use the
   [`ViewStrokeRenderer`](https://developer.android.com/reference/kotlin/androidx/ink/rendering/android/view/ViewStrokeRenderer) as a
   higher-level abstraction built on top of the
   [`CanvasStrokeRenderer`](https://developer.android.com/reference/kotlin/androidx/ink/rendering/android/canvas/CanvasStrokeRenderer).
   This can further simplify the rendering process within your view hierarchy.  

       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, ...
           }
         }
       }

   <br />