В этой серии мы создаем игру Hangman для платформы Android. В первом уроке мы настроили приложение для представления пользователю двух экранов, а также начали с элементов пользовательского интерфейса, точных изображений и формы. Во втором уроке мы увеличим макет игры.
Вступление
Создание макета игры потребует использования адаптера для создания буквенных кнопок и размещения частей тела, которые мы будем отображать, когда пользователи выбирают неправильные буквы. Мы также будем хранить ответы игрока в XML, извлекать их и выбирать случайное слово, используя Java.
Чтобы освежить вашу память, так будет выглядеть финальная игра.
1. Расположите части тела
Шаг 1
В предыдущем уроке мы создали изображения для виселицы и для шести частей тела. Мы разместим их внутри макета игры в этом уроке. Позиции, которые вы устанавливаете для этих элементов, должны определяться элементами изображения, которые вы используете. Один из подходов состоит в том, чтобы импортировать изображения в редактор изображений, такой как Adobe Photoshop, расположить их вручную, а затем использовать их положения x
и y
относительно изображения виселицы, чтобы определить правильные положения для применения к каждому изображению в макете XML. Если вы начинаете с демонстрационных изображений, которые мы включили в предыдущий учебник, вы можете использовать значения, перечисленные в этом учебнике.
Начните с открытия activity_game.xml. Внутри линейного макета, который мы добавили в предыдущем уроке, введите относительный макет для хранения семи изображений, которые будут составлять область виселицы.
1
2
3
4
5
6
7
|
<RelativeLayout
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:background=»#FFFFFFFF»
android:gravity=»center»
android:paddingTop=»15dp» >
</RelativeLayout>
|
Шаг 2
Внутри созданного вами относительного макета начните с добавления изображения виселицы, как показано ниже.
1
2
3
4
5
6
7
|
<ImageView
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/gallows»
android:paddingLeft=»0dp»
android:paddingTop=»0dp»
android:src=»@drawable/android_hangman_gallows» />
|
Не забудьте изменить имя для рисования, если изображение, которое вы используете, названо по-другому. Мы устанавливаем отступ слева и сверху изображения на 0
чтобы мы могли расположить другие изображения относительно его положения. Мы добавим строковые ресурсы для описаний контента чуть позже в этом уроке. Далее идет голова.
1
2
3
4
5
6
7
8
|
<ImageView
android:id=»@+id/head»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/head»
android:paddingLeft=»108dp»
android:paddingTop=»23dp»
android:src=»@drawable/android_hangman_head» />
|
Если вы используете свои собственные изображения, вам нужно соответственно изменить левый и верхний отступы. Мы используем атрибут id
чтобы мы могли ссылаться на изображение в коде. Это необходимо, чтобы он появлялся и исчезал в зависимости от ввода пользователя. Следующее изображение, которое мы добавляем, это тело.
1
2
3
4
5
6
7
8
|
<ImageView
android:id=»@+id/body»
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/body»
android:paddingLeft=»120dp»
android:paddingTop=»53dp»
android:src=»@drawable/android_hangman_body» />
|
Это выглядит очень похоже на то, что мы сделали для головы, и, как вы можете видеть ниже, руки и ноги очень похожи.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<ImageView
android:id=»@+id/arm1″
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/arm»
android:paddingLeft=»100dp»
android:paddingTop=»60dp»
android:src=»@drawable/android_hangman_arm1″ />
<ImageView
android:id=»@+id/arm2″
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/arm»
android:paddingLeft=»120dp»
android:paddingTop=»60dp»
android:src=»@drawable/android_hangman_arm2″ />
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<ImageView
android:id=»@+id/leg1″
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/leg»
android:paddingLeft=»101dp»
android:paddingTop=»90dp»
android:src=»@drawable/android_hangman_leg1″ />
<ImageView
android:id=»@+id/leg2″
android:layout_width=»wrap_content»
android:layout_height=»wrap_content»
android:contentDescription=»@string/leg»
android:paddingLeft=»121dp»
android:paddingTop=»90dp»
android:src=»@drawable/android_hangman_leg2″ />
|
Откройте XML-файл строк res / values проекта и добавьте строки описания контента, которые мы использовали в приведенных выше фрагментах кода.
1
2
3
4
5
|
<string name=»gallows»>The Gallows</string>
<string name=»head»>The Head</string>
<string name=»body»>The Body</string>
<string name=»arm»>An Arm</string>
<string name=»leg»>A Leg</string>
|
Вернитесь к файлу макета и переключитесь на вкладку Графический макет, чтобы увидеть результат нашей работы. Отрегулируйте верхний и левый отступы каждого изображения, чтобы отрегулировать их положение при необходимости.
Всякий раз, когда запускается новая игра, части тела должны быть скрыты. Каждая часть тела раскрывается, если игрок выбирает букву, которой нет в целевом слове.
2. Храните ответные слова
В игре будет использоваться набор предопределенных слов, которые мы будем хранить в XML. В папке значений ресурсов проекта добавьте новый файл и назовите его arrays.xml.
Переключитесь на вкладку XML , создайте массив и добавьте к нему несколько слов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<resources>
<string-array name=»words»>
<item>CHARGER</item>
<item>COMPUTER</item>
<item>TABLET</item>
<item>SYSTEM</item>
<item>APPLICATION</item>
<item>INTERNET</item>
<item>STYLUS</item>
<item>ANDROID</item>
<item>KEYBOARD</item>
<item>SMARTPHONE</item>
</string-array>
</resources>
|
3. Выберите и подарите слово
Шаг 1
Вернувшись в файл макета активности вашей игры, добавьте линейный макет сразу после относительного макета, который мы добавили для области виселицы. Линейный макет зарезервирован для области ответа.
01
02
03
04
05
06
07
08
09
10
|
<LinearLayout
android:id=»@+id/word»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:layout_marginBottom=»5dp»
android:background=»#FFFFFFFF»
android:gravity=»center»
android:orientation=»horizontal»
android:padding=»10dp» >
</LinearLayout>
|
Мы сохраняем каждый символ целевого слова в его собственном текстовом представлении, чтобы мы могли раскрывать каждую букву отдельно. Мы будем использовать id
линейного макета в коде, чтобы добавлять текстовые представления каждый раз, когда выбирается новое слово.
Шаг 2
Откройте класс GameActivity
и начните с добавления следующих операторов импорта вверху.
1
2
3
4
5
6
|
import android.content.res.Resources;
import android.graphics.Color;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;
|
Внутри объявления класса добавьте метод onCreate
как показано ниже.
1
2
3
4
5
|
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
}
|
Мы устанавливаем вид содержимого для файла макета, который мы создали минуту назад.
Шаг 3
Прежде чем двигаться дальше, нам нужно объявить несколько переменных экземпляра. Добавьте объявление непосредственно перед методом onCreate
.
1
2
3
4
5
|
private String[] words;
private Random rand;
private String currWord;
private LinearLayout wordLayout;
private TextView[] charViews;
|
Коллекция слов хранится в массиве, и приложение использует объект rand
для выбора слова из массива каждый раз, когда пользователь запускает новую игру. Мы сохраняем ссылку на текущее слово ( currWord
), макет, который мы создали для хранения области ответа ( wordLayout
), и массив текстовых представлений для букв ( charViews
). Вернувшись в onCreate
, после настройки представления содержимого запросите ресурсы приложения, прочитайте набор слов и сохраните их в переменной экземпляра words
.
1
2
|
Resources res = getResources();
words = res.getStringArray(R.array.words);
|
Обратите внимание, что мы используем имя, которое мы дали слово массив в XML. Инициализируйте объект rand
и строку currWord
.
1
2
|
rand = new Random();
currWord = «»;
|
Получите ссылку на область макета, которую мы создали для ответных писем.
1
|
wordLayout = (LinearLayout)findViewById(R.id.word);
|
Шаг 4
Ряд задач необходимо выполнять каждый раз, когда игрок запускает новую игру. Давайте создадим вспомогательный метод, чтобы все было организовано. После метода onCreate
добавьте следующую схему метода.
1
2
3
|
private void playGame() {
//play a new game
}
|
playGame
методе playGame
начните с выбора случайного слова из массива.
1
|
String newWord = words[rand.nextInt(words.length)];
|
Поскольку метод playGame
вызывается, когда пользователь выбирает снова играть после победы или поражения в игре, важно убедиться, что мы не выбираем одно и то же слово два раза подряд.
1
|
while(newWord.equals(currWord)) newWord = words[rand.nextInt(words.length)];
|
Обновите currWord
экземпляра currWord
новым целевым словом.
1
|
currWord = newWord;
|
Шаг 5
Следующим шагом является создание одного текстового представления для каждой буквы целевого слова. По-прежнему в нашем вспомогательном методе создайте новый массив для хранения текстовых представлений для букв целевого слова.
1
|
charViews = new TextView[currWord.length()];
|
Затем удалите все текстовые представления из макета wordLayout
.
1
|
wordLayout.removeAllViews();
|
Используйте простой цикл for
чтобы перебрать каждую букву ответа, создать текстовое представление для каждой буквы и установить для текста текстового представления текущую букву.
1
2
3
4
|
for (int c = 0; c < currWord.length(); c++) {
charViews[c] = new TextView(this);
charViews[c].setText(«»+currWord.charAt(c));
}
|
Строка хранится в виде массива символов. Метод charAt
позволяет нам получить доступ к символам по определенному индексу. Находясь внутри цикла for
, установите свойства отображения в текстовом представлении и добавьте его в макет.
01
02
03
04
05
06
07
08
09
10
11
|
for (int c = 0; c < currWord.length(); c++) {
charViews[c] = new TextView(this);
charViews[c].setText(«»+currWord.charAt(c));
charViews[c].setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
charViews[c].setGravity(Gravity.CENTER);
charViews[c].setTextColor(Color.WHITE);
charViews[c].setBackgroundResource(R.drawable.letter_bg);
//add to layout
wordLayout.addView(charViews[c]);
}
|
Мы установили цвет текста на белый, чтобы пользователь не мог видеть его на белом фоне. Если они правильно угадывают букву, свойство color для текста становится черным, чтобы показать его игроку. Вернувшись в метод onCreate
, вызовите вспомогательный метод, который мы только что создали.
1
|
playGame();
|
Мы расширим как onCreate
и вспомогательный метод чуть позже.
4. Создайте кнопки письма
Шаг 1
Следующим шагом является создание области для игрока, чтобы выбрать буквы угадать. Пересмотрите игровой макет игры и добавьте следующий вид сетки, чтобы удерживать кнопки с буквами. Вставьте вид сетки сразу после линейного макета, который мы добавили для слова ответа.
01
02
03
04
05
06
07
08
09
10
11
12
|
<GridView
android:id=»@+id/letters»
android:layout_width=»fill_parent»
android:layout_height=»wrap_content»
android:layout_gravity=»center»
android:layout_marginBottom=»5dp»
android:background=»#FF000000″
android:horizontalSpacing=»5dp»
android:numColumns=»7″
android:padding=»5dp»
android:stretchMode=»columnWidth»
android:verticalSpacing=»5dp» />
|
Мы собираемся использовать адаптер для сопоставления букв алфавита с кнопками в виде сетки. Мы выкладываем по семь кнопок на строку, как вы можете видеть из атрибута numColumns
.
Шаг 2
Каждая буква будет кнопкой, добавленной в макет с помощью адаптера. Добавьте новый файл в папку макета проекта, назовите его letter.xml и заполните его следующим фрагментом кода.
1
2
3
4
5
|
<Button xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»wrap_content»
android:layout_height=»35dp»
android:background=»@drawable/letter_up»
android:onClick=»letterPressed» />
|
Мы используем одну из доступных для рисования фигур в качестве фона и устанавливаем метод onClick
который будем реализовывать в следующий раз. Если вы расширяете приложение для работы с разными плотностями экрана, вы можете соответствующим образом настроить атрибут высоты.
Шаг 3
Добавьте новый класс в пакет src вашего проекта, назовите его LetterAdapter
и выберите android.widget.BaseAdapter
качестве суперкласса.
Как вы увидите, класс адаптера включает в себя серию стандартных методов, которые мы будем реализовывать. Добавьте следующие операторы импорта в новый класс.
1
2
3
|
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.Button;
|
Внутри объявления класса добавьте две переменные экземпляра, как показано ниже.
1
2
|
private String[] letters;
private LayoutInflater letterInf;
|
Массив letters
будет хранить буквы алфавита, а инфлятор макета будет применять макет кнопки, который мы определили, к каждому представлению, обрабатываемому адаптером. После переменных экземпляра добавьте метод конструктора для адаптера.
1
2
3
|
public LetterAdapter(Context c) {
//setup adapter
}
|
Внутри конструктора создайте экземпляр массива алфавита и назначьте буквы AZ каждой позиции.
1
2
3
4
|
letters=new String[26];
for (int a = 0; a < letters.length; a++) {
letters[a] = «» + (char)(a+’A’);
}
|
Каждый символ представлен как число, так что мы можем установить буквы от A до Z в цикле, начинающемся с нуля, добавив значение символа A к каждому индексу массива. Еще в методе конструктора укажите контекст, в котором мы хотим накачать макет, который будет передан из основного действия позже.
1
|
letterInf = LayoutInflater.from(c);
|
Eclipse должен был создать схему метода getCount
. Обновите его реализацию следующим образом.
1
2
3
4
|
@Override
public int getCount() {
return letters.length;
}
|
Это представляет количество просмотров, по одному на каждую букву. Мы не вызываем методы в классе адаптера явно в приложении. Именно операционная система использует их для создания элемента пользовательского интерфейса, к которому мы применяем адаптер, который в этом случае будет представлением сетки.
Обновите реализацию getView
как показано в фрагменте кода ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//create a button for the letter at this position in the alphabet
Button letterBtn;
if (convertView == null) {
//inflate the button layout
letterBtn = (Button)letterInf.inflate(R.layout.letter, parent, false);
} else {
letterBtn = (Button) convertView;
}
//set the text to this letter
letterBtn.setText(letters[position]);
return letterBtn;
}
|
Потратьте немного времени, чтобы все впиталось. По сути, этот метод создает каждое представление, сопоставленное элементу пользовательского интерфейса через адаптер. Мы раздуваем созданный нами макет кнопки и устанавливаем букву в соответствии с положением в алфавите, которое представляет представление. Мы заявили, что будет отображено 26 видов, причем позиция каждого отражает свою позицию в алфавитном массиве. Вы можете оставить другие методы в классе адаптера как они есть.
1
2
3
4
5
6
7
8
9
|
@Override
public Object getItem(int arg0) {
return null;
}
@Override
public long getItemId(int arg0) {
return 0;
}
|
Шаг 4
Пересмотрите класс игровой активности и добавьте переменную экземпляра для вида сетки и адаптера.
1
2
|
private GridView letters;
private LetterAdapter ltrAdapt;
|
Вам также нужно добавить еще одну инструкцию импорта.
1
|
import android.widget.GridView;
|
В методе onCreate
перед строкой, в которой вы вызываете вспомогательный метод playGame
, получите ссылку на представление сетки.
1
|
letters = (GridView)findViewById(R.id.letters);
|
Добавьте текущую реализацию playGame
с помощью следующего фрагмента. В этом фрагменте мы создаем экземпляр адаптера и устанавливаем его в виде сетки.
1
2
|
ltrAdapt=new LetterAdapter(this);
letters.setAdapter(ltrAdapt);
|
Запустите приложение в эмуляторе, и вы должны увидеть пользовательский интерфейс. Однако вы еще не сможете взаимодействовать с кнопками. Именно на этом мы сосредоточимся в третьей и последней части этой серии.
Вывод
Если вы запустите приложение на этом этапе, оно предоставит вам интерфейс игры, но пока не будет реагировать на взаимодействие с пользователем. Мы реализуем оставшуюся функциональность в заключительной части этой серии.
Когда игрок нажимает кнопки с буквами, приложение должно проверить, включена ли выбранная буква в целевое слово, обновить ответ и соответствующим образом подсвечивать. Мы также представим диалог игроку, если он выиграет или проиграет, и мы также добавим кнопку помощи. Наконец, мы добавим базовую навигацию через панель действий.