Рейтинг
0.00
голосов:
0
avatar

Архив: статьи  

GymPy — лог-программа для любителей потягать железо

Из особенностей: PyGTK (hildonize for Nokia N900), само-модифицируемый код, что бы не возиться с конфиг-файлами.

( Читать дальше )

Битва смартфонов, 1 квартал 2010: полный обзор рынка

Я предсказывал, что 2010 год принесет на рынок смартфонов большие перемены. Я предсказывал, что ситуация будет весьма изменчивой. Я предсказывал, что в уже начавшуюся гонку вступят новые игроки. И еще я предсказывал, что битва будет жестокой. Ну что ж, жертвы уже есть: первой стал Palm. Я предполагал, что они не переживут этот год, и то, что в апреле эта марка была продана, демонстрирует, как накаляется ситуация. Итак, я дал свой прогноз на текущий год. Теперь мы получили данные от ведущих производителей и можем составить отчет о ситуации на рынке смартфонов в 1 квартале. Я использовал средние цифры суммарных продаж смартфонов за квартал по отраслевым аналитическим отчетам, получив общий объем продаж в 54,5 млн. штук.

( Читать дальше )

  • +1
  • 10 августа 2010, 13:10
  • Eric
  • 7

Отправляем/получаем смс в Android Emulator

 

Отправляем/получаем смс в Android Emulator

 

Как отправить смс в эмулятор?

Самый простой способ это сделать - использовать Eclipse с плагином ADT.
1. Запускаем Eclipse.
2. Переходим в режим DDMS, для этого открываем в меню Window->Open Perspective->Other… и там выбираем DDMS

 

ddms-perspective

 

3. Запускаем эмулятор:
- либо из папки где у вас установлен сдк, например, путь_к_сдк-tools-emulator
- или запустив свой проект в эмуляторе

4. В Eclipse, в режиме DDMS выбираем эмулятор из списка (нужно кликнуть по нему):

ddms-emul-select


5. Чуть ниже выбора эмулятора, находится окно отправки смс.

ddms-sms-send

здесь указываем номер звонящего (от кого придет смс), выбираем режим SMS и указываем текст сообщения. Что бы отправить нажимаем Send.

6. Открываем окно с эмулятором и видим наше отправленное смс.

emulator-received-sms

Как отправить смс из эмулятора (или программы) и получить его в другом эмуляторе?

1. Для этого нам понадобится еще один эмулятор. Запускаем его: путь_к_сдк-tools-emulator.exe. Таким образом у нас должно быть открыто два эмулятора и Eclipse.

2. В Eclipse должны быть видны оба эмулятора:

ddms-device-select

Цифры рядом с emulator - это порт на котором работает эмулятор, он же и будет номером телефона.

3. Например мы хотим отправить смс из emulator-5554 на emulator-5556. Для этого открываем emulator-5554 и запускаем программу для отправки смс - Messaging.

emulator-5554-create-sms

4. Выбираем New message и создаем новую смс. В качестве Incoming number указываем номер эмулятора которому хотим отправит, в нашем случае 5556. И указываем текст сообщения.

emulator-5554-send-sms

Далее нажимаем кнопку отправить.

6. Открываем эмулятор 5556 и видим наше сообщение:

emulator-5556-recieved-sms

Таким образом, мы видим, что пришло сообщение от эмулятора 5554 с текстом: Hello from emulator 5554 to 5556

 

Автор: vovkab

 

Источник: Android Team

 

 

Подключение к датчикам в Android

 

Подключение к датчикам в Android

Дата:  26.02.2010
Наблюдаем за окружающей средой дистанционно

Источник: IBM developerWorks Россия
Автор: Фрэнк Эйблсон, проектировщик ПО, Независимый разработчик

Описание:  В этой статье мы научимся взаимодействовать через Android с различными датчиками в целях мониторинга окружающей среды.

Уровень сложности:  средний



Содержание:



Введение

Платформа Android идеально подходит для создания инновационных приложений с использованием аппаратных датчиков, особенно Java-программистами. Мы рассмотрим некоторые варианты интерфейсов для приложений Android, включая использование подсистемы датчиков и записи аудиофрагментов.

Какое приложение лучше построить, чтобы задействовать аппаратные возможности устройства на платформе Android? Подойдет любое, в котором требуются электронные глаза и уши. На ум приходит электронная няня, система безопасности или даже сейсмограф. Хотя с метафизической точки зрения одновременно присутствовать в двух местах невозможно, Android может предложить некоторые практические способы преодоления этого ограничения. В рамках этой статьи под Android-устройством понимается не просто "мобильный телефон", а установленное в определенном месте устройство с беспроводным соединением с сетью, таким как EDGE или Wi-Fi. Загрузите исходный код примера приложения для этой статьи.


Сенсорные возможности Android

Один из приятных аспектов работы с платформой Android заключается в возможности получить доступ к некоторым полезным компонентам самого устройства. До сих пор разработчиков мобильных устройств разочаровывала невозможность доступа к их внутреннему оборудованию. Хотя между вами и металлом все же остается прослойка Java-среды Android, команда разработчиков Android вывела многие возможности аппаратуры на поверхность. А так как это платформа с открытым исходным кодом, можно засучить рукава и написать собственный код для решения своих задач.

 

Загрузите SDK Android, если вы этого еще не сделали. Рекомендуем также изучить содержимое пакета android.hardware и следить за примерами этой статьи. Пакет android.media package содержит классы, которые предоставляют разработчикам новые полезные функции.

 

Ниже описаны некоторые аппаратно-ориентированные функции, содержащиеся в SDK Android.


Таблица 1. Аппаратно-ориентированные функции SDK Android

Функция Описание
android.hardware.Camera
Класс, позволяющий приложениям взаимодействовать с видеокамерой в целях фотосъемки, записи изображений с экрана предварительного просмотра или для изменения параметров настройки.
android.hardware.SensorManager
Класс, обеспечивающий доступ к внутренним датчикам платформы Android. Не каждое устройство на платформе Android поддерживает все датчики из
SensorManager
, однако интересно обдумать такие возможности. (Краткое описание имеющихся датчиков приведено ниже.)
android.hardware.SensorListener
Интерфейс реализован с помощью класса, который используется для ввода значений датчиков по мере их изменения в режиме реального времени. Приложение реализует этот интерфейс для мониторинга одного или нескольких имеющихся аппаратных датчиков. Например, код из этой статьи содержит класс, который использует этот интерфейс для контроля ориентации устройства и показаний встроенного акселерометра.
android.media.MediaRecorder
Класс, используемый для записи медиафрагментов, который можно применять для записи звуков в определенном месте (например, в детской комнате). Можно также анализировать аудиофрагменты для контроля доступа в помещение и в целях безопасности. Например, можно открывать дверь собственным голосом в обычное время своего прихода, вместо того, чтобы обращаться к консьержу за ключом.
android.FaceDetector
Класс, который позволяет распознавать лицо человека по хранящейся в памяти фотографии. Ничто не удостоверяет личность лучше, чем лицо. Если использовать его для блокировки устройства, вам больше не придется запоминать пароли – достаточно биометрических возможностей мобильного телефона.
android.os.* Пакет, содержащий несколько полезных классов для взаимодействия с операционной средой, включая управление питанием, поиск файлов, обработчик и классы для обмена сообщениями. Как и многие другие портативные устройства, телефоны на базе Android могут потреблять достаточно много электроэнергии. Обеспечение "бодрствования" устройства в нужный момент, чтобы проконтролировать нужное событие, - важный аспект проектирования, заслуживающий особого внимания.
java.util.Date

java.util.Timer

java.util.TimerTask
При измерении событий реального мира часто имеют значение дата и время. Например, класс
java.util.Date
позволяет получить метку времени, когда происходит какое-либо событие или возникает определенное состояние.
java.util.Timer
и
java.util.TimerTask
можно использовать соответственно для выполнения периодических действий по расписанию или разового действия в определенный момент времени.

Android.hardware.SensorManager содержит несколько констант, которые характеризуют различные аспекты системы датчиков Android, в том числе:

Тип датчика
Ориентация, акселерометр, свет, магнитное поле, близость, температура и т.д.
Частота измерений
Максимальная, для игр, обычная, для пользовательского интерфейса. Когда приложение запрашивает конкретное значение частоты отсчетов, с точки зрения сенсорной подсистемы это лишь рекомендация. Никакой гарантии, что измерения будут производиться с указанной частотой, нет.
Точность
Высокая, низкая, средняя, ненадежные данные.

Центром сенсорных приложений служит интерфейс

SensorListener
. Он включает в себя два необходимых метода:

  • Метод
    onSensorChanged(int sensor,float values[])
    вызывается всякий раз, когда изменяется значение датчика. Этот метод вызывается только для датчиков, контролируемых данным приложением (подробнее об этом ниже). В число аргументов метода входит целое, которое указывает, что значение датчика изменилось, и массив значений с плавающей запятой, отражающих собственно значение датчика. Некоторые датчики выдают только одно значение данных, тогда как другие предоставляют три значения с плавающей запятой. Датчики ориентации и акселерометр дают по три значения данных каждый.
  • Метод
    onAccuracyChanged(int sensor,int accuracy)
    вызывается при изменении точности показаний датчика. Аргументами служат два целых числа: одно указывает датчик, а другое соответствует новому значению точности этого датчика.

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

registerListener
класса
SensorManager
. Пример кода для этой статьи демонстрирует, как приложение регистрируется и отменяет регистрацию с помощью
SensorListener
.

 

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


Пример работы с датчиком

Этот пример приложения просто следит за изменениями значений датчиков ориентации и акселерометра (исходный код приведен в разделе Загрузки). При изменении значений датчиков они отображаются на экране в виджете

TextView
. Рисунок 1 демонстрирует приложение в действии.


Рисунок 1. Мониторинг ускорения и ориентации
Мониторинг ускорения и ориентации

Приложение создано в среде Eclipse с плагином Android Developer Tools. (Более подробную информацию о разработке Android-приложений с помощью Eclipse см. в разделе Ресурсы.) В листинге 1 приведен код этого приложения.


Листинг 1. IBMEyes.java


