Conceitos básicos para o desenvolvimento de uma aplicação para a plataforma Android

Nesse artigo, veremos os passos mais rudimentares para desenvolver uma aplicação android que tenha apenas o básico do básico de uma aplicação: entrada e saída de dados; desenvolveremos uma aplicação com apenas dois formulários, um para inserir os dados a serem salvos na aplicação e e outro, um ListView, para mostrar os dados inseridos.

Antes de continuar esse tutorial, você precisa configurar o seu ambiente de desenvolvimento com o Eclipse eu plug-in ADT. Para ver como fazer isso, leia esse artigo.
O primeiro passo em nossa empreitada é criar um novo projeto no Eclipse. Vá para File > New > Projetc… e, na caixa de dialogo que aparecerá, escolha Android Project e clique em Next, digite um nome para o projeto, clique em Next, escolha a plataforma alvo e na última tela informe um nome para o pacote que armazenará o código fonte de sua aplicação. Para finalizar clique em Finish.
Você obterá um novo projeto com uma estrutura similar a essa:

 
Nesse projeto, além do layout principal (main.xml), iremos criar mais dois (input.xml e output.xml). O layout main.xml terá como elemento raiz TabHost, que define um layout baseado em abas. No nosso caso, teremos duas abas, que serão definidas pelos layouts input.xml e output.xml.
Para criar um novo layout, clique com o botão direito no nome do projeto e selecione New > Other. Na caixa de dialogo que aparece, selecione a opção Android XML Layout File, e na próxima tela informe um nome para o arquivo e escolha uma das opções para o Root Element. No exemplo descrito aqui, os dois layouts que criaremos devem ter o elemento raiz configurado para TabHost.
O layout main.xml deve ter o seguinte conteúdo:
 

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent">
<FrameLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content">
</TabWidget>
<FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="fill_parent">
</FrameLayout>
</FrameLayout>
</TabHost>

 
Os layouts input.xml e output.xml devem ter, respectivamente, os seguintes conteúdos:
input.xml
Esse layout será composto de uma série de campos de formulário, dispostos em pares (um label com seu correspondente textfield ou um spinner, que é uma lista de opções que devem ser selecionadas. O spinner será preenchido com itens salvos no banco de dados. O arquivo input.xml deve ter o seguinte formato:
 

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TableRow
android:id="@+id/tableRow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nome:"
android:textAppearance="?android:attr/textAppearanceLarge" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10" >
<requestFocus />
</EditText>
</TableRow>
<TableRow
android:id="@+id/tableRow2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ok" />
</TableRow>
</TableLayout>

 
output.xml
Esse layout será uma lista de itens que estarão salvos no banco de dados. Ao ser iniciado, portanto, a Activity associada a esse layout deve puxar a lista de itens que devem ser exibidos quando for iniciada. O arquivo terá o seguinte formato:
 

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

 
Para cada layout da aplicação, iremos associar uma Activity para ela. Uma Activity é uma classe que possui uma entrada no arquivo de manifesto (AndroidManifest.xml) da aplicação. Para criar uma nova Activity, clique com o botão direito no nome do projeto e selecione New > Class. Na caixa de dialogo, que aparece, você precisa preencher os campos Name com o nome da classe. No nosso exemplo, criaremos mais dois Activity, que serão nomeados como InputActivity e OutputActivity; também criaremos uma classe chamada DataHelper, que não terá uma entrada no manifesto, apenas servirá para comunicação com o banco de dados.
A Activity associada ao layout output.xml deve seguir o seguinte formato:
 

public class HelloListViewActivity extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] countries = getResources().getStringArray(R.array.countries_array);
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, countries));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), ((TextView) arg1).getText(), Toast.LENGTH_SHORT).show();
}
});
}
}

 
A parte importante do código acima reside nas seguintes linhas:
 

String[] countries = getResources().getStringArray(R.array.countries_array);
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, countries));

 
Nesse exemplo, o código está utilizando uma lista salva em um arquivo xml localizado no diretório res/ do projeto; esse código deve ser substituído para ler os dados em uma banco de dados.
A Activity associada ao layout input.xml deve seguir o seguinte formato:
 

public class HelloAndroidActivity extends Activity {
/** Called when the activity is first created. */
private EditText input;
private Button okButton;
private TextView output;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.input = (EditText) this.findViewById(R.id.editText1);
this.okButton = (Button) this.findViewById(R.id.button1);
this.output = (TextView) this.findViewById(R.id.textView2);
this.okButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
output.setText("Hello" + input.getText());
}
});
}
}

 
A parte importante do código acima reside nas seguintes linhas:
 

