Para finalizar os tutoriais de introdução a android, era impensável não se falar de Base de Dados. O tutorial irá usar como base o tema de clubes de futebol que temos vindo a utilizar. Para isso, iremos adicionar 3 clubes inicialmente na base de dados e, depois iremos demonstrar como é que se carregam os dados, como adicionar e/ou remover um clube.
Gradle file
O primeiro grande passo para o tutorial, uma vez que vamos usar sqlcipher é o leitor dirigir-se ao ficheiro gradle do módulo e, nas dependencias acrescentar a seguinte:
dependencies {
(...)
compile 'net.zetetic:android-database-sqlcipher:3.3.1-1@aar'
}Isto irá permitir o utilizador utilizar o sqlcipher, fazendo o import do mesmo.
Classe da Base Dados
O próximo passo será criar uma classe que nos irá servir de apoio para todo o processo. Para realizar isso, o utilizador deverá acrescentar a classe da seguinte forma e com o nome de classe a ser “BaseDados”:

Esta classe irá dar-nos todo o apoio necessário. Irá conter o nome da base de dados, o nome das tabelas e seus respectivos campos. Também irá neste caso criá-la caso ainda não exista e, preenche-la com dados que iremos inserir manualmente. A classe deverá ter o seguinte aspecto:
import android.content.ContentValues;
import android.content.Context;
// Use import android.database.sqlite.SQLiteDatabase;
// import android.database.sqlite.SQLiteOpenHelper; for normal support, with no encryption
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
public class BaseDados extends SQLiteOpenHelper {
private static final String PASS_HARDCODED =
"hard-coded password - Nao fazer isto em ambientes de producao";
private static final String NOME_BASEDADOS = "clubes.db";
private static final int SCHEMA = 1;
//We could put those on a contract class container...
static final String NOME = "nome";
static final String SIGLA = "sigla";
static final String TABELA = "clubes";
//SCHEMA Version is 1
public BaseDados(Context context) {
super(context, NOME_BASEDADOS, null, SCHEMA);
SQLiteDatabase.loadLibs(context);
}
//Called on db creation, thanks to getReadableDatabase or getWriteableDatabase
@Override
public void onCreate(SQLiteDatabase db) {
//Table creation !!!
try {
db.beginTransaction();
db.execSQL("CREATE TABLE clubes (nome TEXT, sigla TEXT);");
ContentValues cv = new ContentValues();
cv.put(NOME, "Futebol Clube do Porto");
cv.put(SIGLA, "FCP");
db.insert(TABELA, NOME, cv);
cv.put(NOME, "Sport Lisboa e Benfica");
cv.put(SIGLA, "SLB");
db.insert(TABELA, NOME, cv);
cv.put(NOME, "Sporting Clube de Portugal");
cv.put(SIGLA, "SCP");
db.insert(TABELA, NOME, cv);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
throw new RuntimeException("Erro");
}
SQLiteDatabase getReadableDatabase() {
return (super.getReadableDatabase(PASS_HARDCODED));
}
SQLiteDatabase getWritableDatabase() {
return (super.getWritableDatabase(PASS_HARDCODED));
}
}Layouts
O layout da activity_main irá ser bastante simples, irá ter um spinner para mostrar dados e dois botões, um para adicionar e outro para remover os clubes. Para uma primeira fase, os botões estarão inactivos. O layout deverá ter o seguinte aspecto:
<Spinner
android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Adicionar Clube"
android:id="@+id/btnAdd"
android:layout_below="@+id/spinner"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="45dp"
android:onClick="AdicionarClube"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remover Clube"
android:id="@+id/delete"
android:layout_below="@+id/btnAdd"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="45dp"
android:onClick="RemoverClube"/>O próximo passo deverá ser criar um ficheiro .xml com o nome de row. Este ficheiro irá indicar a estrutura do tipo de dados que nós queremos mostrar. Neste caso, será mostrar o nome e a sigla do clube, logo dois “TextView” serão necessários. O código será o seguinte:
<TextView
android:id="@+id/Nome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
/>
<TextView
android:id="@+id/Sigla"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
/>MainActivity
Para finalizar a fase inicial de bases de dados, o código java da MainActivity deverá ter o aspecto indicado em baixo. Importa clarificar o seguinte:
- Cursor current -> cursor com os dados actual
- BaseDados bd -> Base de dados criada
- AsyncTask task -> uma tarefa que irá correr “de parte” para inserir/eliminar dados. Esta é importante, sempre que queremos fazer uma ação à Base de Dados deveremos usar AsyncTasks para não haver conflitos com a thread principal da aplicação
- Spinner meuSpinner -> spinner de dados. Se o leitor não sabe o que é um spinner, poderá ver aqui.
- Classe BaseTask -> será a classe que irá correr as AsyncTask para operar sobre a base de dados e, a mesma terá uma função de Load sobre os dados que já contem na base de dados.
public class MainActivity extends AppCompatActivity {
private Cursor current = null;
private BaseDados bd = null;
private AsyncTask task = null;
Spinner meuSpinner = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(this, R.layout.row,
current, new String[]{
BaseDados.NOME,
BaseDados.SIGLA},
new int[]{R.id.Nome, R.id.Sigla},
0);
meuSpinner = (Spinner) findViewById(R.id.spinner);
meuSpinner.setAdapter(adapter);
if (current == null) {
bd = new BaseDados(this);
task = new LoadCursorTask().execute();
}
}
abstract private class BaseTask<T> extends AsyncTask<T, Void, Cursor> {
@Override
public void onPostExecute(Cursor result) {
((CursorAdapter) meuSpinner.getAdapter()).changeCursor(result);
current = result;
task = null;
}
//Check http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String)
//We are also retrieving the ROWID, apart from the title and value columns
//Return results ordered by title column
protected Cursor doQuery() {
Cursor result =
bd
.getReadableDatabase()
.query(BaseDados.TABELA,
new String[]{"ROWID AS _id",
BaseDados.NOME,
BaseDados.SIGLA},
null, null, null, null, BaseDados.NOME);
result.getCount();
return (result);
}
}
//Wrapper classes for insertion and data loading
private class LoadCursorTask extends BaseTask<Void> {
@Override
protected Cursor doInBackground(Void... params) {
return (doQuery());
}
}
}O leitor pode agora correr a aplicação e verificar que o spinner criado se encontra preenchido com o nome dos 3 clubes que adicionámos no content values.
Remover Clubes
Para criar o remover, o utilizador deverá acrescentar código na classe BaseTask para criar uma tarefa de remoção.
Para isso, logo a seguir ao método LoadCursorTask, deverá acrescentar o seguinte:
private class DeleteTask extends BaseTask<Cursor> {
@Override
protected Cursor doInBackground(Cursor... values) {
String rowid=values[0].getString(values[0].getColumnIndex("_id"));
bd.getWritableDatabase().delete(BaseDados.TABELA,
"ROWID" + "=" + rowid, null);
return(doQuery());
}
}Esta função deverá depois ser chamada quando o botão de remoção é utilizado. Para isso o leitor deverá criar um método para o botão de remover e codificá-lo de forma a que consiga ir buscar o item do spinner e eliminá-lo da seguinte forma:
public void RemoverClube(View view) {
final Cursor cr = (Cursor) meuSpinner.getSelectedItem();
task=new DeleteTask().execute(cr);
}Adicionar Clubes
Neste ponto, iremos ser mais “preguiçosos” e adicionar novamente manualmente. O leitor poderá criar edit text para ir buscar o texto e adicionar vários clubes.
Da mesma forma que criámos uma task para o delete, teremos de criar outra para a inserção, da seguinte forma:
private class InsertTask extends BaseTask<ContentValues> {
@Override
protected Cursor doInBackground(ContentValues... values) {
bd.getWritableDatabase().insert(BaseDados.TABELA,
BaseDados.NOME, values[0]);
return(doQuery());
}
}Para dicionar, terá de criar um Content Values para inserir 2 valores (nome e sigla dos clubes) e enviá-lo para a InsertTask da seguinte forma (isto no evento do botão adicionar):
public void AdicionarClube(View view) {
ContentValues values=new ContentValues(2);
values.put(BaseDados.NOME, "Sporting Clube de Braga");
values.put(BaseDados.SIGLA, "SCB");
task=new InsertTask().execute(values);
}O leitor poderá correr a aplicação e, terá uma base de dados operacional para adicionar e remover clubes de futebol.
