Go to content
Your browser is out of date. It has known security flaws and may not display all features of this websites. Learn how to update your browser[Закрыть]

Старые поделки


Разработка под Android - Часть 5: DroidDraw и Создание сборщика информации


Автор: Серый череп



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

Начнем с загрузки DroidDraw  и запуска программы с помощью загруженного .exe файла после извлечения из архива. Сам интерфейс программы довольно прост и понятен. Левая часть программы представляет собой дисплей аппарата, который показывает, как будет выглядеть работающая программа непосредственно на устройстве. Правая часть окна предоставляет собой набор объектов, которые можно поместить на экран в верхней части, и отображает генерированный код в нижней части. Эта программа уникальна по принципу своей работы: вы не можете выбрать только генерацию кода в нижней части правой панели, также нельзя вставить код уже существующего макета с целью улучшить его структуру и внешний вид. Скриншоты, приведенные ниже, дают представление о макете, облегчает понимание дальнейших объяснений:

В первую очередь мы поменяем параметр absolute layout на экране на ScrollView. Для этого нужно в левой верхней части окна программы открыть выпадающее меню RootLayout и выбрать ScrollView. Затем необходимо добавить LinearLayout на экран, перейдя на вкладку Layouts в правой верхней части окна. Перетяните кнопку LinearLayout, нажав на ней левой кнопкой мыши, на экран слева. Оставив его выбранным, перейдите на вкладку под названием Properties (опции) и нажмите на нее. Мы изменим ширину и высоту LinearLayout на fill_parent и затем подтвердим изменения, нажав Apply (применить). Вернувшись на вкладку Layouts, выберем TableLayout и перетянем его в LinearLayout. После этого во вкладке Properties изменим высоту и ширину на fill_parent.

Покончив с настройками макета, начнем переносить виджеты на экран, для большей интерактивности интерфейса. Для начала нужна метка, которая будет запрашивать номер. Используя drag-and-drop, перетяните TextView из панели виджетов на экран, затем перейдите на вкладку опций (Properties) и поменяйте параметр ширины (width) на fill_parent, а параметр text на Number:. Теперь нужно добавить текстовое поле (TextBox или EditText) под меткой. Мы также переносим ее с панели виджетов на экран и меняем параметр ширины на fill_parent, а параметр текста на пустое поле (blank field). В значении id нужно указать @+id/NumRewards. Добавляем еще один Label, с шириной fill_parent и текстом Date: и нажимаем Apply (применить). Теперь нам нужна кнопка, которая бы извлекала информацию из формы при нажатии. Ее тоже перетаскиваем на экран из виджетов, меняем параметр id на @+id/Add, а текст на Add и нажимаем Apply. На данном этапе осталось добавить еще две метки и два текстовых поля. Текст первой метки надо изменить на Numbers For This Month:. Параметр id первого текстового поля нужно изменить на @+id/RewardsMonthly. Вторая метка будет называться Numbers To Date:, а id второго текстового поля будет иметь значение @+id/RewardsTD. Ниже будет приведен код программы на случай, если что-нибудь пойдет не так. Но для лучшего понимания DroidDraw лучше самостоятельно поиграться с его интерфейсом.

Как только наш интерфейс скомпонован, нужно сгенерировать его код, нажав кнопку Generate вверху окна программы. После нажатия этой кнопки правая нижняя часть экрана заполнится кодом, его можно скопировать и вставить в файл main.xml.  На скриншоте ниже вы заметите, что текстовые поля неактивны. Это обеспечивается добавлением строки android:enabled="false" перед закрывающим знаком > в узлах  EditText. Таким образом можно просто отображать информацию без возможности ее изменения кем-либо из пользователей. Ниже приведен код макета, сгенерированный DroidDraw без добавления параметра enabled:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView
android:id="@+id/widget33"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<LinearLayout
android:id="@+id/widget29"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<TableLayout
android:id="@+id/widget34"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/widget35"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Number:"
>
<!--<span class="hiddenSpellError" pre=""-->TextView>
<EditText
android:id="@+id/NumRewards"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:phoneNumber="true"
>
<!--<span class="hiddenSpellError" pre=""-->EditText>
<TextView
android:id="@+id/widget37"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Date:"
>
<!--<span class="hiddenSpellError" pre=""-->TextView>
<DatePicker
android:id="@+id/Date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<!--<span class="hiddenSpellError" pre=""-->DatePicker>
<Button
android:id="@+id/Add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
>
</Button>
<TextView
android:id="@+id/widget41"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Numbers For This Month:"
>
</TextView>
<EditText
android:id="@+id/RewardsMonthly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:enabled="false"
>
<!--<span class="hiddenSpellError" pre=""-->EditText>
<TextView
android:id="@+id/widget43"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Numbers To Date:"
>
</TextView>
<EditText
android:id="@+id/RewardsTD"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:enabled="false"
>
<!--<span class="hiddenSpellError" pre=""-->EditText>
</TableLayout>
</LinearLayout>
</ScrollView>

