65.9K
CodeProject 正在变化。 阅读更多。
Home

Android Wearable 世界之旅

starIconstarIconstarIconstarIconstarIcon

5.00/5 (31投票s)

2015年10月16日

CPOL

15分钟阅读

viewsIcon

44925

downloadIcon

606

对Android Wear应用开发进行了简要说明,对所有初学者和中级Android开发者都有帮助。

将要涵盖的主题

引言

Android通过在电视、穿戴设备和汽车等各种电子模块中扩展下一代应用程序开发的概念,正在扩展其所有范围和可能性。本文是对Android Wear(未来的新兴应用程序开发平台)应用程序开发的简要摘要和概述。

本文清晰地介绍了Android可穿戴设备及其功能。本文讨论了Wear平台上的Android应用程序开发的高级概述。如果您是Android爱好者,喜欢玩代码,并希望用它来制作令人惊叹的应用程序,让我们为您的手表构建一些酷炫的东西!

Android Wear

技术上,它是为可穿戴智能手表和计算机设计的Android操作系统版本。需要一台运行Android 4.3或更高版本、或iOS 8.2或更新版本(支持有限)的移动设备才能与这些手表配对。它从API级别20(代码名为KITKAT_WATCH)开始推出。

Android Wear(来源:tech.thaivisa.com)

历史

Wearable平台于2014年3月18日首次推出,同时发布了开发者预览版。让我们回顾一下该平台的一些关键点。

  • 三星Gear LiveLG G Watch于2014年6月25日在Google I/O上发布。
  • 摩托罗拉的Moto 360于2014年9月5日发布。
  • 2014年12月10日,关于Android 5.0 Lollipop的更新开始为Wear推出。
  • 2015年8月31日,Google发布了适用于iOS 8.2或更高版本的配对应用程序。

目前,摩托罗拉、三星、LG、HTC和华硕正在生产Android可穿戴智能手表。

主要功能一览

Google为这款智能手表平台推出了一些炫酷的功能,这些功能在移动平台上仍然不存在。让我们来看看——

  • Google Now:可能是智能手表最重要的功能。当您问“OK Google,我的心率是多少?”或“最近的消防站在哪里?”时,从手表上获得答案非常棒,有点像《钢铁侠》电影中的J.A.R.V.I.S。
  • 通知:通过手表保持联系,一目了然地查看消息。
  • 天气更新:不时获取最新的天气报告。
  • 出行:一目了然地获取您的出行信息。
  • 提醒:永远不要错过任何事情。获取日历和其他提醒。
  • 与移动设备交互:从手表上播放手机音乐!
  • Google Fit:支持骑行和跑步追踪。只需说“Ok Google,开始跑步”。
  • 支持Google地图、睡眠追踪、查找手机等功能!

开始之前

本文是对面向Wear平台的应用开发流程的简要说明。因此,无论是开发还是对代码结构有扎实的了解,您都需要具备Android应用程序开发方面的知识,还需要具备JAVA知识。如果您觉得知识不足,没关系,花些时间学习JAVAOOP。为了制作应用程序,我们将使用Android Studio。如果您的计算机有4GB内存和JDK 7或更高版本,就可以开始了。要正确设置所有开发环境以及Studio,您可以按照这个YouTube视频进行操作。请注意操作系统和JDK之间的版本(32/64位)匹配。

Wear 应用的设计原则

由于手表比移动设备的屏幕更小,因此该平台的设计原则与传统Android应用程序有很大不同。在从官方网站获得简要说明之前,您可以看一下关键的设计指南——

  • 遵循5秒规则,这意味着您应用程序中的任何内容都应该在此典型时间内与用户进行交互。用户不应在此时间之外集中注意力。如果超出该时间,您应该重新考虑应用程序的功能和设计!
  • 设计时要记住您的大拇指规则!考虑您在行走或吃饭时,很难放慢这些活动并集中精力在手表上完成工作。因此,在设计时,请在屏幕上为您的拇指留出足够的空间来完成该工作。不要以项目过于相邻的方式设计您的应用程序,始终设计用于大尺寸手势
  • 如果您的应用程序需要显示的内容太多,请将其拆分成多个页面(卡片)。
  • 避免用户输入传统系统,使其易于查看并保持最少,一目了然,易于阅读,为您的手表使用更智能的解决方案,问候“Google Now”。

构建你的第一个Wear应用

首先,我们将专注于创建一个“Hello Wear”应用程序,它将演示创建基本Wear应用程序的步骤并将应用程序运行在Wear模拟器上。接下来,我们将优化应用程序并构建一些炫酷的东西!

SDK要求