package com.msi.ibm.eyes;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.SensorListener;
public class IBMEyes extends Activity implements SensorListener {
    final String tag = "IBMEyes";
    SensorManager sm = null;
    TextView xViewA = null;
    TextView yViewA = null;
    TextView zViewA = null;
    TextView xViewO = null;
    TextView yViewO = null;
    TextView zViewO = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       // get reference to SensorManager
        sm = (SensorManager) getSystemService(SENSOR_SERVICE);
        setContentView(R.layout.main);
        xViewA = (TextView) findViewById(R.id.xbox);
        yViewA = (TextView) findViewById(R.id.ybox);
        zViewA = (TextView) findViewById(R.id.zbox);
        xViewO = (TextView) findViewById(R.id.xboxo);
        yViewO = (TextView) findViewById(R.id.yboxo);
        zViewO = (TextView) findViewById(R.id.zboxo);
    }
    public void onSensorChanged(int sensor, float[] values) {
        synchronized (this) {
            Log.d(tag, "onSensorChanged: " + sensor + ", x: " + 
values[0] + ", y: " + values[1] + ", z: " + values[2]);
            if (sensor == SensorManager.SENSOR_ORIENTATION) {
                xViewO.setText("Orientation X: " + values[0]);
                yViewO.setText("Orientation Y: " + values[1]);
                zViewO.setText("Orientation Z: " + values[2]);
            }
            if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
                xViewA.setText("Accel X: " + values[0]);
                yViewA.setText("Accel Y: " + values[1]);
                zViewA.setText("Accel Z: " + values[2]);
            }            
        }
    }

    public void onAccuracyChanged(int sensor, int accuracy) {
    	Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
    }
    @Override
    protected void onResume() {
        super.onResume();
      // register this class as a listener for the orientation and accelerometer sensors
        sm.registerListener(this, 
                SensorManager.SENSOR_ORIENTATION |SensorManager.SENSOR_ACCELEROMETER,
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onStop() {
        // unregister listener
        sm.unregisterListener(this);
        super.onStop();
    }    
}

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

 

Метод действия

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

 

Метод

onResume()
, используя ссылку на
SensorManager
, регистрируется на прием обновлений данных датчика посредством метода
registerListener
:

  • Первый параметр представляет собой экземпляр класса, реализующий интерфейс
    SensorListener
    .
  • Второй параметр - это битовая маска желаемых датчиков. В данном случае приложение запрашивает данные из
    SENSOR_ORIENTATION
    и
    SENSOR_ACCELEROMETER
    .
  • Третий параметр указывает системе, как быстро должны обновляться значения датчиков для данного приложения.

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

unregisterListener
класса
SensorManager
. Единственным параметром является экземпляр
SensorListener
.

 

При вызове обоих методов

registerListener
и
unregisterListener
приложение использует ключевое слово
this
. Обратите внимание на ключевое слово
implements
в определении класса, которое декларирует, что этот класс реализует интерфейс
SensorListener
. Вот почему в
registerListener
и
unregisterListener
передается this.

 

SensorListener
должен реализовать два метода:
onSensorChange
и
onAccuracyChanged
. Наш пример приложения не заботится о точности датчиков, а лишь вводит текущие значения X, Y и Z этих датчиков. Метод
onAccuracyChanged
, по существу, ничего не делает; он просто добавляет запись в журнал при каждом вызове.

 

Кажется, что метод

onSensorChanged
вызывается постоянно, так как акселерометр и датчик ориентации передают данные быстро. Чтобы определить, какой датчик передает данные, мы смотрим на первый параметр. Когда передающий датчик выявлен, соответствующие элементы пользовательского интерфейса обновляются данными, содержащимися в массиве значений с плавающей запятой, принятом в качестве второго аргумента метода. Хотя пример просто отображает эти значения, в более сложных приложениях они могут анализироваться, сравниваться с предыдущими значениями или использоваться в некоем алгоритме распознавания образов для определения того, что делает пользователь (или что происходит во внешней среде).

 

Теперь, когда мы рассмотрели подсистему датчиков, в следующем разделе приведем пример кода, который записывает звук на телефон Android. Этот пример работает на устройстве для разработчиков Dev1.


Применение MediaRecorder

Пакет Android.media содержит классы для взаимодействия с мультимедийной подсистемой. Класс

android.media.MediaRecorder
используется для записи медиафрагментов, включая аудио и видео.
MediaRecorder
действует как конечный автомат. Вы задаете различные параметры, такие как устройство-источник и формат. После установки запись может выполняться как угодно долго, пока не будет остановлена.

 

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


Листинг 2. Запись аудиофрагмента


MediaRecorder mrec ;
File audiofile = null;
private static final String TAG="SoundRecordingDemo";
protected void startRecording() throws IOException 
{
   mrec.setAudioSource(MediaRecorder.AudioSource.MIC);
   mrec.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
   mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
   if (mSampleFile == null) 
   {
       File sampleDir = Environment.getExternalStorageDirectory();
       try 
       { 
          audiofile = File.createTempFile("ibm", ".3gp", sampleDir);
       }
       catch (IOException e) 
       {
           Log.e(TAG,"sdcard access error");
           return;
       }
   }
   mrec.setOutputFile(audiofile.getAbsolutePath());
   mrec.prepare();
   mrec.start();
}
protected void stopRecording() 
{
   mrec.stop();
   mrec.release();
   processaudiofile(audiofile.getAbsolutePath());
}
protected void processaudiofile() 
{
   ContentValues values = new ContentValues(3);
   long current = System.currentTimeMillis();
   values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
   values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
   values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gpp");
   values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
   ContentResolver contentResolver = getContentResolver();

   Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
   Uri newUri = contentResolver.insert(base, values);

   sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}

В методе

startRecording
создается и инициализируется экземпляр
MediaRecorder
.

  • В качестве источника данных выбирается микрофон (
    MIC
    ).
  • Выходной формат устанавливается в 3GPP (файлы *.3gp) - медиаформат, ориентированный на мобильные устройства.
  • Кодер настроен на
    AMR_NB
    - аудиоформат с частотой дискретизации 8 кГц. NB означает узкую полосу частот. Различные форматы данных и имеющиеся кодеры рассматриваются в документации SDK.

Аудиофайл хранится не во внутренней памяти, а на отдельной карте.

External.getExternalStorageDirectory()
возвращает имена карты памяти и временного файла, созданного в этом каталоге. Затем этот файл связывается с экземпляром
MediaRecorder
, обращаясь к методу
setOutputFile
. Аудиоданные будут храниться в этом файле.

 

Вызов метода

prepare
завершает инициализацию
MediaRecorder
. Когда нужно начать процесс записи, вызывается метод
start
. Запись в файл на карте памяти ведется до тех пор, пока не будет вызван метод
stop
. Этот метод освобождает ресурсы, выделенные экземпляру
MediaRecorder
.

 

Когда аудиофрагмент записан, можно выполнить несколько действий:

  • Добавить аудиозапись в медиатеку на устройстве.
  • Выполнить некоторые шаги по распознаванию звука:
    • Не плач ли это ребенка?
    • Это голос хозяина, и нужно разблокировать телефон?
    • Или это фраза "Сезам, откройся", которая отпирает потайную дверь?
  • Автоматически загрузить звуковой файл в сетевую папку для обработки.

В примере кода метод

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

 

И последнее замечание об этом фрагменте кода: сразу после создания он не будет записывать аудио. Вы увидите созданный файл, но не услышите звука. Нужно добавить разрешение в файл AndroidManifest.xml:

 
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
 

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


Android как измерительная платформа

Платформа Android располагает широкими возможностями по измерению параметров окружающей среды. При таком многообразии входных параметров и датчиков в сочетании с мощными вычислительными и сетевыми функциями Android становится привлекательной платформой для создания систем взаимодействия с реальным миром. На рисунке 2 упрощенно показано соотношение между входами, логикой приложений и методами уведомления или выходами.


Рисунок 2. Блок-схема Android-ориентированной системы датчиков
Блок-схема построенной на Android системы датчиков

Это очень гибкая архитектура; логику приложения можно распределять между локальным Android-устройством и серверными ресурсами, которые могут подключаться к более крупным базам данных и вычислительным мощностям. Например, аудиотрек, записанный на локальном Android-устройстве, можно отправить методом

POST
на Web-сервер, где данные сопоставляются с базой данных образцов голоса. Ясно, что это только самое поверхностное знакомство с возможностями платформы. Надеюсь, что вы заинтересовались и начнете копать глубже, чтобы использовать платформу Android не только для целей мобильной телефонии.


Заключение

Из этой статьи вы получили первое представление о работе с датчиками Android. Приведенные примеры приложений определяют ориентацию и ускорение, а также взаимодействуют со средствами звукозаписи с использованием класса

MediaRecorder
. Android - гибкая, удобная платформа для создания систем взаимодействия с реальным миром. Сфера влияния Android очень быстро расширяется, и эта платформа осваивает все новые области применения. Не упускайте ее из вида.



Загрузка

Описание Имя Размер
Исходный код примера Eyes os-android-sensorEyes.zip 28 КБ
Исходный код примера IBMAudio os-android-sensorIBMAudio.zip 33 КБ

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Об авторе

Когда Фрэнк Эйблсон (Frank Ableson) закончил карьеру баскетболиста в команде своего колледжа, не заключив многолетнего контракта с «Лос-Анджедес Лейкерс», он занялся разработкой компьютерных программ. Он любит решать сложные задачи, особенно из области связи и интерфейсов с аппаратурой. Свободное время Фрэнк проводит со своей женой Никки и детьми. С ним можно связаться по адресу: frank@cfgsolutions.com.



Введение в разработку для платформы Android

 

Введение в разработку для платформы Android

Дата:  26.02.2010
Платформа с открытым исходным кодом для специализированных устройств

Источник: IBM developerWorks Россия
Автор: Фрэнк Эйблсон, проектировщик ПО, Независимый разработчик

Описание:  Android — это полноценная операционная среда, основанная на ядре Linux® V2.6. На начальном этапе областью распространения Android был сегмент мобильных телефонов, включая смартфоны и более дешевые раскладные устройства. Однако полный спектр вычислительных сервисов и богатые функциональные возможности Android позволяют выйти за рамки рынка мобильных телефонов. Android может быть полезен для других платформ и приложений. Настоящая статья дает введение в платформу Android и учит программированию простых приложений для этой платформы.

Уровень сложности:  простой



Содержание:



Введение

BlackBerry и iPhone, две удобные и массовые мобильные платформы, олицетворяют собой противоположные концы спектра мобильных устройств. BlackBerry незаменим для корпоративного пользователя. В классе же потребительских устройств у iPhone мало конкурентов в части удобства обращения и «крутизны». Android, молодая и еще не утвердившаяся платформа, способна проявить себя на обоих концах спектра мобильных телефонов и, возможно, даже стать мостом через пропасть между работой и игрой.

 

Сегодня многие сетевые или поддерживающие сеть устройства работают на том или ином варианте ядра Linux. Это солидная платформа: недорогая в развертывании и поддержке и хорошо принимаемая благодаря удобному подходу к развертыванию. Пользовательский интерфейс таких устройств зачастую основан на HTML и может просматриваться с помощью браузера для РС или Mac. Но не всяким устройством нужно управлять посредством компьютера. Рассмотрим обычные бытовые приборы, такие, как плита, микроволновая печь или хлебопечка. Что если у ваших бытовых приборов была бы ОС Android и цветной сенсорный экран? Будь у плиты интерфейс Android, автор и сам, возможно, смог бы что-нибудь приготовить.

 

Из этой стати вы узнаете о платформе Android и о том, как ее можно использовать для мобильных и стационарных приложений. Мы установим SDK Android и создадим простую программу. Загрузите исходный код примера приложения для этой статьи.


Краткая история Android

Платформа Android является продуктом группы Open Handset Alliance, ставящей себе целью создание более совершенного мобильного телефона. Эта группа во главе с Google включает операторов мобильных сетей, производителей телефонов и компонентов, разработчиков программных решений и поставщиков услуг, а также маркетинговые компании. С точки зрения разработки программного обеспечения Android находится в самом центре мира открытого ПО.

 

Первым выпущенным на рынок телефоном на платформе Android стало устройство G1 производства HTC, которое распространяла T-Mobile. Это устройство появилось почти через год после того, как о нем пошли слухи, и единственным инструментом разработки программ для него были постепенно совершенствуемые последовательные выпуски SDK. Накануне выпуска G1 команда Android представила SDK v1.0, и начали появляться приложения для новой платформы.

 

Чтобы стимулировать инновации, Google спонсировала два «Конкурса разработчиков для Android», победители которых получили миллионы долларов. Через несколько месяцев после выхода G1 открылся сайт Android Market, откуда пользователи могли загружать приложения прямо в свой телефон. Всего за полтора года новая мобильная платформа вышла на арену.


Платформа Android

По широте возможностей платформа Android не уступает операционным системам настольных ПК. Это многоуровневая среда на основе ядра Linux с богатыми функциональными возможностями. В подсистему пользовательского интерфейса входят:

  • Окна
  • Представления
  • Виджеты для отображения общих элементов, таких как редактируемые поля, списки и развертывающиеся списки.

Android содержит встраиваемый браузер на базе WebKit - того же механизма с открытым исходным кодом, который лежит в основе браузера Safari мобильного телефона iPhone.

 

Android обладает широким спектром возможностей подключения, охватывающим Wi-Fi, Bluetooth и протоколы передачи данных через сотовую сеть (GPRS, EDGE, 3G и др.). Популярным приемом в приложениях для Android является ссылка на Google Maps для отображения адреса непосредственно в приложении.В стек программного обеспечения Android входит и поддержка сервисов, основанных на определении местоположения (например, GPS), и акселерометров, хотя не все устройства на этой платформе оснащены необходимым оборудованием. Есть также поддержка видеокамеры.

 

Исторически двумя областями, где мобильные приложения отставали от своих настольных собратьев, были графика/мультимедиа и способы хранения данных. Android решает проблему графики благодаря встроенной поддержке 2-D и 3-D графики, включая библиотеку OpenGL. Задача хранения данных упрощается благодаря наличию в платформе Android популярной базы данных с открытым исходным кодом SQLite. На рисунке 1 показана упрощенная схема уровней программного обеспечения Android.


Рисунок 1. Уровни программного обеспечения Android
Уровни программного обеспечения Android


Архитектура приложений

Как уже говорилось, Android работает поверх ядра Linux. Android-приложения пишутся на языке программирования Java и выполняются в виртуальной машине (VM). Важно отметить, что виртуальная машина – это не JVM, как можно было бы ожидать, а открытая технология Dalvik Virtual Machine. Каждое приложение Android запускается внутри экземпляра Dalvik VM, который, в свою очередь заключен в пределах управляемого ядром Linux процесса, как показано на рисунке 2.


Рисунок 2. Dalvik VM
Dalvik VM

Android-приложение содержит элементы одного или нескольких перечисленных ниже типов:

Действия (Activities)
Приложение с графическим интерфейсом реализуется с помощью действия. Когда пользователь выбирает приложение на главном экране или экране пуска приложений, он вызывает действие.
Сервисы (Services)
Сервисы применяются для приложений, которые работают в течение длительного времени, таких как сетевой монитор или проверка обновлений.
Источники данных (Content providers)
Источник данных можно представить себе как сервер баз данных. Его задача - управление доступом к хранящимся данным, таким как база данных SQLite. Если приложение совсем простое, источник данных создавать не обязательно. Если вы пишете более сложное приложение или приложение, в котором к данным обращается несколько действий или приложений, источник данных служит средством организации доступа к вашей информации.
Приемники (Broadcast receivers)
Android-приложение может запускаться для обработки элемента данных или реагирования на события, например, на получение текстового сообщения.

Приложение для Android развертывается на устройстве вместе с файлом AndroidManifest.xml. Этот файл содержит необходимую информацию о конфигурации, которая позволяет правильно установить приложение на устройстве. Он включает также необходимые имена классов и типы событий, которые может обрабатывать приложение, и разрешения, требуемые для его работы. Так, если приложению нужен доступ к сети – например, чтобы загрузить файл, - соответствующее разрешение должно быть явно указано в файле манифеста. Это конкретное разрешение могут иметь многие приложения. Такая защита путем декларирования помогает уменьшить вероятность повреждения устройства по вине некорректно написанного приложения.

В следующем разделе рассматривается среда разработки, необходимая для создания Android-приложений.


Необходимые инструменты

Самый простой способ приступить к разработке приложений для Android - это загрузить SDK Android и Eclipse IDE (см. Ресурсы). Разработку Android-приложений можно вести на платформах Microsoft® Windows®, Mac OS X или Linux.

 

В этой статье предполагается, что вы используете Eclipse IDE и плагин Android Developer Tools для Eclipse. Android-приложения пишутся на языке Java, но компилируются и выполняются в Dalvik VM (не в виртуальной машине Java). Кодирование на языке Java в рамках Eclipse – интуитивно понятный процесс; Eclipse предоставляет богатую среду Java, включая контекстно-зависимую справку и подсказки к коду. Когда ваш Java-код будет безошибочно скомпилирован, Android Developer Tools сам позаботится о том, чтобы приложение был надлежащим образом упаковано, в том числе снабдит его файлом AndroidManifest.xml.

 

Android-приложение можно написать и без Eclipse и плагина Android Developer Tools, но для этого нужно хорошо разбираться в Android SDK.

 

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

android.jar
Файл архива Java, содержащий все классы SDK Android, необходимые для создания приложений.
documention.html и каталог docs
Документация SDK предоставляется локально и через Интернет. В основном она выполнена в формате Javadocs, что позволяет легко ориентироваться во множестве пакетов SDK. Документация включает также общее руководство по разработке и ссылки на широкое сообщество программистов Android.
Каталог с примерами
Подкаталог с примерами содержит полный исходный код различных приложений, в том числе ApiDemo, который демонстрирует многие API. Примеры приложений – отличная отправная точка для изучения разработки Android-приложений.
Каталог инструментов
Содержит все инструменты командной строки для создания Android-приложений. Наиболее часто используемый и полезный инструмент – это утилита
adb
(Android Debug Bridge).
usb_driver
Каталог, содержащий все необходимые драйверы для подключения среды разработки к поддерживающим Android устройствам, таким, как G1 или разблокированный телефон для разработки Android Dev 1. Эти файлы необходимы только разработчикам, использующим платформу Windows.

Android-приложения могут работать как на реальном устройстве, так и на эмуляторе Android, который прилагается к SDK Android. На рисунке 3 показан главный экран эмулятора Android.


Рисунок 3. Эмулятор Android
Эмулятор Android

Отладочный мост Android

Утилита

adb
поддерживает несколько дополнительных аргументов командной строки, которые обеспечивают мощные функции, такие как копирование файлов в устройство и из него. Аргумент оболочки командной строки позволяет подключаться к самому телефону и подавать простые команды оболочки. Рисунок 4 иллюстрирует команду оболочки
adb
, подаваемую реальному устройству, подключенному к ноутбуку под Windows с помощью кабеля USB.


Рисунок 4. Применение команды оболочки
adb

Применение команды оболочки adb

В рамках этой консоли можно:

  • Отображать конфигурацию сети с несколькими сетевыми соединениями. Обратите внимание на наличие нескольких сетевых соединений:
    • lo
      - это локальное (петлевое) соединение.
    • tiwlan0
      - это соединение WiFi с адресом, предоставленным локальным сервером DHCP.
  • Отображать значение переменной среды
    PATH
    .
  • Выполнять команду
    su
    , чтобы стать суперпользователем.
  • Переходить в каталог /data/app, где хранятся пользовательские приложения.
  • Печатать листинг каталогов, в котором вы увидите одно приложение. Файлы приложений Android на самом деле представляют собой архивы, которые можно просматривать с помощью WinZip или эквивалентной программы. Эти файлы имеют расширение apk.
  • Подавать команду ping, чтобы проверить доступность Google.com.

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

 

В следующем разделе мы создадим простое приложение для Android.


Создание простого приложения

Этот раздел содержит краткий обзор процесса создания Android-приложения. Наш пример приложения предельно прост: это несколько видоизмененное приложение «Hello Android». Мы добавим незначительные изменения, чтобы сделать цвет фона экрана белым – тогда телефон можно будет использовать в качестве фонарика. Не очень оригинально, но как пример полезно. Загрузите весь исходный код.

 

Чтобы создать приложение в Eclipse, выберите File > New > Android project, что приведет к запуску мастера нового проекта Android (рисунок 5).


Рисунок 5. Мастер нового проекта Android
Мастер нового проекта Android

Теперь создадим простое приложение с одним действием, а также макет пользовательского интерфейса, который будет храниться в папке main.xml. Макет содержит текстовый элемент, который мы заменим на Android FlashLight (фонарик Android). Этот простой макет приведен в листинге 1.


Листинг 1. Макет Flashlight


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/all_white">
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" android:textColor="@color/all_black" 
   android:gravity="center_horizontal"/>
</LinearLayout>

Создайте пару ресурсов цвета в strings.xml (листинг 2).


Листинг 2. Цвет в strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Android FlashLight</string>
    <string name="app_name">FlashLight</string>
    <color name="all_white">#FFFFFF</color>
    <color name="all_black">#000000</color>
</resources>

Цвет фона основного экрана определяется как

all_white
. В файле strings.xml видно, что
all_white
соответствует значению триплета RGB #FFFFFF, что означает чисто белый цвет.

 

Макет содержит одно поле

TextView
, которое на самом деле - просто нередактируемый фрагмент статического текста. Текст будет черным и отцентрованным по горизонтали с помощью атрибута
gravity
.

 

Приложение содержит исходный файл Java с именем FlashLight.java, как показано в листинге 3.


Листинг 3. Flashlight.java


package com.msi.flashlight;
import android.app.Activity;
import android.os.Bundle;
public class FlashLight extends Activity {
    /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Код создается непосредственно в мастере новых проектов:

  • Это часть пакета Java с именем com.msi.flashlight.
  • Он импортирует два класса:
    • класс действия
    • класс пакета
  • При инициировании этого действия вызывается метод
    onCreate
    , который передается в
    savedInstanceState
    . Не сомневайтесь по поводу использования этого пакета для наших целей; он применяется, когда действие приостановлено, а затем возобновляется.
  • Метод
    onCreate
    переопределяет метод класса действия с таким же именем. Он обращается к методу суперкласса
    onCreate
    .
  • Обращение к
    setContentView()
    относится к макету UI, определенному в файле main.xml. Содержимое main.xml и strings.xml, автоматически отображается на константы, определенные в исходном файле R.java. Никогда не редактируйте этот файл напрямую, так как он изменяется после каждой сборки.

Запуск приложения приводит к отображению белого экрана с черным текстом (рисунок 6).


Рисунок 6. Белый экран фонарика
Белый экран фонарика

Настройка файла AndroidManifest.xml для приложения FlashLight показана в листинге 4.


Листинг 4. AndroidManifest.xml для приложения FlashLight


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.msi.flashlight"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".FlashLight"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Этот файл создается автоматически плагином Android Developer Tools для Eclipse. Вам не нужно ничего делать.

 

Разумеется, это не очень мощное приложение. Но оно может пригодиться, если вы хотите что-нибудь почитать, не нарушая сна супруги, или если погас свет и надо найти путь к предохранителям в подвале.


Заключение

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



Загрузка

        Исходный код FlashLight (zip, 24 Кб)

Ресурсы

Научиться

  • Оригинал статьи (EN).
     
  • Open Handset Alliance - группа из 47 компаний, специализирующихся на ИТ и мобильной связи, которые поставили перед собой цель ускорить внедрение инноваций в сфере мобильной телефонии и предложить потребителям еще более широкие возможности, меньшие цены и лучшее качество связи. Сообща они разработали Android, первую полную, открытую и бесплатную платформу мобильной телефонии.(EN)
     
  • Сайт для разработчиков Android содержит документацию, загрузки, блоги и многое другое. (EN)
     
  • Подробнее о Dalvik Virtual Machine.(EN)
     
  • Видеоруководства по устройству Dalvik VM на YouTube.(EN)
     
  • Unlocking Android: A Developer's Guide - руководство с практическими рекомендациями по ОС Android и инструментам разработки.(EN)
     
  • Интервью и дискуссии разработчиков ПО в подкастах на developerWorks.(EN)
     
  • Чтобы оставаться в курсе новостей, посещайте раздел технических событий и Web-кастов на developerWorks.(EN)
     
  • Следите за developerWorks по Твиттеру.(EN)
     
  • Следите за предстоящими конференциями, выставками, Web-кастами и другими мероприятиями во всем мире, которые могут заинтересовать разработчиков открытого ПО IBM.(EN)
     
  • Посетите раздел открытого ПО developerWorks, содержащий богатую справочную информацию, инструменты и новости по проектам, которые помогают в разработке технологий с открытым исходным кодом и их использовании с продуктами IBM.(EN)
     
  • Узнавайте о технологиях и возможностях продуктов IBM и open source и учитесь работать с ними с помощью бесплатных демонстраций по требованию на developerWorks.(EN)
     

Получить продукты и технологии

Обсудить

Об авторе

Когда Фрэнк Эйблсон (Frank Ableson) закончил карьеру баскетболиста в команде своего колледжа, не заключив многолетнего контракта с «Лос-Анджедес Лейкерс», он занялся разработкой компьютерных программ. Он любит решать сложные задачи, особенно из области связи и интерфейсов с аппаратурой. Свободное время Фрэнк проводит со своей женой Никки и детьми. С ним можно связаться по адресу: frank@cfgsolutions.com.



Пишем игру для Android. Часть 5. Хранение настроек

 

Пишем игру для Android. Часть 5. Хранение настроек

27.03.2009

Источник: megadarja.blogspot.com



Вот мы и добрались до конца. Осталось сделать только главное меню приложения, а также сделать игре настройки (собственно, меню только для того и нужно, чтобы было откуда настройки вызывать). Ну первое мы еще с прошлой статьи умеем, так что особых сложностей быть не должно. А вот второе следует рассмотреть подробнее. Итак, приступим.

Окно приветствия

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

На нашей форме приветствия будет какая-нибудь картинка и три кнопки: "Начать игру", "Настройки" и "Выход". Картинку в формате png, которую мы назовем

start.png
нужно положить в папку /res/drawable. Названия кнопок нужно вынести в
strings.xml
, добавив следующие строки:

res/values/strings.xml

<resources>
    <string name="app_name">PingPong</string>
    <string name="start_title">Start Game</string>
    <string name="settings_title">Settings</string>
    <string name="exit_title">Exit</string>
</resources>

Тогда разметка для новой формы будет выглядеть так:

res/layout/start.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="bottom"
    android:background="@drawable/start"
    android:padding="8dip">

    <Button android:id="@+id/StartButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/start_title" />

    <Button android:id="@+id/SettingsButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/settings_title" />

    <Button android:id="@+id/ExitButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/exit_title" />
</LinearLayout>

Фоновое изображение можно задать экрану с помощью полезного свойства

android:background
. Собственно, так можно задавать фон и кнопкам, и вообще чему угодно. Получили вот такую разметку:

Экран приветствия

Добавим соответствующий этой разметке класс

StartScreen.java
. Сразу обработаем нажатия всех кнопок:

StartScreen.java

public class StartScreen extends Activity implements OnClickListener
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.start);

        // Кнопка "Start"
        Button startButton = (Button)findViewById(R.id.StartButton);
        startButton.setOnClickListener(this);

        // Кнопка "Exit"
        Button exitButton = (Button)findViewById(R.id.ExitButton);
        exitButton.setOnClickListener(this);

        // Кнопка "Settings"
        Button settingsButton = (Button)findViewById(R.id.SettingsButton);
        settingsButton.setOnClickListener(this);
    }

    /** Обработка нажатия кнопок */
    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.StartButton:
            {
                Intent intent = new Intent();
                intent.setClass(this, GameScreen.class);
                startActivity(intent);
                break;
            }

            case R.id.SettingsButton:
            {
                break;
            }

            case R.id.ExitButton:
                finish();
                break;

            default:
                break;
        }
    }
}

