Vẽ nét

Trong bố cục dựa trên khung hiển thị, bạn cần xử lý các thao tác chạm của người dùng bên trong InProgressStrokesView ngoài MotionEventPredictor.

Để đạt được hiệu suất vẽ tối ưu, hãy sử dụng các phương thức startStroke(), addToStroke()finishStroke() của lớp InProgressStrokesView, truyền các đối tượng MotionEvent làm dữ liệu đầu vào:

  1. Thiết lập thành phần giao diện người dùng

    Đối với bố cục dựa trên khung hiển thị, hãy thêm InProgressStrokesView vào hệ phân cấp khung hiển thị của bạn.

    <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. Khởi tạo InProgressStrokesView

    Trong phương thức onCreate() của hoạt động hoặc mảnh, hãy lấy một tham chiếu đến InProgressStrokesView và đặt một trình nghe thao tác chạm để quản lý dữ liệu đầu vào của người dùng.

    Trong phương thức [onCreate()][ink-draw-include6] của hoạt động hoặc mảnh, hãy lấy thông tin tham chiếu đến InProgressStrokesView và thiết lập trình nghe thao tác chạm để quản lý hoạt động đầu vào của người dùng.

    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. Xử lý sự kiện chạm

    Sau khi thiết lập các thành phần giao diện người dùng, bạn có thể bắt đầu vẽ dựa trên các sự kiện chạm.

    MotionEvent hành động

    Phương thức InProgressStrokesView

    Nội dung mô tả

    ACTION_DOWN

    startStroke()

    Bắt đầu kết xuất nét vẽ

    ACTION_MOVE

    addToStroke()

    Kéo dài nét vẽ

    ACTION_UP

    finishStroke()

    Hoàn tất việc nhập dữ liệu, chuẩn bị hoàn thiện hình học của nét vẽ

    ACTION_CANCEL hoặc FLAG_CANCELED

    cancelStroke()

    Huỷ nét vẽ

    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. Xử lý các nét đã hoàn thành

    Sau finishStroke(), nét vẽ gần như hoàn tất. Nét vẽ được xử lý hoàn toàn và ứng dụng của bạn có thể truy cập vào nét vẽ này khi không có nét vẽ nào khác đang được xử lý. Điều này đảm bảo tất cả các thao tác vẽ đều hoàn tất trước khi nét vẽ được chuyển cho ứng dụng.

    Để truy xuất các nét đã hoàn thành, bạn có 2 lựa chọn:

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

    Sau khi truy xuất các nét vẽ đã hoàn thành, bạn có thể dùng ViewStrokeRenderer để vẽ các nét đó:

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