在构建可穿戴应用程序之前,请先检查2个项目。您必须——

  • 将Android SDK工具更新到23.0.0或更高版本&
  • 将Android SDK更新到Android 4.4W.2 (API 20)或更高版本。

从SDK Manager中选择提到的工具,并将它们更新到最新版本。

图1:Android SDK工具

          

图2:可穿戴应用所需的最低Android SDK版本

如果您没有真正的Android手表,那么请不要忘记安装列出的Android Wear系统镜像,它们是构建Wear模拟器所必需的。

创建新项目

打开Android Studio并选择“新建项目”选项。给您的应用程序命名,我们在此使用了“Hello Android Wear”作为应用程序名称以及公司域名。

图1:为Android Wear创建新项目

选择Wear应用程序的最低SDK版本,我们在此选择了API 20,其代号为KitKat Wear。

图2:选择目标SDK版本

Wear支持各种专用活动,包括表盘、Google地图等。但在此我们选择了“Blank Wear Activity”。

图3:选择Blank Wear Activity

现在设置您的活动和布局文件名,除了主布局文件外,还有不同的矩形和圆形布局文件。到目前为止,请保留默认名称,我们稍后将讨论它们。点击Finish按钮,然后等待一段时间来构建您的Wear应用程序的Gradle项目信息。

图4:设置布局和活动名称

当所有过程成功完成后,您将看到Studio窗口如下。

图5:成功构建后的项目窗口

创建Wear虚拟设备

要创建Wear模拟器,请点击Studio窗口右上角的AVD Manager按钮。然后点击位于窗口左下方的AVD Manager窗口中的“Create Virtual Device”按钮。

        

图1:选择AVD Manager

接下来,从左侧面板的“Category”列表中选择Wear。右侧列出了几个基于屏幕尺寸和形状(圆形/方形)的设备,您可以根据自己的喜好选择。目前,我们选择了一个1.65英寸、分辨率为320*320的圆形Wear设备。

图2:为您的模拟器选择一个Wear设备

下一个窗口将显示可用的系统镜像。我们选择了一个针对Android 4.4x86镜像。出于性能考虑,请始终优先选择x86模拟器而不是armeabi-v7a模拟器。

图3:选择系统镜像

但请记住,x86模拟器需要HAXM加速器才能获得更好的性能。因此,请从SDK Manager窗口安装它。

图4:安装HAXM以提高模拟器性能

您可以使用Show Advanced Settings按钮来编辑模拟器的配置,例如RAM、SD卡等。

图5:AVD配置窗口

当您的Wear虚拟设备准备好后,它将出现在AVD Manager窗口中。点击启动按钮(红色标记)启动模拟器。

图6:AVD列表

根据您的PC性能和可用RAM,模拟器将在15秒到1分钟内启动。当它准备好时,它看起来会像下面一样。

 

 

 

图7:Android Wear模拟器

将你的项目部署到Wear模拟器

现在创建的Wear模拟器已准备好运行您的项目。按Studio窗口顶部中间的绿色“Run”按钮。Gradle构建完成后,将出现一个名为“Device Chooser”的窗口,您将在其中看到创建的Wear模拟器已列出。点击“OK”按钮继续。

图1:设备选择器窗口

项目成功运行后,您将在模拟器中看到如下所示的输出。

图2:项目部署后的输出

更改文本值

让我们更改文本值,使其看起来像“Hello Round Android Wear!”。查看左侧面板的项目树,然后在values文件夹下选择strings.xml。将标签为“hello_round”的字符串值更改为“Hello Round Android Wear!”。

图1:从string.xml更改文本值

现在运行项目。您将找到如下所示的输出。滑动屏幕退出应用程序。这样我们的“Hello Android Wear”应用程序就完成了。

图2:更改文本后的输出

需要考虑的事实

在继续进行下一步之前,需要考虑一些重要因素。

圆形和矩形形状之间的切换

在创建项目时,我们发现了两种不同的布局名称,分别用于矩形和圆形Wear设备。在项目结构中,您会在布局文件夹中找到这两个布局文件以及一个主XML布局文件。名为“rect_activity_main.xml”的文件定义了方形Wear屏幕的布局,而“round_activity_main.xml”文件定义了圆形Wear屏幕的布局。

当我们创建另一个针对方形屏幕的模拟器并运行我们之前做的相同项目时,输出看起来相似,但文本值不同,因为我们还没有在string.xml文件中更改方形屏幕文本视图的字符串值。

 

图:圆形和方形Wear模拟器的输出比较

在为Wear应用程序创建布局时,您需要为方形和圆形Wear设备考虑不同的机制。因为相同的布局或设计可能会被裁剪在设备屏幕的角落附近,或者看起来不好。有两种解决方案可以克服这种情况:一种是WatchViewStub,另一种是BoxInsetLayout

