Dessiner un trait

Dans les mises en page basées sur des vues, vous devez gérer les entrées tactiles des utilisateurs dans un InProgressStrokesView en plus de MotionEventPredictor.

Pour obtenir des performances de dessin optimales, utilisez les méthodes startStroke(), addToStroke() et finishStroke() de la classe InProgressStrokesView, en transmettant des objets MotionEvent en entrée :

  1. Configurer le composant d'UI

    Pour les mises en page basées sur les vues, ajoutez InProgressStrokesView à votre hiérarchie de vues.

    <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>
    
  2. Instancier InProgressStrokesView

    Dans la méthode onCreate() de votre activité ou fragment, obtenez une référence à InProgressStrokesView et définissez un écouteur tactile pour gérer la saisie utilisateur.

    Dans la méthode [onCreate()][ink-draw-include6] de votre activité ou fragment, obtenir une référence à InProgressStrokesView et établir une écouteur tactile pour gérer les entrées utilisateur

    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)
    }
    
  3. Gérer les événements tactiles

    Une fois les composants d'UI établis, vous pouvez commencer à dessiner en fonction des événements tactiles.

    MotionEvent action

    Méthode InProgressStrokesView

    Description

    ACTION_DOWN

    startStroke()

    Début du rendu du tracé

    ACTION_MOVE

    addToStroke()

    Étendre le trait

    ACTION_UP

    finishStroke()

    Terminer les entrées, se préparer à finaliser la géométrie du tracé

    ACTION_CANCEL ou FLAG_CANCELED

    cancelStroke()

    Annuler le trait

    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
      }
    }
    
  4. Gérer les traits terminés

    Après finishStroke(), le tracé est presque terminé. Le tracé est entièrement traité et devient accessible à votre application une fois qu'il n'y a plus d'autres tracés en cours. Cela garantit que toutes les opérations de dessin sont terminées avant que le trait ne soit transmis au client.

    Pour récupérer les traits terminés, vous avez deux options :

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

    Une fois que vous avez récupéré les traits terminés, vous pouvez utiliser ViewStrokeRenderer pour les dessiner :

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