Amazon Stylus SDK入门
先决条件
安装Android Studio
我们建议您下载最新版本的Android Studio,但并非必须实现Predictive Touch API。如果您正在使用另一个IDE,请确保安装了Android SDK(30或以上),并满足Android开发工作的所有其他必要系统要求。本文档假定您使用的是Android Studio。
下载适用于Fire平板电脑的Stylus SDK
下载此项后,您可以在支持Stylus的应用中实现Predictive Touch。将该SDK的内容解压到您所选的位置。
将SDK添加到Android Studio
- 下载SDK,将其保存到首选文件夹,然后解压zip文件。
- 打开您现有的项目或在Android Studio中创建一个新项目。
-
将文件夹结构从Android更改为Project(项目)。
-
如果尚未创建libs文件夹,请在旧应用(旧版Android Studio)或新应用(新版Android Studio)内创建一个。
- 将步骤1中解压的StylusSDKSupportLibrary-1.0.aar复制到您刚才创建的libs文件夹中。
- 在应用的
build.gradle
文件中,引用dependencies
对象中的AAR文件: 在build.gradle
文件的依赖项部分添加实现文件(libs/StylusSDKSupportLibrary-1.0.aar)。
dependencies {
implementation files('libs/StylusSDKSupportLibrary-1.0.aar')
现在,您可以在项目中使用Amazon Stylus SDK API。
API规范
您将使用这些类和API来实现Predictive Touch:
- com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
- com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents
AmazonPredictiveTouch该类用于执行所有任务,如注册/取消注册Predictive Touch。它也有助于获得预测的点,这样应用就可以要求获得这些点。请务必调用isFeatureRuntimeAvailable(Context)
以了解设备在运行时是否支持Predictive Touch。如果不支持,应用可以避免调用其他API,因为这些API没有用处。然后,应用需要使用init(Context)
初始化AmazonPredictiveTouch
才能使用预测。
AmazonPredictiveTouch该类包含预测坐标。当应用要求使用AmazonPredictiveTouch#getLatestPredictions(long)
时,会向应用提供此项的实例。
/**
*初始化Predictive Touch引擎。
*一个应用/进程应该只调用一次,除非它们之前明确取消注册。
*多次调用并没什么作用,并可能因为来自同一进程的重复注册而导致无法将
*预测传递至应用。
* @param上下文 - 应用的上下文。
*/
public static void init(Context ctx);
/**
*务必调用此方法以了解设备在运行时
*是否支持Predictive Touch。如果不支持,应用可以避免调用其他API,
*因为这些API没有用处
* @param上下文 - 应用的上下文。
* @如果此设备支持功能,则返回true,否则返回false
*/
public static boolean isFeatureRuntimeAvailable(Context ctx);
/**
*手动注册以获取Predictive Touch,以防应用过早注销
*并需要重新注册
* @如果支持功能并且我们已经注册或注册成功,则返回true,
*否则返回false
*/
public static boolean registerPredictiveTouches();
/**
* 在应用不需要预测的情况下手动取消注册Predictive Touch
* @如果支持功能并且我们尚未注册或取消注册成功,则返回true,
*否则返回false
*/
public static boolean deregisterPredictiveTouches();
/**
*让应用检查是否已注册预测
* @如果已注册则返回true,否则返回false
*/
public static boolean isPredictiveTouchesRegistered();
/**
* 让应用设置需要预测的视图
* @param视图 - 应用将在其中使用预测的视图应为非null
*/
public static void setCurrentView(View v);
/**
*要获取存储在PredictiveTouchManager中的最新预测,可从算法接收
*如果该功能不受支持,或者我们没有任何预测,此项将返回null
* @param t - 应用具有的最新事件时间戳,用于获取t之后的预测
* @返回包含预测的{@link AmazonPredictedEvents}对象,或者如果未收到任何预测,
*则返回null。
*/
public static AmazonPredictedEvents getLatestPredictions(long t);
}
/**
*获取该{@link AmazonPredictedEvents}实例中的预测数
* @返回预测的数目
*/
public int getNumberOfPredictions();
/**
*获取传递的索引处预测点的x坐标。应用应当检查
*如果返回-1.0f,则不使用它进行绘制
* @param索引 - 预测点的索引
* @如果索引有效,则返回传递的索引处预测点的x坐标,否则返回-1.0f
*/
public float getXAt(int index);
/**
*获取传递的索引处预测点的x坐标。应用应当检查
*如果返回-1.0f,则不使用它进行绘制
* @param索引 - 预测点的索引
* @如果索引有效,则返回传递的索引处预测点的y坐标,否则返回-1.0f
*/
public float getYAt(int index);
/**
*获取传递的索引处预测点的时间戳。应用应当检查
*如果返回-1L,则不使用它,因为这表示无效。
* @param索引 - 预测点的索引
* @如果索引有效,则返回传递的索引处预测点的时间戳,否则返回-1L
*/
public long getEventTimeAt(int index);
}
实现步骤
步骤概述
- 使用
AmazonPredictiveTouch.isFeatureRuntimeAvailable()
检查功能可用性。 - 使用
AmazonPredictiveTouch.init()
初始化AmazonPredictiveTouch
。 - 检查是否使用公共静态布尔值
isPredictiveTouchesRegistered()
注册了PredictiveTouches。 - 使用
AmazonPredictiveTouch.registerPredictiveTouches()
注册预测。 - 使用
AmazonPredictiveTouch.setCurrentView()
设置要使用预测的视图。 - 在
onTouchEvent
中识别是否为MOVE事件,并记录其时间戳和坐标。 - 使用
AmazonPredictiveTouch.getLatestPredictions()
获取预测并使用收到的AmazonPredictedEvents
绘制。 - 使用
AmazonPredictiveTouch.deregisterPredictiveTouches()
取消注册预测。
有关以上每个步骤的详情,请参见下文。
步骤1: 检查Amazon Predictive Touch可用性
使用AmazonPredictiveTouch.isFeatureRuntimeAvailable(android.content.Context context
)
方法来了解该功能在设备上是否可用。
如果功能不可用或不受支持,该方法将返回false
。否则,其将返回true
。
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
private boolean isPredictiveTouchesSupported() {
/*示例实现*/
return AmazonPredictiveTouch.isFeatureRuntimeAvailable(this) == true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches应该可用。此处
// 为初始化和注册预测的代码
}
else {
// PredictiveTouches不可用。将其写入日志消息
Log.w(TAG, "PredictiveTouches is not supported");
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
private fun isPredictiveTouchesSupported(): Boolean {
/*示例实现*/
return AmazonPredictiveTouch.isFeatureRuntimeAvailable(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (isPredictiveTouchesSupported()) {
// PredictiveTouches应该可用。此处
// 为初始化和注册预测的代码
} else {
// PredictiveTouches不可用。将其写入日志消息
Log.w(TAG, "PredictiveTouches is not supported")
}
}
步骤2: 初始化AmazonPredictiveTouch
使用AmazonPredictiveTouch.init(android.content.Context context)
方法来初始化。
这应当使用活动的onCreate
()
方法或在视图创建过程中完成(如果仅该视图需要)。
默认情况下,初始化也会注册PredictionService
,因此在此步骤之后,您可以跳过调用AmazonPredictiveTouch.registerPredictiveTouches()
。
现在,可以在整个应用进程中使用Predictive Touch API。
init()
调用一次以上。Predictive Service旨在以每个进程一个实例的方式使用,因此再次调用init()
时将被忽略。import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches应该可用。此处
// 为初始化和注册预测的代码
AmazonPredictiveTouch.init(MainActivity.this);
}
else {
// PredictiveTouches不可用。将其写入日志消息
Log.w(TAG, "PredictiveTouches is not supported");
}
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches应该可用。此处
// 为初始化和注册预测的代码
AmazonPredictiveTouch.init(this@MainActivity)
} else {
// PredictiveTouches不可用。将其写入日志消息
Log.w(TAG, "PredictiveTouches is not supported")
}
}
}
步骤3: 检查是否已注册PredictiveTouches
使用实用工具方法isPredictiveTouchesRegistered()
检查是否注册了PredictiveTouches。这会返回一个布尔值。
要让应用接收预测点,您必须使用init()
或registerPredictiveTouches()
初始化或注册侦听器。仅在对不需要预测的某些视图调用了deregisterPredictiveTouches()
,然后返回到需要预测的视图的情况下,需要使用registerPredictiveTouches()
。
步骤4: 注册预测
如果您之前使用了AmazonPredictiveTouch.deregisterPredictiveTouches()
来节省计算/内存资源,则需要此步骤,因为您之前不需要预测,但现在您再次需要启用预测。
要再次开始接收预测,请使用AmazonPredictiveTouch.registerPredictiveTouches()
方法。这是一个幂等API,因此不需要多次调用它,并且这样也不会导致不同的行为。如果注册成功,它将返回true
,否则将返回false
。
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
//假设在恢复时,用户将进行书写/绘图等操作,并且我们之前进行了取消注册,
//因此需要注册预测。
AmazonPredictiveTouch.registerPredictiveTouches();
.............
.............
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
override fun onResume() {
super.onResume()
//假设在恢复时,用户将进行书写/绘图等操作,并且我们之前进行了取消注册,
//因此需要注册预测。
AmazonPredictiveTouch.registerPredictiveTouches()
}
}
步骤5: 设置将要使用预测的视图
每个视图具有自己的几何变换。例如,它在屏幕上的位置、可能的旋转或缩放效果。需要相应地转换触控点。
要向要使用预测的当前视图提供准确的预测事件,请设置当前视图。这可以通过切换到使用预测的新视图来完成。使用方法setCurrentView
(android.view.View view)
。
-
如果只在一个视图中使用Predictive Touch,那么您可以使用视图的
onMeasure()
/onLayout()
方法调用它。import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch; public class TouchDisplayView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); AmazonPredictiveTouch.setCurrentView(this); } }
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch class TouchDisplayView : View() { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) AmazonPredictiveTouch.setCurrentView(this) } }
-
如果计划在多个视图中使用预测,那么绘制之前可在方法中针对每个视图,对
MotionEvent.ACTION_DOWN
使用onTouchEvent()
来调用此项,由此每次触碰视图时都会调用该函数。import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch; public class TouchDisplayView extends View { @Override public boolean onTouchEvent(MotionEvent event) { //现有代码 final int action = event.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { AmazonPredictiveTouch.setCurrentView(this); //现有代码 } } } }
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch class TouchDisplayView : View() { override fun onTouchEvent(event: MotionEvent): Boolean { //现有代码 val action = event.action when (action and MotionEvent.ACTION_MASK) { MotionEvent.ACTION_DOWN -> { AmazonPredictiveTouch.setCurrentView(this) } } } }
步骤6: 使用TouchEvent识别是否为MOVE事件,并记录其时间戳和坐标
仅将预测用于MOVE事件。您可以使用onTouchEvent()
方法来确定是否为MOVE事件。将其时间戳和坐标记录下来。要预测最新MOVE事件的点,请使用时间戳。此外,我们想在最后的触控点的X、Y坐标之后绘制预测点,所以我们需要它的坐标。
这可以通过将onTouchEvent()
用于变量来完成,例如变量mLatestX
、mLatestY
和mCurrentMoveTime
。请参见下面的示例代码。
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class TouchDisplayView extends View {
private long mCurrentMoveTime = -1;
private float mLatestX, mLatestY;
private Path mCurrentStroke = new Path(); // Path to store stroke data
@Override
public boolean onTouchEvent(MotionEvent event) {
............
//设置时间戳-1
mCurrentMoveTime = -1;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
.............
case MotionEvent.ACTION_MOVE: {
//获取当前移动事件的时间戳,以及X、Y值
mCurrentMoveTime = event.getEventTime();
mLatestX = event.getX();
mLatestY = event.getY();
for (i in 0 until event.historySize) {
//将历史点添加至当前笔画
mCurrentStroke.lineTo(event.getHistoricalX(0, i), event.getHistoricalY(0, i))
}
//此处为了举例,我们使用的是lineTo,请根据需要使用自定义绘制逻辑
mCurrentStroke.lineTo(mLatestX,mLatestY); // add point to current stroke
this.invalidate();
break;
}
.............
}
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class TouchDisplayView : View() {
private var mCurrentMoveTime: Long = -1
private var mLatestX = 0f
private var mLatestY = 0f
private val mCurrentStroke: Path = Path() // Path to store stroke data
override fun onTouchEvent(event: MotionEvent): Boolean {
//设置时间戳-1
mCurrentMoveTime = -1
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_MOVE -> {
//获取当前移动事件的时间戳,以及X、Y值
mCurrentMoveTime = event.eventTime
mLatestX = event.x
mLatestY = event.y
for (i in 0 until event.historySize) {
//将历史点添加至当前笔画
mCurrentStroke.lineTo(event.getHistoricalX(0, i), event.getHistoricalY(0, i))
}
//此处为了举例,我们使用的是lineTo,请根据需要使用自定义绘制逻辑
mCurrentStroke.lineTo(mLatestX, mLatestY) // add point to current stroke
this.invalidate()
}
}
}
}
步骤7: 获取预测并绘制
现在我们已知道这是一个MOVE事件,并且拥有它的时间戳和坐标,我们需要获取与这个时间戳对应的预测。
- 使用
getLatestPredictions(long t)
来获取预测。 - 这样将得到
AmazonPredictedEvents
对象。您可以使用该对象来确定以下项:- 使用
getNumberOfPredictions()
方法获得的预测数。这将返回整数。 - 使用
getEventTimeAt(int index)
获得的事件时间。这将返回long型的时间戳。 - 使用
getXAt(int index)
和getYAt(int index)
方法获得的X / Y坐标。这些方法会返回浮点值。
- 使用
-
将预测添加到当前路径或单独的路径,以便绘制预测。
重要须知: 清除画布。否则来自最后一次的预测也将可见。 - 绘制包含预测的路径。
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents;
public class TouchDisplayView extends View {
@Override
protected void onDraw(Canvas canvas) {
//获取与mCurrentMoveTime对应的最后预测
AmazonPredictedEvents latestPred = AmazonPredictiveTouch.getLatestPredictions(mCurrentMoveTime);
Path predicted = new Path();
//如果最新预测可用,则将预测添加到Path
if (mCurrentMoveTime != -1 /*valid move*/ && latestPred != null
&& latestPred.getNumberOfPredictions() != 0) {
Log.i(TAG,
"DRAWSTART EVENTTIME: " + mCurrentMoveTime + " currentTime "
+ SystemClock.uptimeMillis() + " latestPredTime "
+ latestPred.getEventTimeAt(latestPred.getNumberOfPredictions() - 1));
predicted.moveTo(mLatestX, mLatestY);
for (int i = 0; i < latestPred.getNumberOfPredictions(); i++) {
//此处为了举例,我们使用的是lineTo,请根据需要使用自定义绘制逻辑
if (latestPred.getXAt(i) > -1.0f && latestPred.getYAt(i) > -1.0f)
predicted.lineTo(latestPred.getXAt(i), latestPred.getYAt(i));
}
}
//首先清除画布,这样最后预测的点会被擦除,就不会有不需要的人为痕迹
canvas.drawColor(Color.WHITE);
//绘制实际点
canvas.drawPath(mCurrentStroke, mPaint);
//使用另一个Paint对象绘制包含预测的路径,以进行确认
if (!predicted.isEmpty())
canvas.drawPath(predicted, mPaintPred);
if (mCurrentMoveTime != -1) {
Log.i(TAG, "DRAWEND EVENTTIME: " + mCurrentMoveTime + " currentTime " + SystemClock.uptimeMillis());
}
super.onDraw(canvas);
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents
class TouchDisplayView() : View() {
override fun onDraw(canvas: Canvas) {
//获取与mCurrentMoveTime对应的最后预测
val latestPred: AmazonPredictedEvents =
AmazonPredictiveTouch.getLatestPredictions(mCurrentMoveTime)
val predicted = Path()
//如果最新预测可用,则将预测添加到Path
if (mCurrentMoveTime !== -1 /*valid move*/ && latestPred != null && latestPred.getNumberOfPredictions() !== 0) {
Log.i(
TAG,
"DRAWSTART EVENTTIME: " + mCurrentMoveTime.toString() + " currentTime "
+ SystemClock.uptimeMillis().toString() + " latestPredTime "
+ latestPred.getEventTimeAt(latestPred.getNumberOfPredictions() - 1)
)
predicted.moveTo(mLatestX, mLatestY)
for (i in 0 until latestPred.getNumberOfPredictions()) {
//此处为了举例,我们使用的是lineTo,请根据需要使用自定义绘制逻辑
if (latestPred.getXAt(i) > -1.0f && latestPred.getYAt(i) > -1.0f) predicted.lineTo(
latestPred.getXAt(i),
latestPred.getYAt(i)
)
}
}
//首先清除画布,这样最后预测的点会被擦除,就不会有不需要的人为痕迹
canvas.drawColor(Color.WHITE)
//绘制实际点
canvas.drawPath(mCurrentStroke, mPaint)
//使用另一个Paint对象绘制包含预测的路径,以进行确认
if (!predicted.isEmpty()) canvas.drawPath(predicted, mPaintPred)
if (mCurrentMoveTime !== -1) {
Log.i(
TAG,
"DRAWEND EVENTTIME: " + mCurrentMoveTime.toString() + " currentTime " + SystemClock.uptimeMillis()
)
}
super.onDraw(canvas)
}
}
步骤8: 取消注册预测
调用deregisterPredictiveTouches()
方法来取消注册PredictiveTouches
。当应用移动到不需要预测的“活动”或“视图”时,或者当应用进入后台时,您可以考虑这样做,以减少CPU/内存的消耗。
有一点很重要,需要将PredictiveTouches
的deregisterPredictiveTouches()
与在其中初始化它的活动/视图的生命周期关联。
例如,对于活动,在onCreate()
方法中使用init()
,并在onDestroy()
方法中使用deregisterPredictiveTouches()
。在进程未终止但“活动”或“视图”已销毁的情况下,如果之前未取消注册PredictiveTouches
,则无法在onCreate()
中重新初始化它。**显示轮换就是这样的例子,其中进程没有被终止(进程仍然注册了服务),但活动被重新创建(试图创建新实例)。
在成功取消注册后,方法将返回true
,否则将返回false
。
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
//可选:如果您想在应用处于后台时停止预测,以节省Predictive Touch内存占用量时,可以这样做。
@Override
protected void onPause() {
AmazonPredictiveTouch.deregisterPredictiveTouches();
super.onPause();
}
//对于那些在进程仍然存在时可能已终止的组件(如活动、视图等),这是必要操作
@Override
protected void onDestroy() {
AmazonPredictiveTouch.deregisterPredictiveTouches();
super.onDestroy();
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
//可选:如果您想在应用处于后台时停止预测,以节省Predictive Touch内存占用量时,可以这样做。
override fun onPause() {
AmazonPredictiveTouch.deregisterPredictiveTouches()
super.onPause()
}
//对于那些在进程仍然存在时可能已终止的组件(如活动、视图等),这是必要操作
override fun onDestroy() {
AmazonPredictiveTouch.deregisterPredictiveTouches()
super.onDestroy()
}
}
故障排除
如果您在获取或绘制预测时遇到问题,请检查以下方面:
- 功能是否在设备上可用? (请参阅步骤1进行检查)。
- 解决方案: 将Fire OS更新到最新版本,并确保您在选定的平板电脑上使用的是Fire OS 8。
- 如果我的应用在不支持Predictive Touch的设备上也可用,我是否需要维护两个APK?
- 解决方案: 否,无需维护两个不同的APK。在初始化AmazonPredictiveTouch之前,使用
AmazonPredictiveTouch.isFeatureRuntimeAvailable()
来确定设备是否支持Predictive Touch。 - 使用步骤7时,您是否可以使用单独的绘制对象(如不同的颜色)绘制预测,并能看到正在绘制预测?
- 解决方案: 请参阅
init()
下的注释和deregisterPredictiveTouches()
方法下的说明。 - 注册预测(步骤4)或设置预测视图(步骤5)失败:
- 目前,对于一个进程,我们只支持用一个通道来接收预测,因此在一个进程中只能调用
init()
一次。 - 存在多个活动或轮换的情况并不少见,在这种情况下会重新创建同一活动并再次调用
init()
。 - 解决方案:
- 在退出调用了
init()
的活动/视图时使用deregisterPredictiveTouches()
。 - 使用
isPredictiveTouchesRegistered()
API来检查是否需要调用init()
。如果返回true,则意味着无需再次调用init()。
- 在退出调用了