WatchViewStub介绍

转到activity_main.xml文件,您可能会发现XML代码以WatchViewStub开始和结束标签,对吗?

WatchViewStub在运行时检测屏幕形状并加载矩形或圆形布局。您需要分别为矩形和圆形布局创建和填充UI视图。

<android.support.wearable.view.WatchViewStub 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    ...

    app:rectLayout="@layout/rect_activity_main"
    app:roundLayout="@layout/round_activity_main"
    ...>    

</android.support.wearable.view.WatchViewStub>

XML布局名称应与activity_main.xml文件中定义的app:rectLayoutapp:roundLayout的值匹配。根据我们之前做的示例,它们应分别命名为rect_activity_main.xmlround_activity_main.xml

现在从JAVA的角度来看,在布局加载完成之前,您无法访问任何子视图。您应该实现OnLayoutInflatedListener接口来检测何时完成布局加载。当正确的视图加载完成时,WatchViewStub将调用onLayoutInflated方法,该方法使用findViewById获取子视图的所需引用。

final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);

stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
    @Override
    public void onLayoutInflated(WatchViewStub stub) {
        mTextView = (TextView) stub.findViewById(R.id.text);
    }
});

BoxInsetLayout

BoxInsetLayout允许您定义一个适用于方形和圆形屏幕的单一布局。要在该区域内显示,布局内的子视图会指定layout_box属性,并与topbottomleftrightall组合。

                                               

图:圆形屏幕上的BoxInsetLayout

 

构建酷炫的东西

让我们扩展我们的工作,构建一些酷炫的东西。我们将制作一个数学游戏,它将在60秒内生成随机方程,用户将得到得分。

设计

首先,我们需要设计我们的主布局文件,使其适合圆形和方形设备。我们将使用WatchViewStub来完成这项工作。

我们将像下面这样设计我们的应用程序。让我们用xml来设计。

 图1:应用布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/back"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="2">

        <TextView
            android:id="@+id/textTimer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fontFamily="sans-serif-condensed"
            android:gravity="center"
            android:text="0:59"
            android:textColor="@android:color/holo_blue_bright"
            android:textSize="22sp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1.5"
        android:orientation="vertical"
        android:weightSum="2">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="0.8">

            <TextView
                android:id="@+id/gameEquation"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fontFamily="sans-serif-light"
                android:gravity="center"
                android:text="22*82 = ?"
                android:textColor="@android:color/white"
                android:textSize="35sp" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1.2"
            android:orientation="horizontal"
            android:weightSum="2">

            <EditText
                android:id="@+id/answer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:layout_weight="1" />

            <ImageView
                android:id="@+id/next"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_weight="2"
                android:background="@drawable/next"
                android:scaleType="fitXY" />

        </LinearLayout>

    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:layout_weight="1.5"
        android:orientation="vertical"
        android:weightSum="3">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:weightSum="4">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="1"
                android:textSize="18sp"
                android:onClick="onText1_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="2"
                android:textSize="18sp"
                android:onClick="onText2_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="3"
                android:textSize="18sp"
                android:onClick="onText3_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:background="@drawable/backspace"
                android:layout_margin="2dp"
                android:onClick="onTextBackSpace_Click"
                android:clickable="true"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:weightSum="4">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="4"
                android:textSize="18sp"
                android:onClick="onText4_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="5"
                android:textSize="18sp"
                android:onClick="onText5_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="6"
                android:textSize="18sp"
                android:onClick="onText6_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="-"
                android:textSize="18sp"
                android:onClick="onTextMinus_Click"
                android:clickable="true"/>

        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:orientation="horizontal"
            android:weightSum="4">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="7"
                android:textSize="18sp"
                android:onClick="onText7_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="8"
                android:textSize="18sp"
                android:onClick="onText8_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="9"
                android:textSize="18sp"
                android:onClick="onText9_Click"
                android:clickable="true"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:onClick="onText0_Click"
                android:clickable="true"/>

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

功能

为了计算游戏时间,我们将需要一个CountDownTimer控件和一些变量来跟踪时间。

private long timeRemaining = 0;
private final int gameLengthInMillis = 60000;
private final int countDownInterval = 1000;

public CountDownTimer timer;

现在我们将利用CountDownTimeronTick事件,并将剩余时间值打印到TextView。

// Set countdown