По нажатию на кнопку Start происходит переход на экран с игрой. Обратите внимание, что

StartScreen
при этом не закрывается. Это значит, что, когда закроется
StartScreen
, мы попадем обратно на экран приветствия. По нажатию на Settings пока что ничего не происходит, по Exit — приложение закрывается.

Осталось только зарегистрировать эту форму в приложении и сделать ее главной. Для этого идем в

AndroidManifest.xml
:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.android.pingpong"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".GameScreen"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" /> 
                <category android:name="android.intent.category.SAMPLE_CODE" /> 
            </intent-filter>
        </activity>
        <activity android:name=".StartScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
</application> </manifest>

Теперь приложение начинается с

StartScreen
, все кнопки работают.

Настройки

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

Сама форма с настройками делается достаточно просто. Есть специальный наследник класса

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

Разметка

Разметка для формы с настройками выглядит несколько необычно:

res/xml/preferences.xml

<?xml version="1.0" encoding="UTF-8"?>
<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory
        android:title="@string/prefs_title">

        <ListPreference android:key="@string/pref_difficulty"
            android:title="@string/difficulty_title"
            android:entries="@array/difficulty"
            android:entryValues="@array/difficulty"
            android:defaultValue="1"
           />

        <EditTextPreference android:key="@string/pref_max_score"
             android:title="@string/score_title"
             android:defaultValue="10"
             />
    </PreferenceCategory>
