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