Wearable Chess (征服 Android Wear,第一部分)






4.92/5 (23投票s)
隆重推出 Wearable Chess - 一款适用于 Android 可穿戴设备的完整开源国际象棋应用程序!
引言
你好,欢迎来到 Wearable Chess!
自从我开始编程以来,我就梦想着创建一个国际象棋应用程序——最好是由我自己创建的国际象棋引擎驱动。不幸的是,尽管我曾多次尝试创建自己的国际象棋 AI,但到目前为止,由于各种原因,每一次尝试都失败了。我离真正的国际象棋 AI 最近的一次是一个能够强制执行规则的引擎——但不包括王车易位和吃过路兵——并且只执行非常简单(且缓慢!)的 1-ply 搜索。
我将来会有一天写我自己的国际象棋 AI 吗?很可能,是的……但不是今天,也不是为了这篇文章。相反,这个应用程序由 Toledo Nanochess 提供支持,这是一个由Óscar Toledo Gutiérrez 创建的、极其紧凑的引擎。
我在构建 Wearable Chess 的过程中学到了很多东西,在这篇文章中,我将尝试分享我学到的东西——从安装 Android Studio 到在 Google Play 上发布完成的应用程序。
请注意:当我第一次向人们展示这个应用程序时,有些人担心棋盘会太难控制。然而,一旦他们有机会试用该应用程序,他们就会惊喜地发现它实际上是多么容易——事实上,我自己也对棋盘的易于控制感到有些惊讶。:-)
如果您在阅读本文时有任何疑问,请随时在页面底部的评论部分提出。祝您阅读愉快!
有用的先决条件
- 一些 Java 经验
……但如果您以前没有使用过 Java,请不要担心。Java 相对容易现学现用,尤其如果您(像我一样)已经熟悉使用 C# 这样的类似语言。 - GIMP
您也可以使用 Inkscape。至少您需要某种图像编辑软件来创建游戏素材和图标。 - Google Play 开发者帐户(收费 25 美元)
如果您想发布您的应用程序,您将需要购买一个。Google Play 很棒,因为这只需要一次性付费——您无需每年支付费用即可保持开发者身份。 - 用于调试的真实设备
虽然不是严格必需的,但真实设备肯定非常有帮助。但是,如果您没有设备,您可以使用 AVD。
设置我们的开发环境
为了确保完全的初学者在这篇文章中不会被落下,本节将解释 Android Wear 开发的一些基本概念。如果您已经熟悉 Android 开发,那么您可能想跳过这一部分 :-)
Android Studio
曾经,开发原生 Android 应用程序的唯一方法是使用 Eclipse,但最近 Google 发布了一个官方的、定制的 Android IDE,名为 Android Studio。我强烈建议您在构建现代 Android 应用程序时使用 Android Studio,主要是因为它已经考虑到像可穿戴设备这样的现代设备而设计,并且对于初学者来说,需要更少的调整。
前往 Google 下载并安装 Android Studio。
创建 AVDs
AVD(Android 虚拟设备)允许您在计算机上测试和调试您的应用程序,而无需连接真实设备。因为它们是本地包含的,所以它们通常比真实设备更快地接受和安装新的构建。当您使用真实设备时,您必须等待每个构建通过蓝牙编译和安装。
要创建 Android Wear AVD,请在 Android Studio 中转到工具>>Android>>AVD 管理器,然后在窗口左下角点击“创建虚拟设备”。在窗口左侧的标签中选择“Wear”,在屏幕中间选择一种类型,然后按照向导完成其余步骤。我建议为您的 AVD 启用 GPU 加速——这将使 AVD 更加响应。
在真实设备上进行调试
这可能会很棘手。要配置您的 Android Wear 手表进行调试,您必须……
- 确保您的手表和手机都启用了开发者模式。
开发者模式可以在设置应用程序的“系统”下找到。如果您在系统设置下看不到“开发者选项”,请改为转到“关于”,向下滚动并反复点按“版本号”字段,直到您收到一条通知,说明您已解锁开发者功能。此过程适用于您的手机和手表。 - 通过 USB 将手机连接到您的计算机.
- 确保您的手机连接为“相机”而不是“媒体设备”。
我花了很长时间才弄清楚这一点是必需的! - 打开命令提示符并导航到 Android SDK 的
platform-tools
目录。我通过使用此命令来实现:"cd C:\Program Files (x86)\Android\android-sdk\platform-tools"
- 仔细检查您的手机是否配置正确,请输入命令
"adb devices"
- 如果您运行该命令后手机未列出,则需要为您的手机安装 ADB 驱动程序。尝试搜索官方驱动程序,或查看 koush 的这个很棒的 Universal ADB Driver。
- 运行
"adb devices"
后可以看到您的手机已列出,请打开手机上的 Android Wear 配套应用,然后点按齿轮图标打开设置。打开“通过蓝牙进行调试”。将设置活动保持在手机上打开。 - 在手表上打开开发者选项并启用“ADB 调试”和“通过蓝牙调试”。
- 如果一切顺利,您应该会在手机上看到“
Host: disconnected, Target: connected
”。 - 最后,返回 PC 上的命令提示符并键入以下确切命令:
adb forward tcp:4444 localabstract:/adb-hub
adb connect localhost:4444
就是这样!您现在可以为您的 Android 手表构建和调试应用程序了!
总之,继续介绍 Wearable Chess 的构建过程……
与 Toledo Nanochess 合作
如果您以前没有听说过 Toledo Nanochess,请准备好惊叹。这是 Java 版的整个压缩(且非常难以阅读 ;P)源代码。
//(c)2010 Oscar Toledo G.
import java.applet.*;import java.awt.event.*;import java.awt.*;
public class toledo_chess extends Applet implements MouseListener{
int B,i,y,u,b,I[]=new int[411],G=120,l[]={5,3,4,6,2,4,3,5,1,1,1,1,1,1,1,1,9,9,9
,9,9,9,9,9,13,11,12,14,10,12,11,13,0,11,0,34,33,55,94,0,1,2,3,3,2,1,0,-1,1,-10,
10,-11,-9,9,11,10,20,-9,-11,-10,-20,-21,-19,-12,-8,8,12,19,21,53,47,61,51,47,47
};Image[]im;static final int x=10,z=15,M=10000;
public void init(){for(B=i=y=u=b=0;B++<120;)I[B-1]=B%x!=0?B/x%x<2|B%x<2?7:(B/x&
4)!=0?0:l[i++]|16:7;im=new Image[16];for(;b<15;b++)if(b<7|b>8)im[b]=this.
getImage(this.getDocumentBase(),b+".gif");addMouseListener(this);}
public void stop(){removeMouseListener(this);}
public void update(Graphics g){paint(g);}
public void paint(Graphics g){int x,y,c=21,a;boolean n=false;for(y=0;y<320;y+=
40){for(x=0;x<320;x+=40){g.drawImage(im[I[c]&z],x,y,40,40,n?new Color(144,144,
208):new Color(192,192,255),this);if(c==B){g.setColor(new Color(255,255,0));g.
drawRect(x,y,39,39);g.drawRect(x+1,y+1,37,37);}c++;n=!n;}c+=2;n=!n;}}
void Z(){paint(getGraphics());}
public void mouseExited(MouseEvent e){}public
void mousePressed(MouseEvent e){}public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseReleased(MouseEvent e){int s=e.getX()/40+(e.getY()/40*x+21);i=
(I[s]^y)&z;if(i>8){B=s;Z();}else if(B!=0&&i<9){b=s;i=I[B]&z;
if((i&7)==1&(b<29|b
>90))i=14^y;X(0,0,0,21,u,1);B=0;Z();if(y>0){X(0,0,0,21,u,4/*ply*/);X(0,0,0,21,u
,1);B=0;Z();}}}
public String getAppletInfo(){return"Toledo Java Chess by Oscar Toledo G.";}
int X(int w,int c,int h,int e,int S,int s){int t,o,L,E,d,O=e,N=-M*M,K=78-h<
0?-x:x;boolean D,J;y^=8;G++;D=w>0||s>0&&s>=h&&X(0,0,0,21,0,0
)>M;do{if((o=I[p=O])>0){q=o&z^y;if(q<7){A=(q--&2)>0?8:4;
C=(o&z)!=9?l[69+q]:57;
do{r=I[p+=l[C]];if(w<1|p==w){g=q>0|p+a!=S?0:S;
if(r<1&(q>0|A<3||g>0)||(r+1&z^y)>
9&&q>0|A>2){if((r&7)==2){y^=8;I[G--]=O;return K;}
m=0;n=o&z;J=true;E=I[p-a]&z;if
(q>0|E!=7)t=n;else{n+=2;t=6^y;}while(n<=t){L=(r>0?l[r&7|32]*9-h-q:0);
if(s>0)L+=
(q!=1?l[p/x+37]-l[O/x+37]+l[p%x+38]-l[O%x+38]+o/16*8:(m>0?9:0))+(q<1?l[p%x+38]-
1+((I[p-1]^n)<1?1:0)+((I[p+1]^n)<1?1:0)+l[n&7|32]*9-99+(g>0?99:0)+(A<2?1:0):
0)+((E^y^9)<1?1:0);if(s>h||1z|D){I[p]=n;I[O]=0;if(m>0){I[g]=I[m];I[m]=0
;}else if(g>0)I[g]=0;L-=X(s>h|D?0:p,L-N,h+1,I[G+1],E=q>0|A>1?0:p,s);
if(h<1&s==1&&B==O&i==n&p==b&L>-M)
G--;return u=E;}J=q!=1||A<7||m>0||s<1|D|r>0|oM;I[O]=o;I[p]=r;
if(m>0){I[m]=I[g];I[g]=0;}else if(g>0)I[g]=9^y;}if(L>N){I[G]=O;if(s>1)
{if(h>0&&c-L<0){y^=8;G--;return L;}if(h<1){i=n;B=O;b=p;}}N=L;}
if(J)n++;else{g=p;m=p0||I[p+=p-O]>0)n++;}}}}}
while(r<1&q>2||((p=O)>0)&&(q>0|A>2|o>z&r<1)
&&++C>0&&--A>0);}}if(++O>98)O=20;}
while(e!=O);y^=8;G--;return N!=-M*M&&N>-K+1924|D?N:0;}}
就是这样!上面的代码可以进行一场完整、智能的国际象棋比赛,还可以渲染棋盘并接受走法输入。事实上,如果您访问 nanochess.org,您会发现此代码的作者还有几个 Javascript 版本,它们的尺寸大约是上面代码的一半。非常令人印象深刻。
那么,我是如何重新配置它以在我的应用程序中工作的呢?我做的第一件事是将上述源代码放入一个在线代码美化工具中,结果是这样的:
//(c)2010 Oscar Toledo G.
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class toledo_chess extends Applet implements MouseListener {
int B, i, y, u, b, I[] = new int[411], G = 120, l[] = {
5, 3, 4, 6, 2, 4, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 13, 11, 12,
14, 10, 12, 11, 13, 0, 11, 0, 34, 33, 55, 94, 0, 1, 2, 3, 3, 2, 1, 0, -1, 1, -10,
10, -11, -9, 9, 11, 10, 20, -9, -11, -10, -20, -21, -19, -12, -8, 8, 12, 19, 21, 53,
47, 61, 51, 47, 47
};
Image[] im;
static final int x = 10, z = 15, M = 10000;
public void init() {
for (B = i = y = u = b = 0; B++ < 120;) I[B - 1] = B % x != 0 ? B / x % x < 2 |
B % x < 2 ? 7 : (B / x & 4) != 0 ? 0 : l[i++] | 16 : 7;
im = new Image[16];
for (; b < 15; b++) if (b < 7 | b > 8) im[b] = this.
getImage(this.getDocumentBase(), b + ".gif");
addMouseListener(this);
}
public void stop() {
removeMouseListener(this);
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
int x, y, c = 21, a;
boolean n = false;
for (y = 0; y < 320; y += 40) {
for (x = 0; x < 320; x += 40) {
g.drawImage(im[I[c] & z], x, y, 40, 40, n ? new Color(144, 144,
208) : new Color(192, 192, 255), this);
if (c == B) {
g.setColor(new Color(255, 255, 0));
g.
drawRect(x, y, 39, 39);
g.drawRect(x + 1, y + 1, 37, 37);
}
c++;
n = !n;
}
c += 2;
n = !n;
}
}
void Z() {
paint(getGraphics());
}
public void mouseExited(MouseEvent e) {}
public
void mousePressed(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
int s = e.getX() / 40 + (e.getY() / 40 * x + 21);
i = (I[s] ^ y) & z;
if (i > 8) {
B = s;
Z();
} else if (B != 0 && i < 9) {
b = s;
i = I[B] & z;
if ((i & 7) == 1 & (b < 29 | b > 90)) i = 14 ^ y;
X(0, 0, 0, 21, u, 1);
B = 0;
Z();
if (y > 0) {
X(0, 0, 0, 21, u, 4 /*ply*/ );
X(0, 0, 0, 21, u, 1);
B = 0;
Z();
}
}
}
public String getAppletInfo() {
return "Toledo Java Chess by Oscar Toledo G.";
}
int X(int w, int c, int h, int e, int S, int s) {
//EDIT: Removed for clarity in this article.
//The code that was here is near-impossible to read, even when beautified.
}
}
这样更容易阅读了!:-)
现在我们可以看到这段代码被分解为几个主要函数:
X(int w, int c, int h, int e, int S, int s)
这个经过高度混淆的函数几乎完成了所有的繁重工作,包括 alpha-beta 剪枝和走法验证。mouseReleased(MouseEvent e)
这个函数是让这段代码中的一切“发生”的原因。我们可以一眼看出它控制着哪些棋子何时移动。paint(Graphics g)
这个函数绘制了一个简单的棋盘。init()
这个函数设置了每个新游戏,并通常为行动做准备。
有了这些知识,我创建了一个名为 Chess.java 的类,它允许我实现突出显示合法走法、撤销走法等功能。它是这样的:
/**
* Created by mitch on 27/09/2015.
* Enormous amounts of this code are from Toledo Nanochess (nanochess.org)
* This code is free for non-commercial use, but for commercial use please contact the author of Nanochess using the URL above.
*/
public class Chess {
public int B, i, y, u, b, L, I[] = new int[411], G = 120, l[] = {
5, 3, 4, 6, 2, 4, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 13, 11,
12, 14, 10, 12, 11, 13, 0, 11, 0, 34, 33, 55, 94, 0, 1, 2, 3, 3, 2, 1, 0, -1, 1,
-10, 10, -11, -9, 9, 11, 10, 20, -9, -11, -10, -20, -21, -19, -12, -8, 8, 12, 19,
21, 53, 47, 61, 51, 47, 47
};
static final int x = 10, z = 15, M = 10000;
//This is basically the init() function, minus the UI stuff.
public Chess() {
for (B = i = y = u = b = 0; B++ < 120;) I[B - 1] = B % x != 0 ? B / x % x < 2 |
B % x < 2 ? 7 : (B / x & 4) != 0 ? 0 : l[i++] | 16 : 7;
}
//Once a starting square has been selected, this function tries every square on the board
//to see whether a move there is legal.
public ArrayList<<integer> getLegalMoves(int s) {
ArrayList<integer> validSq = new ArrayList<>();
for (int j = 21; j < 99 && j != 29; j += j > 90 ? -69 : 10) {
Chess verifier = new Chess();
verifier.CopyBoard(this);
if (verifier.Move(s, j))
validSq.add(j);
}
return validSq;
}
//Used to determine whether a capture has taken place!
public int CountPieces() {
int count = 0;
for (int j : I){
if (j != 0 && j != 7 && j != 8 && j != 15)
++count;
}
return count;
}
//Allows this instance of Chess to copy the board of another.
public void CopyBoard(Chess c) {
I = c.I.clone(); B = c.B; i = c.i; y = c.y; u = c.u; L = c.L; b = c.b;
}
//If the move is legal, it is played and the function returns true,
//Otherwise the board remains unchanged, and the function returns false.
public boolean Move(int s, int f) {
MovePart(s);
return MovePart(f);
}
public boolean MovePart(int s) {
i = (I[s] ^ y) & z;
if (i > 8) {
B = s;
}
else if (B != 0 && i < 9) {
b = s;
i = I[B] & z;
if ((i & 7) == 1 & (b < 29 | b > 90)) i = 14 ^ y;
X(0, 0, 0, 21, u, 1);
L = B; //Highlight last moves
B = 0;
return (y > 0); // If the move was accepted and performed as legal,
// y will be > 0. Don't ask me Y :D
}
return false;
}
...
public void Respond(int depth) {
X(0, 0, 0, 21, u, depth /*ply*/ );
X(0, 0, 0, 21, u, 1);
L = B; //Highlight last moves
B = 0;
}
int X(int w, int c, int h, int e, int S, int s) {
...
}
}
Toledo Nanochess 使用一种称为 10x12 数组表示法来表示棋盘。我在这里没有时间完全解释它,但基本上它意味着棋盘上的每个格子都被分配了一个数字,例如 a8 格是 21,b8 格是 22,依此类推,直到 h8 格是 28。当您向下移动一行到 a7 时,您需要加三得到 31。b7 是 32,c7 是 33,依此类推,每次在棋盘上向下移动一行时加 3。
这就是为什么 getLegalMoves() 函数中的 for
语句具有奇怪的参数。
使用这个 Chess.java 类,实现走法撤销就像维护一个 Chess 对象列表并根据需要恢复它们一样简单。
public void StoreToHistory() {
if (history_index < history.size()) {
Chess record = new Chess();
record.CopyBoard(toledo);
history.set(history_index, record);
}
else {
Chess record = new Chess();
record.CopyBoard(toledo);
history.add(record);
}
++history_index;
}
public void Takeback(int steps) {
if (history_index - steps > 0)
toledo = history.get(history_index -= steps);
Z();
}
构建 Wearable Chess 的其余部分
Wearable Chess 的其余部分相对容易创建。我将列出一些更值得注意的部分。
游戏图形 - 我在哪里获得的?
为了创建木制游戏图形,我首先搜索了免费的木材图片,然后找到了一张包含大量细节的图片,最后使用 GIMP 选择了一些有用的区域。
Stauton 国际象棋棋子来自 WikiMedia。
绘制棋盘
我不得不重写棋盘渲染部分以适用于 Android Wear。这是我的版本,附带注释和解释:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Essential variables
double x, y;
int c = 21;
boolean n = false;
width = canvas.getWidth();
double tile_width = ((double)width / 8.0);
for (y = 0; y < width; y += tile_width) {
for (x = 0; x < width; x += tile_width) {
//Get the rectangle of the square
Rect r_sq = new Rect((int) x, (int) y, (int) (x + tile_width),
(int) (y + tile_width));
//Light or Dark square?
pSquare.setColor(n ? Color.argb(255, 144, 144, 208) :
Color.argb(255, 192, 192, 255));
canvas.drawBitmap(n ? b_sq_black : b_sq_white, new Rect(0, 0,
b_sq_white.getWidth(), b_sq_white.getHeight()), r_sq, pSquare);
if (Highlights) {
//Previous moves
if (c == toledo.b || c == toledo.L) {
pSquare.setColor(Color.argb(120, 0, 0, 180));
canvas.drawRect(r_sq, pSquare);
}
//Is it a valid square to move to?
if (validSq.indexOf(c) != -1) {
pSquare.setColor(Color.argb(120, 0, 180, 0));
canvas.drawRect(r_sq, pSquare);
}
}
//Selected square?
if (c == toledo.B) {
pOutline.setStrokeWidth(1);
pOutline.setColor(Color.argb(255, 255, 255, 0));
canvas.drawRect((int)(x + 1), (int)(y + 1), (int)(x + tile_width),
(int)(y + tile_width), pOutline);
}
//Draw the piece
pSquare.setColor(Color.argb(255, 0, 0, 0));
canvas.drawBitmap(im[toledo.I[c] & z], new Rect(0, 0,
im[toledo.I[c] & z].getWidth(), im[toledo.I[c] & z].getHeight()),
r_sq, pSquare);
c++;
n = !n;
}
c += 2;
n = !n;
}
is_drawing = false;
}
创建菜单
菜单是通过一种称为 GridViewPager 的东西实现的,它基本上允许可滑动的全屏片段一次占据一个屏幕。在这种情况下,我有一个包含 ChessboardView 的片段,以及另一个包含菜单的 Fragment。
创建这样的菜单可能需要一整篇文章——也许我将来会写。但就目前而言,这是 SettingsFragment.java 中将整个菜单串联起来的代码片段:
String[] headers = { "Takeback", "New Game", "Difficulty",
"Style", "Highlights", "Vibrations", "About" };
String[] subheaders = { "Undo last move", "Start a new game",
"4 (Normal)", "Staunton", "On", "Off",
"About this app"};
int[] resources = { R.drawable.ic_undo_black_24dp, R.drawable.white_king_st,
R.drawable.brain, R.drawable.black_knight_st, R.drawable.highlights,
R.drawable.vibration_on, R.drawable.ic_info_outline_black_24dp};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
SharedPreferences settings = rootView.getContext().getSharedPreferences(PREFS_NAME, 0);
boolean Vibration = settings.getBoolean("vibration", false);
boolean Highlights = settings.getBoolean("highlights", true);
subheaders[4] = Highlights ? "On" : "Off";
subheaders[5] = Vibration ? "On" : "Off";
WearableListView listView = (WearableListView) rootView.findViewById(R.id.wearable_list);
listView.setAdapter(new CustomListAdapter(rootView.getContext(), headers, subheaders,
resources));
listView.setClickListener(this);
listView.setGreedyTouchMode(true);
...
}
启用按住关闭
默认情况下,所有 Android 应用程序都可以通过向左滑动来关闭。因为我想避免在游戏过程中意外关闭应用程序,所以我决定实现 Google 推荐的替代方案:按住关闭。
要为您自己的应用程序执行此操作,请创建一个名为 hold_to_exit.xml 的新资源文件:
<resources>
<style name="HoldToExit" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:windowSwipeToDismiss">false</item>
</style>
</resources>
然后,在 AndroidManifest.xml 中,将您的 MainActivity 的样式设置为“HoldToExit”,如下所示:
android:theme="@style/HoldToExit"
最后,将此添加到您的 MainActivity 的 Java 文件中:
private DismissOverlayView mDismissOverlay;
private GestureDetector mDetector;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mDismissOverlay = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
mDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent ev) {
mDismissOverlay.show();
}
});
...
}
@Override
public boolean dispatchTouchEvent (MotionEvent e) {
return mDetector.onTouchEvent(e) || super.dispatchTouchEvent(e);
}
FAQ - 常见问题解答
在结束本文之前,我想为学习构建 Android Wear 应用程序的初学者包含一个通用的 FAQ 部分。如果您有此处未列出的问题,请随时在本文下方的评论部分提出。
如何创建新的 Android Wear 项目?
打开 Android Studio,然后转到文件>>新建>>新建项目。命名您的应用程序后,点击“下一步”,然后在出现的窗口中勾选“Wear”旁边的框。使用默认设置完成向导的其余部分。
什么是资源,我该如何使用它们?
Android 资源都包含在您新项目的“res”文件夹中,主要用于 UI 目的。它们可以是任何东西,从图像到字符串,再到动画和布局。
例如,一个空白的 Android Wear 项目会有一个名为 activity_main.xml 的资源文件,它看起来像这样:
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
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" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/container" tools:context=".MainActivity"
tools:deviceIds="wear">
<TextView android:id="@+id/text" app:layout_box="all" android:layout_width="match_parent"
android:layout_height="match_parent" android:text="@string/hello_world" />
<TextView android:id="@+id/clock" app:layout_box="all" android:layout_gravity="bottom|start"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textColor="@android:color/white" />
</android.support.wearable.view.BoxInsetLayout>
这个 XML 文件没有什么神奇之处——它只是另一个静态资源,就像图像或图标一样。它唯一的重要性在于 MainActivity.java 使用此资源来膨胀其布局。
//The "actual" activity is defined here.
//It is possible to create Activities, Views, and Fragments without using an XML resource.
public class MainActivity extends WearableActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //References activity_main.xml
...
}
...
}
资源有很多用途——例如,将长字符串值存储在名为 strings.xml 的资源文件中,而不是将它们硬编码到 Java 代码或布局 XML 资源中,这被认为是良好的实践。
mhdpi、xxxhdpi 等文件夹是做什么用的?
这些是为了帮助您在具有不同像素密度的屏幕上干净地缩放您的应用程序。
有关更多信息,请参阅此页面,来自官方 Android 开发者网站。
如何创建新的 Activity?
最简单的方法是使用 Android Studio 的向导。在 Android Studio 中,右键单击项目浏览器中的 res/layouts
文件夹,然后转到新建>>Activity>>始终开启 Wear Activity。
要手动创建 Activity,只需创建一个新的 Java 类并让它继承 WearableActivity
类。
package ...;
import android.support.wearable.activity.WearableActivity;
public class SomeCustomActivity extends WearableActivity {
}
别忘了在您的 AndroidManifest.xml 文件中声明它:(否则,当您尝试使用您的 Activity 时会收到错误)
<activity
android:name=".SomeCustomActivity"
android:label="@string/title_activity_..."
android:theme="@android:style/Theme.DeviceDefault.Light" >
</activity>
构建 Android Wear 应用程序时应考虑哪些事项?
您应主要关注一流的性能和最少的电池消耗。
因为 Android 智能手表仍然是,嗯,**手表**,我们作为开发者绝对应该避免浪费用户的时间和电池。理想的 Android Wear 应用程序应该轻巧、简单且易于随时使用。
我的设备无法连接到 ADB!我该怎么办?
有时很难与真实的 Android Wear 手表保持稳定的连接。我尝试过的一些方法包括:
- 重启 PC/手机/手表。您可以使用 adb 命令
"reboot"
重启手机。 - 运行
adb disconnect
,然后通过转发 TCP 端口等方式重新连接(参见上面的“在真实设备上调试”)。 - 关闭设备上的调试模式,然后重新打开。
- 更新 ADB 驱动程序
以上某些组合解决了到目前为止我遇到的所有问题。
如何发布我的应用程序?
您将需要:
- 一个 logo/图标
- 一些截图
- 一个 Google Play 开发者帐户
一旦您拥有了这些,请确保您的可穿戴设备应用程序和您的手机应用程序的包名完全相同。确保两个应用程序在其 Manifest 文件中声明的权限完全相同。
然后,在 Android Studio 中,转到构建>>生成签名 APK 并按照向导创建适合发布的 .APK 文件。然后,您就可以通过开发者控制台将您的应用程序上传到 Google Play 了。
在哪里可以获得 Google Play 开发者帐户?
从这里。费用为 25 美元,但这只是一个一次性费用。
发布时有什么陷阱需要注意吗?
是的!确保您的可穿戴设备应用程序已正确打包到您的移动应用程序中。
当我第一次尝试发布 Wearable Chess 时,Google Play 从可穿戴设备部分拒绝了它,因为我忘记了匹配手机和 Wear 应用程序之间的必需权限。结果,可穿戴设备应用程序无法被检测到。
结论
非常感谢您阅读到最后!构建这个应用程序对我来说是一段旅程,我很高兴能与您分享这个故事 :D
想阅读更多吗?点击此处阅读我“征服 Android Wear”系列的下一篇文章。
一如既往——如果您有任何评论、问题或建议,请在下方发布。我非常乐意收到您的来信。
顺便说一句,如果您有 Android Wear 智能手表,为什么不访问 Google Play 获取免费的 Wearable Chess 呢?
如果您喜欢这款应用程序,请不要忘记给它五星好评并与您的朋友分享!:cool
历史
- 2015/9/10 发布了 Wearable Chess 的第一个版本
- 2015/10/15 发布了一个小更新。稍微解释了一下菜单,修正了一些拼写错误,并链接到了本系列的第二篇文章。
- 2015/10/16 修正了 Toledo Chess 代码的格式,并做了一些其他小的改动。谢谢 Nelek!