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() và finishStroke() của lớp InProgressStrokesView, truyền các đố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
Đối với bố cục dựa trên khung hiển thị, hãy thêm
InProgressStrokesViewvà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>Khởi tạo
InProgressStrokesViewTrong phương thức
onCreate()của hoạt động hoặc mảnh, hãy lấy một tham chiếu đếnInProgressStrokesViewvà đặ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 đếnInProgressStrokesViewvà 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) }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.
MotionEventhành độngPhương thức
InProgressStrokesViewNội dung mô tả
Bắt đầu kết xuất nét vẽ
Kéo dài nét vẽ
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ẽ
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 } }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:
- Triển khai giao diện
InProgressStrokesFinishedListenertrong hoạt động hoặc ViewModel của bạn, rồi đăng ký trình nghe bằngInProgressStrokesViewbằng cách gọiaddFinishedStrokesListener. - Gọi
InProgressStrokesView.getFinishedStrokes()để nhận trực tiếp tất cả các nét đã hoàn thành.
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, ... } } }- Triển khai giao diện