InProgressStrokesView
class InProgressStrokesView : FrameLayout
kotlin.Any | ||||
↳ | android.view.View | |||
↳ | android.view.ViewGroup | |||
↳ | android.widget.FrameLayout | |||
↳ | androidx.ink.authoring.InProgressStrokesView |
Displays in-progress ink strokes as MotionEvent
user inputs are provided to it.
Summary
Public constructors |
---|
InProgressStrokesView( |
Public functions |
|
---|---|
Unit |
Add a listener to be notified when strokes are finished. |
Unit |
addToStroke( Add input data from a |
Unit |
addToStroke( Add input data, from a particular pointer within a |
Unit |
cancelStroke(strokeId: InProgressStrokeId, event: MotionEvent?) Cancel the building of a stroke. |
Unit |
Eagerly initialize rather than waiting for the first stroke to be drawn. |
Unit |
finishStroke(input: StrokeInput, strokeId: InProgressStrokeId) Complete the building of a stroke, with the last input data coming from a |
Unit |
finishStroke( Complete the building of a stroke, with the last input data coming from a particular pointer of a |
Map<InProgressStrokeId, Stroke> |
Returns all the finished strokes that are still being rendered by this view. |
Unit |
removeFinishedStrokes(strokeIds: Set<InProgressStrokeId>) Stop this view from rendering the strokes with the given IDs. |
Unit |
Removes a listener that had previously been added with |
InProgressStrokeId |
startStroke( Start building a stroke with the provided |
InProgressStrokeId |
startStroke( Start building a stroke using a particular pointer within a |
Protected functions |
|
---|---|
open Unit |
Public properties |
|
---|---|
CountingIdlingResource? |
Allows a test to easily wait until all in-progress strokes are completed and handed off. |
Path? |
Denote an area of this |
Matrix |
The transform matrix to convert |
() -> CanvasStrokeRenderer |
A function that creates a |
TextureBitmapStore |
|
Public constructors
InProgressStrokesView
InProgressStrokesView(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: @AttrRes Int = 0
)
Public functions
addFinishedStrokesListener
fun addFinishedStrokesListener(listener: InProgressStrokesFinishedListener): Unit
Add a listener to be notified when strokes are finished. These strokes will continue to be rendered within this view until removeFinishedStrokes
is called. All of the strokes that have been delivered to listeners but have not yet been removed with removeFinishedStrokes
are available through getFinishedStrokes
.
addToStroke
fun addToStroke(
inputs: StrokeInputBatch,
strokeId: InProgressStrokeId,
prediction: StrokeInputBatch = ImmutableStrokeInputBatch.EMPTY
): Unit
Add input data from a StrokeInputBatch
to an existing stroke.
Parameters | |
---|---|
inputs: StrokeInputBatch |
The next |
strokeId: InProgressStrokeId |
The |
prediction: StrokeInputBatch = ImmutableStrokeInputBatch.EMPTY |
Predicted |
addToStroke
fun addToStroke(
event: MotionEvent,
pointerId: Int,
strokeId: InProgressStrokeId,
prediction: MotionEvent? = null
): Unit
Add input data, from a particular pointer within a MotionEvent
, to an existing stroke.
Parameters | |
---|---|
event: MotionEvent |
The next |
pointerId: Int |
The identifier of the pointer within |
strokeId: InProgressStrokeId |
The |
prediction: MotionEvent? = null |
Predicted |
cancelStroke
fun cancelStroke(strokeId: InProgressStrokeId, event: MotionEvent? = null): Unit
Cancel the building of a stroke. It will no longer be visible within this InProgressStrokesView
, and no completed Stroke
object will come through InProgressStrokesFinishedListener
.
This is typically done for one of three reasons:
-
A
MotionEvent
withMotionEvent.getActionMasked
ofMotionEvent.ACTION_CANCEL
. This tends to be when an entire gesture has been canceled, for example when a parentView
usesandroid.view.ViewGroup.onInterceptTouchEvent
to intercept and handle the gesture itself. -
A
MotionEvent
withMotionEvent.getFlags
containingMotionEvent.FLAG_CANCELED
. This tends to be when the system has detected an unintentional touch, such as from the user resting their palm on the screen while writing or drawing, after some events from that unintentional pointer have already been delivered. -
An app's business logic reinterprets a gesture previously used for inking as something else, and the earlier inking may be seen as unintentional. For example, an app that uses single-pointer gestures for inking and dual-pointer gestures for pan/zoom/rotate will start inking when the first pointer goes down, but when the second pointer goes down it may want to cancel the stroke from the first pointer rather than leave the small ink marks on the screen.
Parameters | |
---|---|
strokeId: InProgressStrokeId |
The |
event: MotionEvent? = null |
The |
eagerInit
fun eagerInit(): Unit
Eagerly initialize rather than waiting for the first stroke to be drawn. Since initialization can be somewhat heavyweight, doing this as soon as it's likely for the user to start drawing can prevent initialization from introducing latency to the first stroke.
finishStroke
fun finishStroke(input: StrokeInput, strokeId: InProgressStrokeId): Unit
Complete the building of a stroke, with the last input data coming from a StrokeInput
.
Parameters | |
---|---|
input: StrokeInput |
The last |
strokeId: InProgressStrokeId |
The |
finishStroke
fun finishStroke(
event: MotionEvent,
pointerId: Int,
strokeId: InProgressStrokeId
): Unit
Complete the building of a stroke, with the last input data coming from a particular pointer of a MotionEvent
.
When the stroke no longer needs to be rendered by this InProgressStrokesView
and can instead be rendered anywhere in the View
hierarchy using CanvasStrokeRenderer
, the resulting Stroke
object will be passed to the InProgressStrokesFinishedListener
instances registered with this InProgressStrokesView
using addFinishedStrokesListener
.
Parameters | |
---|---|
event: MotionEvent |
The last |
pointerId: Int |
The identifier of the pointer within |
strokeId: InProgressStrokeId |
The |
getFinishedStrokes
fun getFinishedStrokes(): Map<InProgressStrokeId, Stroke>
Returns all the finished strokes that are still being rendered by this view. The IDs of these strokes should be passed to removeFinishedStrokes
when they are handed off to another view.
removeFinishedStrokes
fun removeFinishedStrokes(strokeIds: Set<InProgressStrokeId>): Unit
Stop this view from rendering the strokes with the given IDs.
This should be called in the same UI thread run loop (HWUI frame) as when the strokes start being rendered elsewhere in the view hierarchy. This means they are saved in a location where they will be picked up in a view's next call to onDraw
, and that view's invalidate
method has been called. If these two operations are not done within the same UI thread run loop (usually side by side - see example below), then there will be brief rendering errors - either a visual gap where the stroke is not drawn during a frame, or a double draw where the stroke is drawn twice and translucent strokes appear more opaque than they should.
removeFinishedStrokesListener
fun removeFinishedStrokesListener(
listener: InProgressStrokesFinishedListener
): Unit
Removes a listener that had previously been added with addFinishedStrokesListener
.
startStroke
fun startStroke(
input: StrokeInput,
brush: Brush,
strokeToViewTransform: Matrix = IDENTITY_MATRIX
): InProgressStrokeId
Start building a stroke with the provided input
. This would typically be followed by many calls to addToStroke
, and the sequence would end with a call to either finishStroke
or cancelStroke
.
In most circumstances, the startStroke
overload that accepts a MotionEvent
is more convenient. However, this overload using a StrokeInput
is available for cases where the input data may not come directly from a MotionEvent
, such as receiving events over a network connection.
If there is a way to request unbuffered dispatch from the source of the input data used here, equivalent to View.requestUnbufferedDispatch
for unbuffered MotionEvent
data, then be sure to request it for optimal performance.
Parameters | |
---|---|
input: StrokeInput |
The |
brush: Brush |
Brush specification for the stroke being started. Note that if stroke coordinate units (the |
strokeToViewTransform: Matrix = IDENTITY_MATRIX |
The |
Returns | |
---|---|
InProgressStrokeId |
The |
startStroke
fun startStroke(
event: MotionEvent,
pointerId: Int,
brush: Brush,
motionEventToWorldTransform: Matrix = IDENTITY_MATRIX,
strokeToWorldTransform: Matrix = IDENTITY_MATRIX
): InProgressStrokeId
Start building a stroke using a particular pointer within a MotionEvent
. This would typically be followed by many calls to addToStroke
, and the sequence would end with a call to either finishStroke
or cancelStroke
.
In most circumstances, prefer to use this function over startStroke
that accepts a StrokeInput
.
For optimum performance, it is strongly recommended to call View.requestUnbufferedDispatch
using event
and the View
that generated event
alongside calling this function. When requested this way, unbuffered dispatch mode will automatically end when the gesture is complete.
Parameters | |
---|---|
event: MotionEvent |
The first |
pointerId: Int |
The identifier of the pointer within |
brush: Brush |
Brush specification for the stroke being started. Note that the overall scaling factor of |
motionEventToWorldTransform: Matrix = IDENTITY_MATRIX |
The matrix that transforms |
strokeToWorldTransform: Matrix = IDENTITY_MATRIX |
Allows an object-specific (stroke-specific) coordinate space to be defined in relation to the caller's "world" coordinate space. This defaults to the identity matrix, which is typical for many use cases at the time of stroke construction. In typical use cases, stroke coordinates and world coordinates may start to differ from one another after stroke creation as a particular stroke is manipulated within the world, e.g. it may be moved, scaled, or rotated relative to other content within an app's document. This matrix must be invertible. |
Returns | |
---|---|
InProgressStrokeId |
The |
Throws | |
---|---|
kotlin.IllegalArgumentException |
if |
Protected functions
Public properties
inProgressStrokeCounter
@VisibleForTesting
var inProgressStrokeCounter: CountingIdlingResource?
Allows a test to easily wait until all in-progress strokes are completed and handed off. There is no reason to set this in non-test code.
maskPath
var maskPath: Path?
Denote an area of this InProgressStrokesView
where no ink should be visible. This is useful for UI elements that float on top of (in Z order) the drawing surface - without this, a user would be able to draw in-progress ("wet") strokes on top of those UI elements, but then when the stroke is finished, it will appear as a dry stroke underneath of the UI element. If this mask is set to the shape and position of the floating UI element, then the ink will never be rendered in that area, making it appear as if it's being drawn underneath the UI element.
This technique is most convincing when the UI element is opaque. Often there are parts of the UI element that are translucent, such as drop shadows, or anti-aliasing along the edges. The result will look a little different between wet and dry strokes for those cases, but it can be a worthwhile tradeoff compared to the alternative of drawing wet strokes on top of that UI element.
motionEventToViewTransform
var motionEventToViewTransform: Matrix
The transform matrix to convert MotionEvent
coordinates, as passed to startStroke
, addToStroke
, and finishStroke
, into coordinates of this InProgressStrokesView
for rendering. Defaults to the identity matrix, for the recommended case where InProgressStrokesView
exactly overlays the android.view.View
that has the touch listener from which MotionEvent
instances are being forwarded.
rendererFactory
var rendererFactory: () -> CanvasStrokeRenderer
A function that creates a CanvasStrokeRenderer
when invoked. The default implementation of this will automatically account for the Android OS version of the device. If you choose to replace the default with an alternate implementation, then you must set this variable before the first call to startStroke
or eagerInit
.
textureBitmapStore
@ExperimentalInkCustomBrushApi
var textureBitmapStore: TextureBitmapStore
TextureBitmapStore
used by the default value for rendererFactory
.
By default, this is a no-op implementation that does not load any brush textures. The factory functions are called when the renderer is initialized, so if this will be changed to something that does load and store texture images, it must be set before the first call to startStroke
or eagerInit
.