开发者控制台

Amazon Stylus SDK入门

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

  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);
     
    /**
     *获取传递的索引处预测点的x坐标。应用应当检查
     *如果返回-1.0f,则不使用它进行绘制
     * @param索引 - 预测点的索引
     * @如果索引有效,则返回传递的索引处预测点的y坐标,否则返回-1.0f
     */
    public float getYAt(int index);
     
    /**
     *获取传递的索引处预测点的时间戳。应用应当检查
     *如果返回-1L,则不使用它,因为这表示无效。
     * @param索引 - 预测点的索引
     * @如果索引有效,则返回传递的索引处预测点的时间戳,否则返回-1L
     */
    public long getEventTimeAt(int index);
}


实现步骤

步骤概述

  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()

现在,可以在整个应用进程中使用Predictive Touch 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)

  1. 如果只在一个视图中使用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)
         }
     }
    
    
    
  2. 如果计划在多个视图中使用预测,那么绘制之前可在方法中针对每个视图,对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()
    }
}


故障排除

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

功能是否在设备上可用? (请参阅步骤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()。