Vẽ nét

Để đạt được hiệu suất vẽ tối ưu, hãy sử dụng phương thức startStroke(), addToStroke()finishStroke() của lớp InProgressStrokesView, truyền đố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

    Tích hợp InProgressStrokesView vào hệ phân cấp khung hiển thị.

    <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. Tạo thực thể InProgressStrokesView

    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 một 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, giờ đây, bạn có thể bắt đầu vẽ dựa trên các sự kiện chạm.

    Hành động MotionEvent

    Phương thức InProgressStrokesView

    Mô tả

    ACTION_DOWN

    startStroke()

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

    ACTION_MOVE

    addToStroke()

    Tiếp tục kết xuất nét vẽ

    ACTION_UP

    finishStroke()

    Hoàn tất quá trình kết xuất nét vẽ

    ACTION_CANCEL hoặc FLAG_CANCELED

    cancelStroke()

    Triển khai tính năng chống tì tay; huỷ thao tác 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ý nét vẽ đã hoàn tất

    Khi gọi finishStroke(), nét vẽ được đánh dấu là hoàn tất. Tuy nhiên, quá trình hoàn tất không diễn ra ngay lập tức. Nét vẽ được xử lý hoàn toàn và ứng dụng của bạn có thể truy cập được ngay sau khi gọi finishStroke(), đặc biệt là khi không có nét vẽ nào khác đang diễn ra. Điều này đảm bảo rằng tất cả các thao tác vẽ đều được kết thúc trước khi nét vẽ được chuyển cho ứng dụng dưới dạng hoàn tất.

    Để truy xuất các nét vẽ đã hoàn tất, bạn có hai 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 thiện, bạn có thể sử dụng ViewStrokeRenderer làm mô hình trừu tượng cấp cao hơn được xây dựng dựa trên CanvasStrokeRenderer. Điều này có thể đơn giản hoá thêm quy trình kết xuất trong hệ phân cấp thành phần hiển thị.

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