Để đạt được hiệu suất vẽ tối ưu, hãy sử dụng phương thức startStroke()
, addToStroke()
và finishStroke()
của lớp InProgressStrokesView
, truyền đối tượng MotionEvent
làm dữ liệu đầu vào.
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>
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 đếnInProgressStrokesView
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) }
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ả
Bắt đầu kết xuất nét vẽ
Tiếp tục kết xuất nét vẽ
Hoàn tất quá trình kết xuất nét vẽ
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 } }
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ọifinishStroke()
, đặ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:
- Triển khai giao diện
InProgressStrokesFinishedListener
trong hoạt động hoặc ViewModel và đăng ký trình nghe vớiInProgressStrokesView
bằngaddFinishedStrokesListener
. - Sử dụng phương thức
getFinishedStrokes()
củaInProgressStrokesView
để trực tiếp lấy tất cả các nét vẽ đã hoàn tất.
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ênCanvasStrokeRenderer
. Đ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, ... } } }
- Triển khai giao diện