</PreferenceScreen>

Настолько необычно, что, если поместить этот XML в папку layout, eclipse начнет ругаться, что не может разрезолвить такие классы. Собственно, это не просто разметка: там также содержатся ключи настроек и значения по умолчанию. Поэтому мы создадим отдельную папку xml, и поместим этот файл туда. А теперь обо всем по порядку.

PreferenceCategory

Ну,

PreferenceScreen
все понятно, а что такое
PreferenceCategory
? Как ни удивительно, это категория настроек. Например, у какой-нибудь игры могут быть настройки графики, настройки звука, настройки сети и т.д.. Удобно отобразить их сгруппированными, вот так:

Пример формы с настройками

А можно обойтись без групп:

PreferenceScreen
уже сам по себе является контейнером для настроек. В нашем случае, например, настроек мало и группировать нечего. Но это я так, для полноты картины.

Какие можно сделать настройки

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

Preference
, и наследуют от него всякие полезные атрибуты, которые можно задавать в разметке, как то:

android:title
Заголовок настройки или контейнера настроек.
android:summary
Краткое описание. Проще говоря, это то, что пишется под заголовком мелким шрифтом.
android:defaultValue
Значение по умолчанию
android:key
Ключ, с которым данная настройка будет храниться и с которым можно будет к ней обращаться.
android:dependency
Задает зависимость от другого контрола. Например, можно поставить эдитору зависимость от флажка, и тогда, если флажок не установлен, но эдитор будет неактивен.

Ну и еще кое-что. Рассмотрим некоторые конкретные виды настроек.

CheckBoxPreference

Вот такой флажок:

Флажок
EditTextPreference

Редактор текста.

Редактор текста

Честно говоря, у редактора текста очень хотелось бы валидатор, а еще лучше маску. Например, IP-адрес имеет специфический формат, и хотелось бы запрещать пользователю вводить там что попало. Но мне такую функциональность так и не удалось обнаружить.