timer = new CountDownTimer(gameLengthInMillis, countDownInterval) {
    @Override
    public void onTick(long millisUntilFinished) {

        if (isPaused || isCanceled) {

            timer.cancel();
            Log.d("GameActivity:", "Timer >>1st timer cancled. remaining time:" + millisUntilFinished);

        } else {

            timeRemaining = millisUntilFinished;
            mTextView.setText(Long.toString(timeRemaining / 1000));
        }
    }

为了生成随机方程,我们将使用一个单独的类来随机生成运算符和数字。

private char[] operators = new char[] { '+', '-', '*', '/' };  // generate operator
int firstNumber = random.nextInt(10); // generate first random number
int secondNumber = random.nextInt(10); // generate second random number

在生成这三样随机东西之后,我们将把它们连接起来生成一个方程。

String equation = first + " " + operator + " " + second + " = ?" ;

接下来,我们需要触发一个按钮事件,该事件会在每次点击时刷新这个随机生成的方程。我们还需要比较用户输入的答案和实际答案。此外,我们需要跟踪正确和错误的答案数量并计算它们。为了解决数学方程,我们可以使用一个名为evalex的第三方开源项目。

buttonNext.setOnClickListener(new View.OnClickListener() {

                            public void onClick(View v) {

                                if (editTextAns.getText().toString().isEmpty()) {
                                    Toast.makeText(getApplicationContext(), "Please write some answer first", Toast.LENGTH_SHORT).show();
                                } else {
                                    total += 1;
                                    int ans = Integer.parseInt(editTextAns.getText().toString());
                                    if (ans == randomEquation.answer) {
                                        correct += 1;
                                    } else {
                                    }
                                    editTextAns.setText("");
                                    refreshEquation();
                                }
                            }
                        }
);

当计时器结束时,我们需要导航到结果页面以显示用户的表现。因此,我们将使用CountDownTimeronFinish()方法。

@Override

public void onFinish() {

    Intent intent = new Intent(MainActivity.this, ResultsActivity.class);
    intent.putExtra("correct", correct);
    intent.putExtra("total", total);
    startActivity(intent);
    finish();

    Log.d("GameActivity:", "Timer >> finished 1 min");
}

ResultsActivity.class中,我们将打印传递给此活动的值,并通过将其保存在SharedPreferences中来跟踪最高分。

final SharedPreferences sharedPreferences = getSharedPreferences("prefs", 0);
int currentHighScore = sharedPreferences.getInt("highscore", 0);

if (score > currentHighScore) {
    isHighScore = true;
    SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
    sharedPreferencesEditor.putInt("highscore", score);
    sharedPreferencesEditor.commit();
    highScoreTextView.setText("New High score !!!");
} else {
 highScoreTextView.setText("High score of this level is " + currentHighScore);
}

在成功设置所有这些组件后,当我们构建项目时,我们将获得一个很棒的数学应用程序,它肯定会在您的空闲时间娱乐您!

 

 

图2:数学游戏的最终输出

附加功能

将Wear模拟器连接到Android设备

让我们将Wear模拟器连接到我们的真实Android设备(手机/平板电脑)。为此,请转到设备的设置,并在开发者选项中勾选USB调试(如果之前未选择)。

图1:保持USB调试开启

现在,从Google Play下载Android Wear应用程序并将其安装到您的设备上。安装后,将设备连接到计算机并打开命令窗口。输入命令“adb devices”。

图2:adb命令显示的可用连接设备列表

接下来,输入命令“adb -d forward tcp:5601 tcp:5601”来打开连接端口。

打开设备的蓝牙连接,并将其与Wear模拟器配对。配对完成后,您将在设备上的Wear应用程序中看到此窗口。

图3:设备与模拟器连接。

要检查您的设备是否与Wear模拟器连接,您可以尝试多种方法。这里我们检查手表通知。选择它,您将看到一个包含几个选项的窗口。从中选择“Incoming Phone Call”选项,您将直接收到到Wear模拟器的通话通知。瞧!您的设备和Wear模拟器现已连接。您可以从Wear模拟器控制设备的音乐播放器等。

  

图4:从设备发送呼叫通知到Wear模拟器。

关注点

正如本文前面所说,在单个文章中涵盖整个Wearable平台是一段漫长的旅程。但我们已经看到了在该平台开发应用程序的最重要因素。我们创建了一个示例Hello Wear应用程序,创建了Wear模拟器并构建了应用程序。然后,我们为手表创建了一个完整的数学游戏。有无数种方法可以将此应用程序升级为商业级应用程序。通过一些逻辑关卡,它可以更有趣。关于Google MapWatchFace等,仍然有更多有趣的Wearable应用程序开发领域。希望我将来会带回那些很棒的话题。在此之前,祝您玩Android Wear愉快。祝您Droid愉快!

文章历史

2015.10.17 - 解决了格式问题

2015.10.16 - 主要文章发布

参考

© . All rights reserved.