Главная » Создаем игру Hangman. Часть 2. Пользовательский интерфейс | |
В предыдущем уроке мы начали создавать своими руками популярную игру Hangman. Нами были загружены в проект необходимые изображения, созданы и частично отредактированы нужные layout файлы и классы java. В этом уроке мы продолжим работу над созданием игры и будем настраивать ее пользовательский интерфейс. Работа над игровыми layout будет включать настройку адаптера, для связи кнопок с буквами и появлением частей тела человечка, при выборе пользователем неправильной буквы. Также будет настроено хранение ответов пользователя в xml файле, настройка доступа к этим ответам, и выбор случайного слова с помощью использования java. Игра в законченном состоянии должна выглядеть вот так:
В предыдущем уроке мы добавили к проекту необходимые изображения частей тела человечка и виселицы. В этом уроке мы поместим их в нужные layout файлы. Положение этих изображений должно быть определено их размерами. Одним из способов грамотно подогнать человечка под виселицу (подогнать картинки друг к другу:)) - это воспользоваться Photoshop, сложить в нем все элементы вместе в необходимую картинку и потом по их координатам x и y определить правильное положение частей тела по отношению к виселице и применить их в layout файле. Но если вы используете изображения, данные к уроку, то можно смело использовать все настройки с этого урока, где уже все подогнано нужным образом. Открываем файл activity_game.xml. Внутри LinearLayout, который мы создали ранее, давайте добавим RelativeLayout, в котором будут нужные изображения:
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFFFFFFF"
android:gravity="center"
android:paddingTop="15dp" >
</RelativeLayout>
Теперь внутри RelativeLayout добавляем изображение виселицы:
<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"/>
Далее мы будем позиционировать остальные изображения по отношению к этому, добавленному выше. Строчка:
android:contentDescription="@string/gallows"
у нас подчеркнута красным, потому, что такого строкового ресурса у нас пока не существует. Не обращаем внимания, мы добавим его позже. Далее добавляем голову человечка:
<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 атрибут для изображения головы для того, чтобы иметь возможность обратиться к нему в java коде. Это необходимо, потому, что в зависимости от выбора пользователя части тела будут либо появляться, либо исчезать. Далее добавляем тело бедняги:
<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" />
Пока что все довольно просто. Добавим остальные части тела:
<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" />
<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" />
Теперь давайте решим проблему с красным подчеркиванием в строках contentDescription. Открываем файл strings.xml приложения (он находится в папке values) и добавляем туда следующее:
<string name="gallows">Виселица</string>
<string name="head">Голова</string>
<string name="body">Тело</string>
<string name="arm">Рука</string>
<string name="leg">Нога</string>
После всех этих манипуляций наш файл activity_game.xml имеет вид:
Всякий раз, когда будет запускаться игра, части тела человечка должны быть скрыты. Они будут появляться в том случае, когда пользователь будет выбирать неправильные буквы. Игра будет содержать набор слов для отгадывания, которые мы будем хранить в xml файле. Давайте создадим этот файл. В папке values приложения создаем файл arrays.xml. Создадим в этом файле тестовый массив из слов, добавив следующий код:
<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>
Мы создали массив из слов, которые должен будет отгадывать пользователь. Как видите, слова посвящены миру технологий. Если чувствуете с в себе достаточно сил, можете сделать более сложно - создать несколько массивов слов, отсортированных по категориям, предлагая пользователю перед игрой выбрать, из какой категории он хочет отгадывать слова. Возвращаемся к редактирования файла activity_game.xml. Сразу после RelativeLayout с виселицей и человечком добавим новый LinearLayout:
<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>
Этот layout будет использоваться, как область для ответов. Мы будем хранить каждый символ отгадываемого слова в отдельном TextView, что даст возможность показывать и скрывать разные буквы отдельно, а не слово целиком. Также для этого LinearLayout мы зададим свой id, чтобы программно, с jaca класса, добавлять к этому layout необходимые элементы TextView. Переходим к работе в файле GameActivity. Добавим сюда такие строки импорта:
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:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
}
С помощью команды setContentView мы настроили классу GameActivity вид, заданный в файле activity_game.xml. Теперь нам нужно объявить переменные, которые мы будем использовать. Перед методом onCreate добавляем следующее:
private String[] words;
private Random rand;
private String currWord;
private LinearLayout wordLayout;
private TextView[] charViews;
Набор слов содержится в массиве и приложение использует объект rand для выбора случайного слова из массива в тот момент, когда пользователь запускает игру. Мы устанавливаем связь с текущим словом (currWord), с LinearLayout, который мы создали как область для ответа (wordLayout), и с массивом из элементов TextView, используемых для букв (charViews). В методе onCreate добавим команды для чтения нашего набора слов и помещения их в массив words:
Resources res = getResources();
words = res.getStringArray(R.array.words);
Инициализируем объект rand и строку currWord:
rand = new Random();
currWord = "";
Ссылаемся на LinearLayout, который мы создали как область для ответа:
wordLayout = (LinearLayout)findViewById(R.id.word);
Каждый раз, когда игрок запускает новую игру, будет выполняться некоторое количество процессов. Чтобы держать все организованно, создадим для этого вспомогательный класс. После метода onCreate создадим класс playGame:
private void playGame() {
//Играем новую игру
}
Внутри нового метода начнем с выбора случайного слова с массива:
String newWord = words[rand.nextInt(words.length)];
Метод playGame будет вызываться когда пользователь будет начинать играть опять, после победы или поражения, нужно удостовериться в том, что мы не выберем два раза подряд одно и то же слово. Чтобы избежать этого, добавляем следующее:
while(newWord.equals(currWord)) newWord = words[rand.nextInt(words.length)];
currWord = newWord;
Теперь нам нужно создать для каждой буквы в отгадываемом слове свой TextView. Внутри этого же вспомогательного метода playGame добавим:
charViews = new TextView[currWord.length()];
Далее, убираем всем TextView с wordLayout:
wordLayout.removeAllViews();
Используем цикл for для повторения над каждой буквой ответа, создаем свой TextView для каждой буквы и настраиваем текст в TextView для правильно выбранной буквы:
for (int c = 0; c < currWord.length(); c++) {
charViews[c] = new TextView(this);
charViews[c].setText(""+currWord.charAt(c));
}
Использование метода charAt позволит нам получить доступ к символам по специальному индексу. Внутри того же цикла for настраиваем параметры отображения для TextView и добавления его в LinearLayout:
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);
//Добавляем в layout
wordLayout.addView(charViews[c]);
Мы настроили белый цвет текста и пользователь не будет видеть текст на фоне такого же белого фона. Если игрок правильно угадает букву, то цвет текста поменяется на черный и станет, таким образом, видимым. Будем вызывать созданный выше вспомогательный метод в методе onCreate. Добавьте в метод onCreate строку:
playGame();
Немного позже мы доработаем методы onCreate и playGame. Теперь нам нужно создать область с буквами, на которые будет нажимать игрок, угадывая нужное слово. Откройте файл activity_game.xml и добавьте туда сетку, которая будет содержать буквы:
<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" />
Для связи букв алфавита и кнопок в созданной выше сетке, мы будем использовать адаптер. В сетке мы зададим ряды по 7 кнопок в каждом. Каждая буква должна соответствовать кнопке, добавленной в layout с помощью адаптера. Создаем в папке layout новый файл по имени letter.xml и заполните его следующим кодом:
<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, который обработаем в следующий раз. Создадим в нашем приложении еще один java класс (напомню, у нас их два: MainActivity и GameActivity) по имени LetterAdapter, который будет наследовать класс BaseAdapter. То есть, после создания класса LetterAdapter добавьте наследование: public class LetterAdapter extends BaseAdapter. При наведении на эту строку, в Android Studio появится красный значок (не пугаемся), жмем на него и выбираем первую строку с командой Implement Methods:
Программа сама создала 4 метода, позже мы будем их использовать. Теперь все okey, идем дальше. Добавим к импорту следующее:
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.Button;
Внутри класса объявим следующие переменные:
private String[] letters;
private LayoutInflater letterInf;
Массив letters будет хранить буквы алфавита, а letterInf будет применять letter.xml к каждому элементу в адаптере. После объявленных переменных добавим метод для адаптера:
public LetterAdapter(Context c) {
//Настройка адаптера
}
Внутри конструктора создаем массив из всех букв алфавита от A до Z.
letters=new String[26];
for (int a = 0; a < letters.length; a++) {
letters[a] = "" + (char)(a+'A');
}
Каждый символ представлен как число, поэтому мы можем настроить буквы от A до Z с помощью циклического запуска с нуля и добавления значения символа A к каждому индексу массива. Внутри конструктора также добавьте строку, относящуюся к работе заполнителя layoutInflater:
letterInf = LayoutInflater.from(c);
Теперь, обновим код метода getCount (он один из 4, созданных самой программой, помните?):
@Override
public int getCount() {
return letters.length;
}
Он представляет количество просмотров, один для каждой буквы. Мы не вызываем методы в классе Adapter явно, вместе в приложением. Это делает сама операционная система, которая вызывает необходимые методы, используемые для построения пользовательского интерфейса, в нашем случае это сетка из букв. Обновим другой стандартный метод getView:
@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;
}
Именно этот метод будет выстраивать пользовательский интерфейс, используя адаптер. Здесь мы наполняем letter layout и настраиваем буквы в соответствии с положением в алфавите. Остальные два метода в классе мы оставим без изменений:
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
Давайте еще раз навестим GameActivity.java и добавим переменные для GridView и адаптера.
private GridView letters;
private LetterAdapter ltrAdapt;
Также добавим еще одну строку в импорт:
import android.widget.GridView;
В методе onCreate до вызова метода playGame добавим объявление о GridView:
letters = (GridView)findViewById(R.id.letters);
Добавьте в метод playGame строки о адаптере и настройке с его помощью GridView:
ltrAdapt=new LetterAdapter(this);
letters.setAdapter(ltrAdapt);
На этот урок все. Запускаем приложение в эмуляторе либо на устройстве и мы должны увидеть пользовательский интерфейс нашей будущей игры: Но сейчас кнопочки с буквами еще не работают и мы все еще не может поиграть в игру Hangman. В следующем и последнем уроке мы завершим работу над этой игрой и сможем насладиться игрой в нее:). Оригинал урока. | |
|
Всего комментариев: 0 | |