ListPreference

Своеобразная реализация Dropdown-а. Хотя, на телефоне наверно и вправду так удобнее.

Список

На этом контроле хотелось бы остановиться подробнее. А конкретнее, рассказать, откуда он, собственно, берет элементы списка. А берет он их из ресурсов с помощью таких атрибутов.

android:entries

Здесь хранится ссылка на ресурс, в котором хранятся отображаемые элементы списка. Все значения, которые хочется вынести в ресурсы, хранятся в папке values. До сих пор там была только одна XML-ка —

strings.xml
. Но теперь надо добавить еще одну — arrays.xml. И добавить в узел
resources
следующее:

<string-array name="performance">
    <item>Best performance</item>
    <item>Normal performance and appearance</item>
    <item>Best appearance</item>
</string-array>

После этого можно смело указывать в

android:entries
этот ресурс, список будет загружен.

Кстати говоря, в

item
-ах может быть не непосредственно строка, а ссылка на строку в
strings.xml
. Например, в нашем случае будет так (разумеется, стоит добавить соответствующие значения в
strings.xml
):

res/values/arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="difficulty">
        <item>@string/difficulty_easy</item>
        <item>@string/difficulty_normal</item>
        <item>@string/difficulty_hard</item>
    </string-array>
</resources>
android:entryValues
Список действительных значений. Также ссылка на ресурс, и задавать можно так же. Если кодов будет меньше, чем значений, приложение будет валиться с исключением. Если больше — не будет. В нашем случае можно в entries и entryValues смело задавать одно и то же, но бывает, когда имеет смысл их разделять.

А еще мне никак не удалось победить у

ListPreference
атрибут
adnroid:defaultValue
. Не работает и все.

RingtonePreference

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

Рингтон

Класс формы

Класс для формы с настройками будет выглядеть так:

SettingsScreen.java

public class SettingsScreen extends PreferenceActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // Настройки и их разметка загружаются из XML-файла
        addPreferencesFromResource(R.xml.preferences);
    }
}

Кстати, настройки необязательно загружать из XML-ки, можно добавлять все эти настройки прямо в коде конструктора. В сэмплах, которые идут с Android SDK, такие примеры есть.

Добавляем в

StartScreen
код для открытия формы настроек, прописываем
SettingsScreen
в
AndroidManifest.xml
. (Все выглядит точно так же, как и для
GameScreen
, так что листинги не привожу). И увидим мы следующее:

Форма с настройками

Не знаю, кому как, а мне нравится, когда рядом с названием опции написано ее значение. Но как это сделать автоматически, я так и не нашла, пришлось все делать руками, используя для этого поле

summary
. Итак, summary настройки должно обновляться при изменении значения. По счастью, есть такое событие
OnPreferenceChange
. Итак, пишем:

SettingsScreen.java