this.okButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
output.setText("Hello" + input.getText());
}
});

 
No exemplo, a saída é mostrada em uma campo de texto do formulário; esse código deve ser substituído para salvar os dados no banco de dados.
O último Activity, main.xml, definirá o comportamento da tela principal da aplicação, e deve seguir o seguinte formato:
 

public class CustomTab extends TabActivity {
private TabHost mTabHost;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTabHost = (TabHost) findViewById(android.R.id.tabhost);
setupTab(new TextView(this), "Tab 1");
setupTab(new TextView(this), "Tab 2");
setupTab(new TextView(this), "Tab 3");
setupTab(new TextView(this), "Tab 4");
}

 
Repare que no nosso exemplo, estamos usando duas funções auxiliares, setupTab e createTabView. O formato dessas funções é o seguinte:
setupTab(…)
 

private void setupTab(final View view, final String tag) {
View tabview = createTabView(mTabHost.getContext(), tag);
TabSpec setContent = mTabHost.newTabSpec(tag).setIndicator(tabview).setContent(new TabContentFactory() {
public View createTabContent(String tag) {return view;}
});
mTabHost.addTab(setContent);
}

 
 
createTabView(…)
 
 

private static View createTabView(final Context context, final String text) {
View view = LayoutInflater.from(context).inflate(R.layout.tabs_bg, null);
TextView tv = (TextView) view.findViewById(R.id.tabsText);
tv.setText(text);
return view;
}

 
 
Para criar as abas usadas em nosso exemplo, alguns arquivos extras são necessários. São eles:
 
drawable/tab_bg_selected.xml
 
 

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:startColor="#A8A8A8" android:centerColor="#7F7F7F"
android:endColor="#696969" android:angle="-90" />
</shape>

 
 
drawable/tab_bg_unselected.xml
 
 

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:startColor="#5C5C5C" android:centerColor="#424242"
android:endColor="#222222" android:angle="-90" />
</shape>

 
 
drawable/tab_bg_selector.xml
 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Active tab -->
<item android:state_selected="true" android:state_focused="false"
android:state_pressed="false" android:drawable="@drawable/tab_bg_selected" />
<!-- Inactive tab -->
<item android:state_selected="false" android:state_focused="false"
android:state_pressed="false" android:drawable="@drawable/tab_bg_unselected" />
<!-- Pressed tab -->
<item android:state_pressed="true" android:drawable="@android:color/transparent" />
<!-- Selected tab (using d-pad) -->
<item android:state_focused="true" android:state_selected="true"
android:state_pressed="false" android:drawable="@android:color/transparent" />
</selector>

 
 
drawable/tab_text_selector.xml
 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/white" />
<item android:state_focused="true" android:color="@android:color/white" />
<item android:state_pressed="true" android:color="@android:color/white" />
<item android:color="#f8f8f8" />
</selector>

 
layout/tabs_bg.xml
 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tabsLayout" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="@drawable/tab_bg_selector"
android:padding="10dip" android:gravity="center" android:orientation="vertical">
<TextView android:id="@+id/tabsText" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Title"
android:textSize="15dip" android:textColor="@drawable/tab_text_selector" />
</LinearLayout>

 
 
No caso especifico do Activity, depois de cria-la, precisamos adicionar uma entrada no arquivo de manifesto relacionada a ele. Essa entrada tem o seguinte formato:
 

<activity
android:name=".HelloListViewActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

 

Os campos android:name e android:label devem ser únicos, e os elementos intent-filter definem características da Activity. Para obter mais informações sobre os intent-filter, veja esse artigo.

Por fim, devemos criar uma classe DataHelper.java que fará a comunicação com o banco de dados, lendo e escrevendo dados nele. Essa classe seguirá o seguinte formato:

 

public class DataHelper {
private static final String DATABASE_NAME = "example.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "table1";
private Context context;
private SQLiteDatabase db;
private SQLiteStatement insertStmt;
private static final String INSERT = "insert into " + TABLE_NAME + "(name) values (?)";
public DataHelper(Context context) {
this.context = context;
OpenHelper openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
this.insertStmt = this.db.compileStatement(INSERT);
}
public SQLiteDatabase getDb() {
return this.db;
}
public long insert(String name) {
this.insertStmt.bindString(1, name);
return this.insertStmt.executeInsert();
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
public List<String> selectAll() {
List<String> list = new ArrayList<String>();
Cursor cursor = this.db.query(TABLE_NAME, new String[] { "name" }, null, null, null, null, "name desc");
if (cursor.moveToFirst()) {
do {
list.add(cursor.getString(0));
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return list;
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY, name TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("Example", "Upgrading database, this will drop tables and recreate.");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}