as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
AWS
文档
Support
Contact Us
My Cases
开发
测试
应用发布
盈利
用户参与
设备规格
资源

Amazon Stylus SDK入门

Amazon Stylus SDK入门

先决条件

安装Android Studio

我们建议您下载最新版本的Android Studio,但并非必须实现Predictive Touch API。如果您正在使用另一个IDE,请确保安装了Android SDK(30或更高),并满足Android开发工作的所有其他必要系统要求。本文档假定您使用的是Android Studio。

下载适用于Fire平板电脑的Amazon Stylus SDK

下载此项后,您可以在支持Stylus的应用中实现Predictive Touch。将该SDK的内容解压到您所选的位置。

将SDK添加到Android Studio

  1. 下载SDK,将其保存到首选文件夹,然后解压zip文件。
  2. 打开您现有的项目或在Android Studio中创建一个新项目。
  3. 将文件夹结构从Android更改为Project(项目)。

    在Android Studio中更改文件夹结构时应当看到的内容
  4. 如果尚未创建libs文件夹,请在旧应用(旧版Android Studio)或新应用(新版Android Studio)内创建一个。

    在Android Studio中更改文件夹结构时应当看到的内容
  5. 将步骤1中解压的StylusSDKSupportLibrary-1.0.aar复制到您刚才创建的libs文件夹中。
  6. 在应用的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);
     
    /**
     * 获取传递的索引处预测点的y坐标。应用应当检查
     *如果返回-1.0f,则不使用它进行绘制
     * @param索引 - 预测点的索引
     * @如果索引有效,则返回传递的索引处预测点的y坐标,否则返回-1.0f
     */
    public float getYAt(int index);
     
    /**
     *获取传递的索引处预测点的时间戳。应用应当检查
     *如果返回-1L,则不使用它,因为这表示无效。
     * @param索引 - 预测点的索引
     * @如果索引有效,则返回传递的索引处预测点的时间戳,否则返回-1L
     */
    public long getEventTimeAt(int index);
}


实现Amazon Stylus SDK

概述

  1. 使用AmazonPredictiveTouch.isFeatureRuntimeAvailable()检查功能可用性。
  2. 使用AmazonPredictiveTouch.init()初始化AmazonPredictiveTouch
  3. 检查是否使用公共静态布尔值isPredictiveTouchesRegistered()注册了PredictiveTouches。
  4. 使用AmazonPredictiveTouch.registerPredictiveTouches()注册预测。
  5. 使用AmazonPredictiveTouch.setCurrentView()设置要使用预测的视图。
  6. onTouchEvent中识别是否为MOVE事件,并记录其时间戳和坐标。
  7. 使用AmazonPredictiveTouch.getLatestPredictions() 获取预测并使用AmazonPredictedEvents绘制。
  8. 使用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()

现在,预测性触摸API可用于所有应用进程。

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)

绘制之前可在方法中针对每个视图,对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()用于变量来完成,例如变量mLatestXmLatestYmCurrentMoveTime。请参阅以下示例代码。

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事件,并且拥有它的时间戳和坐标,我们需要获取与这个时间戳对应的预测。

  1. 使用getLatestPredictions(long t)来获取预测。
  2. 这样将得到AmazonPredictedEvents对象。您可以使用该对象来确定以下项:
    1. 使用getNumberOfPredictions()方法获得的预测数。这将返回整数。
    2. 使用getEventTimeAt(int index)获得的事件时间。这将返回long型的时间戳。
    3. 使用getXAt(int index)getYAt(int index)方法获得的X / Y坐标。这些方法会返回浮点值。
  3. 将预测添加到当前路径或单独的路径,以便绘制预测。

  4. 绘制包含预测的路径。
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/内存的消耗。

有一点很重要,需要将PredictiveTouchesderegisterPredictiveTouches()与在其中初始化它的活动/视图的生命周期关联。

例如,对于活动,在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()
    }
}


故障排除

如果您在获取或绘制预测时遇到问题,请检查以下方面:

功能是否在设备上可用?
解决方案: 将Fire OS更新到最新版本,然后调用AmazonPredictiveTouch.isFeatureRuntimeAvailable() 查看您的设备是否支持此功能。有关详细信息,请参阅步骤1
如果我的应用在不支持Predictive Touch的设备上也可用,我是否需要维护两个APK?
解决方案: 否,无需维护两个不同的APK。在初始化AmazonPredictiveTouch之前,使用AmazonPredictiveTouch.isFeatureRuntimeAvailable() 来确定设备是否支持Predictive Touch。
是否可以使用单独的绘制对象(如不同的颜色)绘制预测,并能看到正在绘制预测?
解决方案: 使用步骤7中的代码示例。
注册预测(步骤4)或设置预测视图(步骤5)失败:
目前,对于一个进程,我们只支持用一个通道来接收预测,因此在一个进程中只能调用init() 一次。
存在多个活动或轮换的情况并不少见,在这种情况下会重新创建同一活动并再次调用init()
解决方案:
  • 在退出调用了init() 的活动/视图时使用deregisterPredictiveTouches()
  • 使用isPredictiveTouchesRegistered() API来检查是否需要调用init()。如果返回true,则无需再次调用init()

Last updated: 2023年11月10日