public class SettingsScreen extends PreferenceActivity
          implements Preference.OnPreferenceChangeListener
{
    /* Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
        ListPreference difficulty = 
                                (ListPreference)this.findPreference("pref_difficulty");
        difficulty.setSummary(difficulty.getEntry());
        difficulty.setOnPreferenceChangeListener(this);

        EditTextPreference maxScore = 
                      (EditTextPreference)this.findPreference("pref_max_score");
        maxScore.setSummary(maxScore.getText());
        maxScore.setOnPreferenceChangeListener(this);
}
public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary((CharSequence)newValue); return true; }
}

Думаю, все понятно без слов. Теперь мы видим такую картину:

Настройки и их значения

И, если мы будем менять настройки, изменения сразу же будут отображаться в

summary
.

Использование настроек в других формах

Ну все, настройки мы сделали, они как-то сами где-то сохранились. Теперь возникла необходимость их прочитать и что-нибудь с ними сделать. Читать и делать мы будем в классе

GameManager
, а конкретно, в конструкторе. Для работы с сохраненными настройками приложения используется класс
SharedPreferences
. Вся работа по чтению и применению настроек выглядит так:

SettingsScreen.java

public GameManager(SurfaceHolder surfaceHolder, Context context)
{
    ...

    // стили для рисования игрового поля
    ...

    // игровые объекты
    ...
    // применение настроек
    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);

    String difficulty = settings.getString(res.getString(R.string.pref_difficulty),
                                                   res.getString(R.string.difficulty_normal));
    setDifficulty(difficulty);

    int maxScore = Integer.parseInt(settings.getString(
                                          res.getString(R.string.pref_max_score), "10"));
    setMaxScore(maxScore);
}

Метод

setDifficulty
приводить не буду, там ничего особо умного не написано.

Настройки из
SharedPreferences
можно читать с помощью методов
getString
,
getInt
,
getBoolean
и т.п. Все они принимают два параметра &mdahs; ключ к настройке (то, что мы задавали с помощью атрибута
android:key
) и значение по умолчанию. Однако, воспользоваться чем-то кроме
getString

мне так и не удалось.

Заключение

Итак, мануал закончен. Получился он огромным, но при этом собственно про андроид оказалось не так уж и много, что самое-то обидное :( Спасибо, если кто дочитал до конца. Буду рада услышать любые мнения.

Отдельное спасибо xeye и std.denis

А вот и все исходники

Пишем игру для Android. Часть 4. Игровой процесс

 

Пишем игру для Android. Часть 4. Игровой процесс

27.03.2009

Источник: megadarja.blogspot.com



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

Обработка проигрыша

Помнится, мы заводили в классе

Racquet
поле
mScore
, в котором собирались хранить количество очков у игрока. Теперь самое время начать использовать это поле.

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

Проверка проигрыша должна осуществляться также в методе

updateObjects()
GameManager
-а. Описанная нами логика запишется так:

GameManager.java

private void updateObjects()
{
    ...
    // проверка проигрыша
    if (mBall.getBottom() < mThem.getBottom())
    {
        mUs.incScore();
        reset();
    }

    if (mBall.getTop() > mUs.getTop())
    {
        mThem.incScore();
        reset();
    }
}

Racquet.incScore()
увеличивает на 1 количество очков у игрока:

Racquet.java

/** Увеличить количество очков игрока */
public void incScore()
{
    mScore++;
}

GameManager.reset()
расставляет ракетки и мячик на исходные позиции, задает мячику новый случайный угол, а также делает паузу (чтобы игрок успел понять, что произошло).

GameManager.java

private void reset()
{
    // ставим мячик в центр
    mBall.setCenterX(mField.centerX());
    mBall.setCenterY(mField.centerY());
    // задаем ему новый случайный угол
    mBall.resetAngle();

    // ставим ракетки в центр
    mUs.setCenterX(mField.centerX());
    mThem.setCenterX(mField.centerX());

    // делаем паузу
    try
    {
        sleep(LOSE_PAUSE);
    }
    catch (InterruptedException iex)
    {
    }
}

LOSE_PAUSE
— это константа класса
GameManager
, в которой задается длина паузы в миллисекундах (у меня она равна 2000). Метод же
resetAngle()
класса
Ball
выглядит следующим образом:

Ball.java

/** Задает новое случайное значение угла */
public void resetAngle()
{
    mAngle = getRandomAngle();
}

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

Вывод количества очков

Вывод текста на экран производится с помощью метода

drawText(String text, float x, float y, Paint paint)
класса Canvas. Как можно заметить, стили текста задаются с помощью экземпляра класса
Paint
. Где-то в первой части мы создавали в
GameManager
такое поле
mPaint
, где хранились стили для рисования игрового поля. Для вывода текста можно использовать это же поле, и при каждой перерисовке экрана задавать ему стили сначала для игрового поля, а потом для текста. А можно завести отдельный экземпляр
Paint
для хранения стилей текста:

GameManager.java

private Paint mScorePaint;

Инициализировать его в конструкторе:

GameManager.java

public GameManager(SurfaceHolder surfaceHolder, Context context)
{
    mSurfaceHolder = surfaceHolder;
    Resources res = context.getResources();
    mRunning = false;

    // стили для рисования игрового поля
    mPaint = new Paint();
    mPaint.setColor(Color.BLUE);
    mPaint.setStrokeWidth(2);
    mPaint.setStyle(Style.STROKE);
    // стили для вывода счета
    mScorePaint = new Paint();
    mScorePaint.setTextSize(20);
    mScorePaint.setStrokeWidth(1);
    mScorePaint.setStyle(Style.FILL);
    mScorePaint.setTextAlign(Paint.Align.CENTER);
// игровые объекты mField = new Rect(); mBall = new Ball(res.getDrawable(R.drawable.ball)); mUs = new Racquet(res.getDrawable(R.drawable.us)); mThem = new Racquet(res.getDrawable(R.drawable.them)); }

А непосредственно вывод счета игры производится в методе, где происходит вся отрисовка текущей игровой ситуации —

refreshCanvas

GameManager.java

private void refreshCanvas(Canvas canvas)
{
    // вывод фонового изображения
    canvas.drawBitmap(mBackground, 0, 0, null);

    // рисуем игровое поле
    canvas.drawRect(mField, mPaint);

    // рисуем игровые объекты
    mBall.draw(canvas);
    mUs.draw(canvas);
    mThem.draw(canvas);
    // вывод счета
    mScorePaint.setColor(Color.RED);
    canvas.drawText(String.valueOf(mThem.getScore()),
                mField.centerX(), mField.top - 10, mScorePaint);
    mScorePaint.setColor(Color.GREEN);
    canvas.drawText(String.valueOf(mUs.getScore()),
         mField.centerX(), mField.bottom + 25, mScorePaint);
}

Правда, совсем уж без изменения стиля не обошлось. Наши очки мы рисуем зелёным, а очки противника — красным. Запустив, увидим примерно такую картину:

Выводится количество очков

Использование пользовательских шрифтов

А теперь нам захотелось использовать для вывода счета какой-нибудь наш красивый шрифт. Рассмотрим, как это можно сделать.

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

res
— это то, что используются они гораздо реже, и доставать их оттуда сложнее. Ресурсы из
res
можно запросто достать с помощью класса
R
, а assets вытаскиваются с помощью специального класса
AssetManager
.

Итак, создадим в папке

assets
папку fonts и кинем туда шрифт под названием
Mini.ttf
. Теперь, чтобы достать этот шрифт и использовать его для вывода количества очков, достаточно добавить в инициализацию
mScorePaint
в конструкторе одну строчку:

GameManager.java

mScorePaint.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/Mini.ttf"));

context.getAssets()
получит менеджер ресурсов (
AssetManager
) для данного приложения, откуда потом будет можно загрузить шрифт по указанному пути. Стоит обратить внимание, что путь является case-sensitive, т.е. "fonts/mini.ttf" уже ничего не загрузит.

Загружен пользовательский шрифт

Неприятность

И всё бы хорошо, но теперь время от времени стали возникать ситуации, когда в начале игры у одного из игроков выводится не 0 очков, а 1. Я так понимаю, что проблемы возникают в самом начале программы, перед

initPositions
, когда у игровых объектов координаты еще не заданы, а
updateObjects
уже вызывается. Чтобы исправить положение, заведем в классе
GameManager
еще одно булево поле
mInitialized
, в конструкторе зададим как
false
, а в
initPositions
присвоим ему
true
. Тогда в
run
можно написать так:

GameManager.java

public void run()
{
    while (mRunning)
    {
        Canvas canvas = null;
        try
        {
            // подготовка Canvas-а
            canvas = mSurfaceHolder.lockCanvas();
            synchronized (mSurfaceHolder)
            {
                if (mInitialized)
                {
updateObjects(); // обновляем объекты refreshCanvas(canvas); // обновляем экран sleep(20); } } } catch (Exception e) { } finally { if (canvas != null) { mSurfaceHolder.unlockCanvasAndPost(canvas); } } } }

Теперь гарантированно не будет происходить никаких проверок, пока не будут инициализированы координаты игровых объектов. Проблема решена.

Обработка окончания игры

Прежде всего, нам следует завести в

GameManager
переменную, где бы хранилось количество очков, до которого идет игра. Заведем такую переменную и сразу сеттер к ней. Итак:

GameManager.java

/** Максимальное число очков, до которого идет игра */
private static int mMaxScore = 5;

public static void setMaxScore(int value)
{
    mMaxScore = value;
}

Саму проверка на окончание игры можно поместить как в метод

updateObjects()
, так и прямо в
run()
. Но, думаю, правильнее именно в
updateObjects()
:

GameManager.java

/** Обновление состояния игровых объектов */
private void updateObjects()
{
    // Обновление состояния игровых объектов
    ...

    // обработка столкновений
    ...

    // проверка проигрыша
    ...

    // проверка окончания игры
    if (mUs.getScore() == mMaxScore  mThem.getScore() == mMaxScore)
    {
        this.mRunning = false;
    }
}

Напомню, что метод

run
выглядит так:

GameManager.java

public void run()
{
    while (mRunning)
    {
        // обновление и отрисовка объектов
    }
}

То есть, когда

mRunning
станет равным
false
, поток завершится. Раз он завершился — игра закончена, и надо вывести на экран ее результаты. Так что логично видеть в методе
run()
что-то вроде:

GameManager.java

public void run()
{
    while (mRunning)
    {
        // обновление и отрисовка объектов
    }
    // рисование GameOver
    ...
}

А теперь разберемся, как это рисование может выглядеть. Как известно, при рисовании мы лочим Canvas, рисуем, и затем разлочиваем. При этом еще нужно отловить возможные исключения. Получается куча кода, которая появляется при каждом рисовании и сильно загромождает текст программы. Естественно, хочется вынести все это в метод-обертку и передавать туда ссылку на функцию, осуществляющую собственно рисование. На C# это выглядело бы так:

delegate void DrawFunction(Canvas canvas);

private void draw(DrawFunction something)
{
    Canvas canvas = null;
    try
    {
        canvas = mSurfaceHolder.lockCanvas();
        synchronized (mSurfaceHolder)
        {
            something(canvas);
        }
    }
    }
    catch (Exception e) { }
    finally
    {
        if (canvas != null)
        {
            mSurfaceHolder.unlockCanvasAndPost(canvas);
        }
    }
}

Но здесь нам не C#, здесь климат иной, и делегатов нет. Однако, как мне подсказал товарищ xeye, подобный код можно написать. Итак, добавим в

GameManager
такой интерфейс:

GameManager.java

private interface DrawHelper
{
    void draw(Canvas canvas);
}

И такой метод, куда мы вынесем всю работу по подготовке canvas-а:

GameManager.java

private void draw(DrawHelper helper)
{
    Canvas canvas = null;
    try
    {
        // подготовка Canvas-а
        canvas = mSurfaceHolder.lockCanvas();
        synchronized (mSurfaceHolder)
        {
            if (mInitialized)
            {
                helper.draw(canvas);
            }
        }
    }
    catch (Exception e) { }
    finally
    {
        if (canvas != null)
        {
            mSurfaceHolder.unlockCanvasAndPost(canvas);
        }
    }
}

Теперь можно завести конкретные реализации

DrawHelper
на все случаи жизни. Я добавляю их две:

GameManager.java

/** Хелпер для перерисовки экрана */
private DrawHelper mDrawScreen;

/** Хелпер для рисования результата игры*/
private DrawHelper mDrawGameover;

Инициализирую в конструкторе таким образом:

GameManager.java

public GameManager(SurfaceHolder surfaceHolder, Context context)
{
    ...
   
    // функция для рисования экрана
    mDrawScreen = new DrawHelper()
    {
        public void draw(Canvas canvas)
        {
            refreshCanvas(canvas);
        }
    };

    // функция для рисования результатов игры
    mDrawGameover = new DrawHelper()
    {
        public void draw(Canvas canvas)
        {
            // Вывели последнее состояние игры
            refreshCanvas(canvas);
           
            // смотрим, кто выиграл и выводим соответствующее сообщение
            String message = "";
            if (mUs.getScore() > mThem.getScore())
            {
                mScorePaint.setColor(Color.GREEN);
                message = "You won";
            }
            else
            {
                mScorePaint.setColor(Color.RED);
                message = "You lost";
            }
            mScorePaint.setTextSize(30);
            canvas.drawText(message, mField.centerX(), mField.centerY(), mScorePaint);
        }
    };
}

После этого метод

run()
преображается до неузнаваемости:

GameManager.java

/** Действия, выполняемые в потоке */
public void run()
{
    while (mRunning)
    {
        if (mInitialized)
        {
            updateObjects(); // обновляем объекты
            draw(mDrawScreen);
        }
    }
    draw(mDrawGameover);
}

И сразу результат:

Результат игры

Пауза

Тут совсем кратко. Объявим в классе

GameManager
поле:

GameManager.java

/** Стоит ли приложение на паузе */
private boolean mPaused;

Если приложение на паузе, поток работает "вхолостую", т.е. состояния объектов не меняются и вообще ничего не происходит. Это значит, в методе

run()
будет следущее:

GameManager.java

public void run()
{
    while (mRunning)
    {
        if (mPaused) continue;
if (mInitialized) { updateObjects(); // обновляем объекты draw(mDrawScreen); } } draw(mDrawGameover); }

Будем ставить приложение на паузу, если нажата средняя клавиша джойстика:

GameManager.java

public boolean doKeyDown(int keyCode)
{
    switch (keyCode)
    {
        case KeyEvent.KEYCODE_DPAD_LEFT:
        case KeyEvent.KEYCODE_A:
            mUs.setDirection(GameObject.DIR_LEFT);
            return true;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
        case KeyEvent.KEYCODE_D:
            mUs.setDirection(GameObject.DIR_RIGHT);
            return true;
        case KeyEvent.KEYCODE_DPAD_CENTER:
            mPaused = !mPaused;
            draw(mDrawPause);
            return true;
default: return false; } }

mDrawPause
— хелпер для рисования паузы. Я уже не буду приводить к нему листинг, там все просто.

Итак

У нас уже совсем готовая игра. Можно играть, выигрывать, проигрывать, ставить на паузу.

Исходники примера

Пишем игру для Android. Часть 3. Управление игровыми объектами

 

Пишем игру для Android. Часть 3. Управление игровыми объектами

27.03.2009

Источник: megadarja.blogspot.com




В этой статье мы рассмотрим две темы: управление игровыми объектами и их взаимодействие. Мячик у нас уже летает, осталось сделать, чтобы он отражался от стен и ракеток; также стоит реализовать управление нижней ракетки игроком, а верхней — неким алгоритмом. Итак, приступим.

Движение мячика

Для начала добавим в

GameObject
следующие полезные функции:

GameObject.java

/** Верхняя граница объекта */
public int getTop() { return mPoint.y; }

/** Нижняя граница объекта */
public int getBottom() { return mPoint.y + mHeight; }

/** Левая граница объекта */
public int getLeft() { return mPoint.x; }

/** Правая граница объекта */
public int getRight() { return mPoint.x + mWidth; }

/** Центральная точка объекта */
public Point getCenter() { return new Point(mPoint.x + mWidth / 2, mPoint.y + mHeight / 2); }

/** Высота объекта */
public int getHeight() { return mHeight; }

/** Ширина объекта */
public int getWidth() { return mWidth; }

/** @return Прямоугольник, ограничивающий объект */
public Rect getRect() { return mImage.getBounds(); }

/** Проверяет, пересекаются ли два игровых объекта */
public static boolean intersects(GameObject obj1, GameObject obj2)
{
    return Rect.intersects(obj1.getRect(), obj2.getRect());
}

Игровые объекты ничего не знают ни о друг друге, ни об игровом поле, поэтому все столкновения будут обрабатываться GameManager-ом. Итак, рассмотрим сначала такую ситуацию:

 

Столкновение со стеной

 

Итак, наш мячик был в некотором состоянии A, потом, пройдя расстояние

mSpeed
в заданном направлении, перешел в состояние B, и оказалось, что он вышел за пределы поля. Тут надо сделать следующее: поместить шарик в правильное состояние C, получившееся при отражении соответствующей координаты от стены, и изменить направление движения так, чтобы угол падения был равен углу отражения.

Расчет координат при столкновении

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

Вычисление нового направления движения

Рассмотрим варианты столкновения. Пусть ? — угол, под которым движется мячик, а ? — угол, получившийся после столкновения. Посмотрим, как ? зависит от ? в различных случаях:

  Значения ? Столкновение с вертикалью Столкновение с горизонталью
1 0 < ? < ? / 2 ? = ? ? ? ? = 2? ? ?
2 ? / 2 < ? < ? ? = ? ? ? ? = 2? ? ?
3 ? < ? < 3? / 2 ? = 3? ? ? ? = 2? ? ?
4 3? / 2 < ? < 2? ? = 3? ? ? ? = 2? ? ?

Выяснив всй это, можно добавить в класс

Ball
следующие функции:

Ball.java

/** Отражение мячика от вертикали */
public void reflectVertical()
{
    if (mAngle > 0 && mAngle < PI)
        mAngle = PI - mAngle;
    else
        mAngle = 3 * PI - mAngle;
}

/** Отражение мячика от горизонтали */
public void reflectHorizontal()
{
    mAngle = 2 * PI - mAngle;
}

Обновление же в

GameManager
изменится таким образом:

GameManager.java

private void updateObjects()
{
    mBall.update();
    mUs.update();
    mThem.update();

    // проверка столкновения мячика с вертикальными стенами
     if (mBall.getLeft() <= mField.left)
     {
      mBall.setLeft(mField.left + Math.abs(mField.left - mBall.getLeft()));
      mBall.reflectVertical();
     }
     else if (mBall.getRight() >= mField.right)
     {
      mBall.setRight(mField.right - Math.abs(mField.right - mBall.getRight()));
      mBall.reflectVertical();
     }

    // проверка столкновения мячика с горизонтальными стенами
     if (mBall.getTop() <= mField.top)
     {
      mBall.setTop(mField.top + Math.abs(mField.top - mBall.getTop()));
      mBall.reflectHorizontal();
     }
     else if (mBall.getBottom() >= mField.bottom)
     {
      mBall.setBottom(mField.bottom - Math.abs(mField.bottom - mBall.getBottom()));
      mBall.reflectHorizontal();
     }
}

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

Управление ракеткой

Перемещать ракетку можно двумя способами. Первый — отлавливать нажатие кнопок вправо и влево, и при нажатии смещать ракетку в нужную сторону. Однако, как показала практика, такой способ не очень хорош, так как ракетка двигается резко и подтормаживает при первом нажатии. Более прогрессивным способом оказался другой: при нажатии клавиши назначать ракетке соответствующий Direction, а при отпускании обнулять его. А в методе

updateObjects
ракетка сама перемещается в том направлении, которое у нее указано.

Итак, теперь надо бы написать код для обработки нажатия клавиш. Вообще, нажатие клавиш ловит View, и для обработки нужно перегрузить функции

onKeyDown
и
onKeyUp
. Однако, игровыми объектами у нас ведает
GameManager
, так что фактическая обработка будет происходить именно там. Так что добавляем в
GameView
следующее:

GameView.java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
    return mGameManager.doKeyDown(keyCode);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
    return mGameManager.doKeyUp(keyCode);
}

А в

GameManager
нужно добавить методы
doKeyDown
и
doKeyUp
, который будут выполнять всю работу:

GameView.java

/**
 * Обработка нажатия кнопки
 * @param keyCode Код нажатой кнопки
 * @return Было ли обработано нажатие
 */
public boolean doKeyDown(int keyCode)
{
    switch (keyCode)
    {
        case KeyEvent.KEYCODE_DPAD_LEFT:
            mUs.setDirection(GameObject.DIR_LEFT);
            return true;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            mUs.setDirection(GameObject.DIR_RIGHT);
            return true;
        default:
            return false;
    }
}
/**
 * Обработка отпускания кнопки
 * @param keyCode Код кнопки
 * @return Было ли обработано действие
 */
public boolean doKeyUp(int keyCode)
{
    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
        keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)
    {
        mUs.setDirection(GameObject.DIR_NONE);
        return true;
    }
    return false;
}
Однако, если мы запустим сейчас наше приложение, окажется, что обработка клавиш не работает. View попросту не получает эти события. Чтобы исправить положение, нужно в конструктор
GameView

добавить одну строчку:

GameView.java

public GameView(Context context, AttributeSet attrs)
{
    super(context, attrs);

    // подписываемся на события Surface
    mSurfaceHolder = getHolder();
    mSurfaceHolder.addCallback(this);

    mGameManager = new GameManager(mSurfaceHolder, context);
    setFocusable(true);
}

Теперь все работает, ракетка управляется.

Искусственный интеллект для противника

У компьютера логика будет простая: по возможности не допускать, чтобы мячик выходил за пределы ракетки. Если мячик окажется правее ракетки, ракетка едет направо, если левее — налево. Реализуем всю эту логику в методе

moveAI()
у
GameManager
-а:

GameManager.java

private void moveAI()
{
    if (mThem.getLeft() > mBall.getRight())
        mThem.setDirection(GameObject.DIR_LEFT);
    else if (mThem.getRight() < mBall.getLeft())
        mThem.setDirection(GameObject.DIR_RIGHT);
    mThem.update();
}

А

GameManager.updateObjects()
будет выглядеть так:

GameManager.java

private void updateObjects()
{
    mBall.update();
    mUs.update();
    moveAI();
    // обработка столкновений     ... }

Еще пара доработок

Ракетки выходят за пределы экрана

Собственно, никто им этого делать не запрещает. Напишем в

GameManager
такой метод:

GameManager.java

private void placeInBounds(Racquet r)
{
    if (r.getLeft() < mField.left)
        r.setLeft(mField.left);
    else if (r.getRight() > mField.right)
        r.setRight(mField.right);
}

А в

GameManager.updateObjects()
добавим:

GameManager.java

private void updateObjects()
{
    mBall.update();
    mUs.update();
    moveAI();
    // чтобы ракетки не выходили за пределы поля
    placeInBounds(mUs);
    placeInBounds(mThem);
    // обработка столкновений     ... }

Теперь все стало хорошо, ракетки не выходят за пределы поля.

Столкновение мячика с ракетками

Сейчас у нас мячик летает, отражаясь от стен, а хотелось бы, чтобы он летал, отражаясь только от вертикальных стен и от ракеток. Убираем из

GameManager.updateObjects()
весь код, осуществляющий отражение от горизонтальных стен, и пишем код для отражения от ракеток. Код достаточно прост:

GameManager.java

private void updateObjects()
{
    // Обновление положений объектов
    ...

    // Обработка столкновений с вертикальными стенами
    ...
    // проверка столкновений мячика с ракетками
    if (GameObject.intersects(mBall, mUs))
     {
      mBall.setBottom(mUs.getBottom() - Math.abs(mUs.getBottom() - mBall.getBottom()));
      mBall.reflectHorizontal();
     }
     else if (GameObject.intersects(mBall, mThem))
     {
      mBall.setTop(mThem.getTop() + Math.abs(mThem.getTop() - mBall.getTop()));
      mBall.reflectHorizontal();
     }
}

Физика тут простейшая: везде угол падения равен углу отражения. Надо сказать, смотрится это местами довольно несуразно, да и искусственный интеллект в таких условиях практически непобедим. Но более реалистичное движение шарика делать не хочется. Во-первых, я в физике не очень сильна, а во-вторых, статья получится совсем уж большая и совсем не про андроид. Так что оставлю эту часть читателям в качестве легкого домашнего упражнения :)

Итак

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

Исходники для этой части

Пишем игру для Android. Часть 2. Игровые объекты

 

Пишем игру для Android. Часть 2. Игровые объекты

27.03.2009

Источник: megadarja.blogspot.com


Добавим в нашу игру экшена. Как известно, в пинг-понге три действующих лица: мячик и две ракетки. Имеет смысл реализовать соответствующие классы —

Ball
и
Racquet
. А, поскольку имеются некоторые свойства, присущие обоим этим сущностям (как то: расположение, изображение, размеры и т.д.), то можно сделать базовый класс под названием
GameObject
. Диаграмма классов будет такая:

 

Диаграмма классов

 

У всех игровых объектов есть:

  • mPoint
    — левый верхний угол прямоугольника, ограничивающего объект. С её помощью однозначно определяется положение объекта на плоскости
  • mImage
    — изображение объекта. Изображения обычно берутся из ресурсов приложения — /res/drawable
  • mHeight
    ,
    mWidth
    — размеры изображения объекта. Вынесены как поля класса только для того, чтобы не загромождать код всякими
    mImage.getIntrinsicHeight()
    и
    mImage.getIntrinsicWidth()
  • mSpeed
    — скорость перемещения объекта (фактически, на сколько перемещается объект за один шаг)

Кроме того, все игровые классы умеют рисовать себя на указанном Canvas-е (метод

draw
) и вычислять своё следующее состояние (
update
). Причем, перемещение происходит так: вычисляется следующее состояние опорной точки (
updatePoint
), а потом туда переносится изображение. А, поскольку мячик и ракетка перемещаются по-разному, метод
updatePoint
сделан абстрактным.

В класс

Ball
добавлено поле
mAngle
— угол (в градусах) к оси Ox, под которым летит мячик. В класс
Racquet
— поля
mDirection
(направление, куда движется ракетка — вправо или влево, или вообще не движется) и
mScore
(количество очков у игрока).

Реализации классов

GameObject

GameObject.java

public abstract class GameObject
{
    // Константы для направлений
    public final int DIR_LEFT = -1;
    public final int DIR_RIGHT = 1;
    public final int DIR_NONE = 0;

    /** Координаты опорной точки */
    protected Point mPoint;

    /** Высота изображения */
    protected int mHeight;

    /** Ширина изображения */
    protected int mWidth;

    /** Изображение */
    private Drawable mImage;

    /** Скорость */
    protected int mSpeed;

    /**
     * Конструктор
     * @param image Изображение, которое будет обозначать данный объект
     */
    public GameObject(Drawable image)
    {
        mImage = image;
        mPoint = new Point(0, 0);
        mWidth = image.getIntrinsicWidth();
        mHeight = image.getIntrinsicHeight();
    }

    /** Перемещение опорной точки */
    protected abstract void updatePoint();

    /** Перемещение объекта */
    public void update()
    {
        updatePoint();
        mImage.setBounds(mPoint.x, mPoint.y, mPoint.x + mWidth, mPoint.y + mHeight);
    }

    /** Отрисовка объекта */
    public void draw(Canvas canvas)
    {
        mImage.draw(canvas);
    }
    /** Задает левую границу объекта */
    public void setLeft(int value) { mPoint.x = value; }

    /** Задает правую границу объекта */
    public void setRight(int value) { mPoint.x = value - mWidth; }

    /** Задает верхнюю границу объекта */
    public void setTop(int value) { mPoint.y = value; }

    /** Задает нижнюю границу объекта */
    public void setBottom(int value) { mPoint.y = value - mHeight; }

    /** Задает абсциссу центра объекта */
    public void setCenterX(int value) { mPoint.x = value - mHeight / 2; }

    /** Задает левую ординату центра объекта */
    public void setCenterY(int value) { mPoint.y = value - mWidth / 2; }
}

Ball

Ball.java

public class Ball extends GameObject
{
    private static final int DEFAULT_SPEED = 3;
    private static final int PI = 180;

    /** Угол, который составляет направление полета шарика с осью Ox */
    private int mAngle;

    /**
     * @see com.android.pingpong.objects.GameObject#GameObject(Drawable)
     */
    public Ball(Drawable image)
    {
        super(image);

        mSpeed = DEFAULT_SPEED; // задали скорость по умолчанию
        mAngle = getRandomAngle(); // задали случайный начальный угол
    }

    /**
     * @see com.android.pingpong.objects.GameObject#updatePoint()
     */
    @Override
    protected void updatePoint()
    {
        double angle = Math.toRadians(mAngle);

        mPoint.x += (int)Math.round(mSpeed * Math.cos(angle));
        mPoint.y -= (int)Math.round(mSpeed * Math.sin(angle));
    }

    /** Генерация случайного угла в промежутке [95, 110]U[275,290] */
    private int getRandomAngle()
    {
        Random rnd = new Random(System.currentTimeMillis());

        return rnd.nextInt(1) * PI + PI / 2 + rnd.nextInt(15) + 5;
    }
}

Racquet

Racquet.java

public class Racquet extends GameObject
{
    private static final int DEFAULT_SPEED = 3;

    /** Количество заработанных очков */
    private int mScore;

    /** Направление движения */
    private int mDirection;

    /** Задание направления движения*/
    private void setDirection(int direction)
    {
        mDirection = direction;
    }

    /**
     * @see com.android.pingpong.objects.GameObject#GameObject(Drawable)
     */
    public Racquet(Drawable image)
    {
        super(image);

        mDirection = DIR_NONE; // Направление по умолчанию - нет
        mScore = 0; // Очков пока не заработали
        mSpeed = DEFAULT_SPEED; // Задали скорость по умолчанию
    }

    /**
     * @see com.android.pingpong.objects.GameObject#updatePoint()
     */
    @Override
    protected void updatePoint()
    {
        // двигаем ракетку по оси Ox в соответствующую сторону
        mPoint.x += mDirection * mSpeed;
    }
}

Отображение игровых объектов

Рисуем картинки

Ну вроде классы наши готовы, можно теперь их использовать в программе. Но сначала надо нарисовать картинки, для наших объектов. Картинки должны быть в png. У меня получилось так:

Мячик Мячик
Наша ракетка Наша ракетка
Pакетка противника Pакетка противника

Берем все это счастье, и кидаем в /res/drawable, где у нас хранятся всякие такие ресурсы.

Создаем игровые объекты

Теперь нам надо где-то создать экземпляры наших классов, чтобы они там жили, обновлялись и отображались на экране. Очевидно, что тут нам поможет

GameManager
. Итак, добавим в него такие поля:

GameManager.java

/** Ресурсы приложения */
private Resources mRes;

/** Мячик */
private Ball mBall;

/** Ракетка, управляемая игроком */
private Racquet mUs;

/** Ракетка, управляемая компьютером*/
private Racquet mThem;

В конструкторе инициализируем их:

GameManager.java

public GameManager(SurfaceHolder surfaceHolder, Context context)
{
    mSurfaceHolder = surfaceHolder;
    mRunning = false;

    // инициализация стилей рисования
    mPaint = new Paint();
    mPaint.setColor(Color.BLUE);
    mPaint.setStrokeWidth(2);
    mPaint.setStyle(Style.STROKE);
    Resources res = context.getResources();

    mField = new Rect();
    mBall = new Ball(res.getDrawable(R.drawable.ball));
    mUs = new Racquet(res.getDrawable(R.drawable.us));
    mThem = new Racquet(res.getDrawable(R.drawable.them));
}

Расставляем их по местам

Однако, этого мало. У всех игровых объектов опорная точка задана в начале координат, т.е. они сейчас все в куче, и надо бы их растащить по местам. И самый лучший метод, где можно это сделать —

initPositions()
:

GameManager.java

public void initPositions(int screenHeight, int screenWidth)
{
    int left = (screenWidth - FIELD_WIDTH) / 2;
    int top = (screenHeight - FIELD_HEIGHT) / 2;

    mField.set(left, top, left + FIELD_WIDTH, top + FIELD_HEIGHT);
    // мячик ставится в центр поля
    mBall.setCenterX(mField.centerX());
    mBall.setCenterY(mField.centerY());

    // ракетка игрока - снизу по центру
    mUs.setCenterX(mField.centerX());
    mUs.setBottom(mField.bottom);

    // ракетка компьютера - сверху по центру
    mThem.setCenterX(mField.centerX());
    mThem.setTop(mField.top);
}

Заставляем их что-то делать

Итак, объекты мы создали, теперь надо заставить их что-то делать. Понятно, что все изменения должны происходить в цикле, который работает в методе

run()
. На каждом шаге цикла мы должны обновить состояния объектов и отобразить их. Добавим две функции:

GameManager.java

/** Обновление объектов на экране */
private void refreshCanvas(Canvas canvas)
{
    // рисуем игровое поле
    canvas.drawRect(mField, mPaint);

    // рисуем игровые объекты
    mBall.draw(canvas);
    mUs.draw(canvas);
    mThem.draw(canvas);
}

/** Обновление состояния игровых объектов */
private void updateObjects()
{
    mBall.update();
    mUs.update();
    mThem.update();
}

А в методе

run()
будут вызовы этих методов:

GameManager.java

public void run()
{
    while (mRunning)
    {
        Canvas canvas = null;
        try
        {
            // подготовка Canvas-а
            canvas = mSurfaceHolder.lockCanvas();
            synchronized (mSurfaceHolder)
            {
                updateObjects(); // обновляем объекты
                refreshCanvas(canvas); // обновляем экран
                sleep(20);
} } catch (Exception e) { } finally { if (canvas != null) { mSurfaceHolder.unlockCanvasAndPost(canvas); } } } }

Ну все, вроде сделали, можно запускать. Ракетки не двигаются, потому что ими пока никто не управляет, а вот шарик вполне себе летает. Правда, оставляет при этом за собой шлейф:

Бага

Делаем фон

Это всё из-за того, что мы не очищаем экран перед очередной отрисовкой. А очищать экран это тоже не так-то просто. Ничего вроде

canvas.clear()
я не нашла, так что пришлось извращаться. Суть в том, что мы заводим специальный Bitmap, при инициализации Surface задать ему размеры на весь экран, а потом в
refreshCanvas
выводить его. При желании можно загрузить в этот Bitmap какое-нибудь изображение из ресурсов.

Итак, заводим поле:

GameManager.java

/** Фон */
private Bitmap mBackground;

Инициализируем его в

initPositions

GameManager.java

public void initPositions(int screenHeight, int screenWidth)
{
    int left = (screenWidth - FIELD_WIDTH) / 2;
    int top = (screenHeight - FIELD_HEIGHT) / 2;

    mField.set(left, top, left + FIELD_WIDTH, top + FIELD_HEIGHT);
    mBackground = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.RGB_565);
// мячик ставится в центр поля mBall.setCenterX(mField.centerX()); mBall.setCenterY(mField.centerY()); // ракетка игрока - снизу по центру mUs.setCenterX(mField.centerX()); mUs.setBottom(mField.bottom); // ракетка компьютера - сверху по центру mThem.setCenterX(mField.centerX()); mThem.setTop(mField.top); }

И отрисовываем в

refreshCanvas
:

GameManager.java

private void refreshCanvas(Canvas canvas)
{
    // вывод фонового изображения
    canvas.drawBitmap(mBackground, 0, 0, null);
// рисуем игровое поле canvas.drawRect(mField, mPaint); // рисуем игровые объекты mBall.draw(canvas); mUs.draw(canvas); mThem.draw(canvas); }

И получаем примерно вот такую картину:

 

Движущийся шар

 

Итак

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

Исходники примера

Как начать программировать для Android. Установка Eclipse и Android SDK

 

Как начать программировать для Android. Установка Eclipse и Android SDK


logo_androidAndroid является программным стеком для мобильных устройств, в том числе операционной системы, промежуточного программного обеспечения и основных приложений. Он разрабатывается группой Open Handset Alliance, включающей в себя более 30 мобильных компаний и технологий. Android был создан “с нуля”, чтобы позволить разработчикам создавать конкурентоспособные мобильные приложения, использующие все возможности мобильного телефона. Разработчики могут создавать приложения для платформы с помощью SDK Android.

С чего же начать?

Создавать программы для Android можно на всех самых распространенных ОС, таких как Windows, Linux и Mac OS. Для этого нам понадобяться 4 вещи: Java Development Kit (JDK), Eclipse, плагин для Eclipse(ADT) и собственно Android SDK.

 

Ссылки:

  1. Java Development Kit (JDK) (~72MB)
  2. Eclipse Classic (~151MB)
  3. Android SDK (~94MB)
  4. ADT его установим через Eclipse

Замечание: даже если у вас 64 битная ОС, устанавливать нужно только 32х битный JDK

Установка

  1. Устанавливаем Java Development Kit (JDK), ничего не обычного там нет. Ставим все по умолчанию.
  2. Распаковываем Eclipse Classic в c:eclipse
  3. Распаковываем Android SDK в папку c:eclipse

 

Должно выглядеть как на картинке ниже:

andr_sdk_install_1

 

Настройка Eclipse

1. Запускаем Eclipse и выбираем в меню: Help > Software Updates->Find and Install…

 

andr_sdk_install_2

 

2. Выбираем Search for new features to install и нажимаем Next >

 

andr_sdk_install_3

 

3. В появившемся окне нажимаем кнопку New Remote Site…
Добавляем источник для Android Development Tools: https://dl-ssl.google.com/android/eclipse/

 

andr_sdk_install_4

 

Нажимаем OK. Проверяем что наш источник выбран andr_sdk_install_5, нажимаем Finish. Начнется поиск доступных плагинов, еще раз выделяем плагин для установкиandr_sdk_install_5, далее, соглашаемся с лицензией, Finish-> Install All.

4. Указываем Eclipse путь к установленному SDK:

В меню Window->Preferences… В разделе Android указываем путь к SDK:

andr_sdk_install_6

 

После выбора должно быть что то вида:

 

andr_sdk_install_7

 

Нажимаем OK

На этом установка и настройка готова. Можно приступать к созданию программ для Android.

Основная информация по Android может быть найдена на официальном сайте: code.google.com/android

Автор: vovkab
Источник: Android Team