А вот скриншоты с добавленным параметром enabled=”false” в файл макета:

Так как макет завершен, можно приступать к созданию проекта. Этот процесс уже был подробно описан, так что на этот раз сделайте это самостоятельно. После того, как проект создан, можно начать писать код для адаптера к базе данных, которую мы будем активно использовать. Большая часть кода будет стандартной, кроме функций select, с помощью которых мы будем отбирать данные по определенным критериям. Вот собственно код для файла DBAdapter.java:

package com.gregjacobs.infotracker;
/**
 * @author Greg
 *
 */
import java.util.Calendar;
import java.util.Date;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
 
public class DBAdapter
{
	public static final String KEY_ROWID = "_id";
    public static final String KEY_YEAROFREWARDS = "YearOfRewards";
    public static final String KEY_MONTHOFREWARDS = "MonthOfRewards";
    public static final String KEY_DAYOFREWARDS = "DayOfRewards";
    public static final String KEY_NUMOFREWARDS = "NumOfRewards";
    private static final String TAG = "DBAdapter";
 
    private static final String DATABASE_NAME = "BlockbusterRewards";
    private static final String DATABASE_TABLE = "tblRewards";
    private static final int DATABASE_VERSION = 1;
 
    private static final String DATABASE_CREATE =
        "create table tblRewards (_id integer primary key autoincrement, "
        + "YearOfRewards text not null, MonthOfRewards text not null," +
        		"DayOfRewards text not null," +
        		" NumOfRewards int not null );";
 
    private static final Date date = new Date();
    private final Context context;
 
    private DatabaseHelper DBHelper;
    private SQLiteDatabase db;

Вышеуказанный код во многом нам знаком из предыдущих проектов. Единственное, на что я бы хотел обратить внимание, так это на наличие колонок отдельно для дня, месяца и года, вместо объединения их в одну строку. Я умышленно предусмотрел такую структуру, чтобы при необходимости ссылаться на день и месяц без нагромождения кода в операторах SQL. Остальная часть кода должна быть понятна.

    public DBAdapter(Context ctx)
    {
        this.context = ctx;
        DBHelper = new DatabaseHelper(context);
    }
 
	private static class DatabaseHelper extends SQLiteOpenHelper
    {
        DatabaseHelper(Context context)
        {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
 
        @Override
        public void onCreate(SQLiteDatabase db)
        {
            db.execSQL(DATABASE_CREATE);
        }
 
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion,
                              int newVersion)
        {
            Log.w(TAG, "Upgrading database from version " + oldVersion
                  + " to "
                  + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS titles");
            onCreate(db);
        }
    }
 
    //---открывает базу данных---
    public DBAdapter open() throws SQLException
    {
        db = DBHelper.getWritableDatabase();
        return this;
    }
 
    //---закрывает базу данных---
    public void close()
    {
        DBHelper.close();
    }
 
    //---вставляет заголовок в БД---
    public long insertRewards(String yearRewards, String monthRewards, String dayRewards, String numRewards)
    {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_YEAROFREWARDS, yearRewards);
        initialValues.put(KEY_MONTHOFREWARDS, monthRewards);
        initialValues.put(KEY_DAYOFREWARDS, dayRewards);
        initialValues.put(KEY_NUMOFREWARDS, Integer.parseInt(numRewards));
        return db.insert(DATABASE_TABLE, null, initialValues);
    }
 
    public int getAllRewards()
    {
        Cursor cursor = db.rawQuery(
                    "SELECT SUM(NumOfRewards) FROM tblRewards", null);
                if(cursor.moveToFirst()) {
                    return cursor.getInt(0);
                }
                return cursor.getInt(0);
 
    }
 
    public int getMonthlyRewards()
    {
 
        Cursor cursor = getMonthRewards();/*db.rawQuery(
                    "SELECT SUM(NumOfRewards) FROM tblRewards WHERE MonthOfRewards = " +
                    Calendar.MONTH + " AND YearOfRewards = " + Calendar.YEAR, null);*/
                if(cursor.moveToFirst()) {
                    return cursor.getInt(0);
                }
                return cursor.getInt(0);
 
    }
 
