Статьи

Создать игру Hangman: пользовательский интерфейс

В этой серии мы создаем игру Hangman для платформы Android. В первом уроке мы настроили приложение для представления пользователю двух экранов, а также начали с элементов пользовательского интерфейса, точных изображений и формы. Во втором уроке мы увеличим макет игры.


Создание макета игры потребует использования адаптера для создания буквенных кнопок и размещения частей тела, которые мы будем отображать, когда пользователи выбирают неправильные буквы. Мы также будем хранить ответы игрока в XML, извлекать их и выбирать случайное слово, используя Java.

Чтобы освежить вашу память, так будет выглядеть финальная игра.

Android Палач Игра Финал

В предыдущем уроке мы создали изображения для виселицы и для шести частей тела. Мы разместим их внутри макета игры в этом уроке. Позиции, которые вы устанавливаете для этих элементов, должны определяться элементами изображения, которые вы используете. Один из подходов состоит в том, чтобы импортировать изображения в редактор изображений, такой как 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>

Внутри созданного вами относительного макета начните с добавления изображения виселицы, как показано ниже.

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>

Вернитесь к файлу макета и переключитесь на вкладку Графический макет, чтобы увидеть результат нашей работы. Отрегулируйте верхний и левый отступы каждого изображения, чтобы отрегулировать их положение при необходимости.

Android Hangman Game Layout

Всякий раз, когда запускается новая игра, части тела должны быть скрыты. Каждая часть тела раскрывается, если игрок выбирает букву, которой нет в целевом слове.


В игре будет использоваться набор предопределенных слов, которые мы будем хранить в XML. В папке значений ресурсов проекта добавьте новый файл и назовите его arrays.xml.

Файл игровых массивов Android Hangman

Переключитесь на вкладку 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>
Для этого урока мы используем один массив слов, связанных с вычислениями. Однако, чтобы сделать игру более интересной, вы можете работать с различными категориями, каждая из которых содержит слова, относящиеся к определенной теме. Когда пользователь нажимает кнопку воспроизведения, вы можете предложить ему выбрать категорию и прочитать соответствующий набор слов.

Вернувшись в файл макета активности вашей игры, добавьте линейный макет сразу после относительного макета, который мы добавили для области виселицы. Линейный макет зарезервирован для области ответа.

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 линейного макета в коде, чтобы добавлять текстовые представления каждый раз, когда выбирается новое слово.

Откройте класс 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);
}

Мы устанавливаем вид содержимого для файла макета, который мы создали минуту назад.

Прежде чем двигаться дальше, нам нужно объявить несколько переменных экземпляра. Добавьте объявление непосредственно перед методом 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);

Ряд задач необходимо выполнять каждый раз, когда игрок запускает новую игру. Давайте создадим вспомогательный метод, чтобы все было организовано. После метода 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;

Следующим шагом является создание одного текстового представления для каждой буквы целевого слова. По-прежнему в нашем вспомогательном методе создайте новый массив для хранения текстовых представлений для букв целевого слова.

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 и вспомогательный метод чуть позже.


Следующим шагом является создание области для игрока, чтобы выбрать буквы угадать. Пересмотрите игровой макет игры и добавьте следующий вид сетки, чтобы удерживать кнопки с буквами. Вставьте вид сетки сразу после линейного макета, который мы добавили для слова ответа.

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 .

Каждая буква будет кнопкой, добавленной в макет с помощью адаптера. Добавьте новый файл в папку макета проекта, назовите его 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 который будем реализовывать в следующий раз. Если вы расширяете приложение для работы с разными плотностями экрана, вы можете соответствующим образом настроить атрибут высоты.

Добавьте новый класс в пакет src вашего проекта, назовите его LetterAdapter и выберите android.widget.BaseAdapter качестве суперкласса.

Класс игрового адаптера Android Hangman

Как вы увидите, класс адаптера включает в себя серию стандартных методов, которые мы будем реализовывать. Добавьте следующие операторы импорта в новый класс.

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;
}

Пересмотрите класс игровой активности и добавьте переменную экземпляра для вида сетки и адаптера.

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

Запустите приложение в эмуляторе, и вы должны увидеть пользовательский интерфейс. Однако вы еще не сможете взаимодействовать с кнопками. Именно на этом мы сосредоточимся в третьей и последней части этой серии.

Android Hangman Game

Если вы запустите приложение на этом этапе, оно предоставит вам интерфейс игры, но пока не будет реагировать на взаимодействие с пользователем. Мы реализуем оставшуюся функциональность в заключительной части этой серии.

Когда игрок нажимает кнопки с буквами, приложение должно проверить, включена ли выбранная буква в целевое слово, обновить ответ и соответствующим образом подсвечивать. Мы также представим диалог игроку, если он выиграет или проиграет, и мы также добавим кнопку помощи. Наконец, мы добавим базовую навигацию через панель действий.