  //---возвращает все заголовки---
    public Cursor getMonthRewards()
    {
    	Calendar cal = Calendar.getInstance();
    	int year = cal.get(Calendar.YEAR);
    	String WHERE = "MonthOfRewards = " + date.getMonth() + " AND YearOfRewards = " + year;
        return db.query(DATABASE_TABLE, new String[] {
        		"SUM(" + KEY_NUMOFREWARDS + ")"},
                WHERE,
                null,
                null,
                null,
                null);
    }
}

getMonthlyRewards собирает все записи в методе getMonthRewards(), относящиеся к определенному месяцу и отображает их в одном из текстовых полей под кнопкой, которую мы используем в макете. Метод getAllRewards() будет вызываться для отображения сбора записей, относящихся к определенной дате.Далее мы переходим на файл InfoTracker.java. В нем будет содержаться код, который присоединяет слушатель событий к кнопке в нашем макете. При ее нажатии будут обновляться текстовые поля под кнопкой. Код в этом .java файле не оптимизирован, он просто выполняет свои функции. Моей целью было показать вам понятную реализацию нужных функций, и если сам код вам не очень нравится, можете его оптимизировать на свой лад. Вот сам код для InfoTracker.java:

package com.gregjacobs.infotracker;
 
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Toast;
 
public class InfoTracker extends Activity {
    /** Called when the activity is first created. */
	DBAdapter db = new DBAdapter(this);
	EditText rewards;
	EditText rewardsMonthly;
	EditText rewardsTD;
	DatePicker dateRewards;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	try
    	{
	        super.onCreate(savedInstanceState);
	        setContentView(R.layout.main);
	        rewardsTD = (EditText)findViewById(R.id.RewardsTD);
	        rewardsMonthly = (EditText)findViewById(R.id.RewardsMonthly);
	        db.open();
	        rewardsTD.setText(String.valueOf(db.getAllRewards()));
	        rewardsMonthly.setText(String.valueOf(db.getMonthlyRewards()));
	        db.close();
	        // Capture our button from layout
	        Button button = (Button)findViewById(R.id.Add);
	        // Register the onClick listener with the implementation above
	        button.setOnClickListener(mAddListener);
 
    	}
        catch (Exception ex)
        {
      	  Context context = getApplicationContext();
      	  CharSequence text = ex.toString();
      	  int duration = Toast.LENGTH_LONG;
 
      	  Toast toast = Toast.makeText(context, text, duration);
      	  toast.show();
      	  //System.out.println(ex.getMessage());
        }
    }
 
    // Create an anonymous implementation of OnClickListener
    private OnClickListener mAddListener = new OnClickListener() {
        public void onClick(View v) {
        long id = 0;
          // do something when the button is clicked
    	db.open();
    	try{
    		//setContentView(R.layout.main);
			rewards = (EditText)findViewById(R.id.NumRewards);
		    dateRewards = (DatePicker)findViewById(R.id.Date);
		    rewardsMonthly = (EditText)findViewById(R.id.RewardsMonthly);
		    rewardsTD = (EditText)findViewById(R.id.RewardsTD);
		    id = db.insertRewards(String.valueOf(dateRewards.getYear()),
		    		String.valueOf(dateRewards.getMonth()),
		    		String.valueOf(dateRewards.getDayOfMonth()),
		    		rewards.getText().toString());
		    rewardsTD.setText(String.valueOf(db.getAllRewards()));
            rewardsMonthly.setText(String.valueOf(db.getMonthlyRewards()));
          }
          catch (Exception ex)
          {
        	  Context context = getApplicationContext();
        	  CharSequence text = ex.toString() + "ID = " + id;
        	  int duration = Toast.LENGTH_LONG;
 
        	  Toast toast = Toast.makeText(context, text, duration);
        	  toast.show();
        	  //System.out.println(ex.getMessage());
          }
 
          db.close();
 
        }
    };
}

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

Последний кусок работы предстоит проделать в файле strings.xml, вот его код:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Information Tracker</string>
</resources>

Ниже приведены ссылки на файлы из моего проекта для сравнения:

DBAdapter.java | InfoTracker.java | Main.xml

Мы закончили создание сборщика информации и получили базовые знания по работе с DroidDraw. Это предпоследняя часть нашего обучающего курса по разработке под Android, остался всего один урок. В нем мы запакуем приложение в .apk файл для установки непосредственно в Android-сматрфон или размещения на Android Market. До следующего поста!