Saltar para conteúdo


Foto
- - - - -

Colocar dados de SQLite numa listview


  • Por favor inicie sessão para responder
16 respostas a este tópico

#1 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 24 July 2012 - 18:09

Olá pessoa.

Estou com umas dúvidas e venho aqui tentar esclarecer qual a melhor maneira para proceder.

Tenho uma base de dados, com mais de uma tabela e cada tabela com várias colunas.
Neste momento, estou a fazer a query à bd através de uma função que devolve o cursor com essa query.
A nível de exemplo, algo como:
public Cursor getGroupData() {
String[] colunas = new String[] {_ROWID, _NAME};
Cursor c = BaseDados.query(BD_TABLE, colunas, null, null, null, null, null);
return c;
}

Queria que desse resultado, um dos campos fosse utilizado numa listview. Tenho duas situações.
1ª Situação:
Tendo os resultados nessa listview, ao carregar numa das linhas, abre uma actividade.
2ª Situação:
Tendo os resultados nessa listview, ao carregar numa das linhas, é feita nova query à bd, devolvido novo cursor, apresentado numa nova listview e ao carregar numa das linhas abre uma actividade.

Aquilo que neste momento tenho feito para a primeira situação é algo como:
  Cursor values = baseDados.getGroupData();
  // Aqui provavelmente colocaria o código para percorrer o cursor, retirar os dados que pretendo mostrar na listview e armazená-los num ArrayList de Strings.
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Dados_extraídos_do_cursor);
  setListAdapter(adapter);
Como para a primeira situação a posição da string no array pode ser usada não estou muito preocupado mas como posso fazer para a segunda situação?
O cursor vai devolver para cada registo vários campos, sendo que alguns desses campos podem ser usados para a segunda query que vai retornar os dados a apresentar na segunda listview.

Como recomendam que deva proceder?

Obrigado,

kodiak

#2 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 24 July 2012 - 19:12

Primeiro:
Aconselho-te a ver o CursorAdapter e o CursorLoader
Escusas de andar a criar "DataSources" temporárias...

Segundo e respondendo à questão:
Tens uma listagem principal e das duas uma, ou abres outra listagem ou abres uns "detalhes" digamos.
Portanto, o que tens é:
ListActivity -> Activity
L1 A1
ou
ListActivity -> ListActivity -> Activity
L1 L2 A1

Aqui podes usar duas ListActivity distintas (como o exemplo em cima) passando algum parametro para a segunda de maneira a que ela possa saber que query fazer e depois abrir a Activity normal quando for necessário, ou
fazeres algo do tipo:
L1 -> L1 -> A1
Usando a mesma ListActivity mas mudando só o cursor em que é baseada a Lista. Desta forma poupas uma Activity mas tens que ter em atenção que tens de ser tu proprio a manter a "Back History" porque visto ser sempre a mesma, o utilizador vai esperar que quando carregar no "Back" que lhe seja mostrado a lista anterior, e não a activity anterior.

Pessoalmente, aconselho a 1ª por ser mais facil entender, embora na 2ª poupes código.
  • pedronveloso gosta disto

#3 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 25 July 2012 - 08:18

Primeiro:
Aconselho-te a ver o CursorAdapter e o CursorLoader
Escusas de andar a criar "DataSources" temporárias...

Segundo e respondendo à questão:
Tens uma listagem principal e das duas uma, ou abres outra listagem ou abres uns "detalhes" digamos.
Portanto, o que tens é:
ListActivity -> Activity
L1 A1
ou
ListActivity -> ListActivity -> Activity
L1 L2 A1

Aqui podes usar duas ListActivity distintas (como o exemplo em cima) passando algum parametro para a segunda de maneira a que ela possa saber que query fazer e depois abrir a Activity normal quando for necessário, ou
fazeres algo do tipo:
L1 -> L1 -> A1
Usando a mesma ListActivity mas mudando só o cursor em que é baseada a Lista. Desta forma poupas uma Activity mas tens que ter em atenção que tens de ser tu proprio a manter a "Back History" porque visto ser sempre a mesma, o utilizador vai esperar que quando carregar no "Back" que lhe seja mostrado a lista anterior, e não a activity anterior.

Pessoalmente, aconselho a 1ª por ser mais facil entender, embora na 2ª poupes código.


Olá r3pek.
Obrigado pela ajuda.

Começando pelo fim.
Acho que vou tentar fazer duas listviews distintas. Assim garanto mais facilmente a Back History.

Relativamente à parte dos cursores é que é pior. Pelos vistos o Cursor Adapter é deprecated e devemos usar o CursorLoader. Já tive a ler mas confesso que entendi pouco e não consegui perceber como vou fazer.

Vou bater mais um bocado com a cabeça e vamos ver se descubro.

kodiak

#4 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 26 July 2012 - 09:00

Atenção ao uso do CursorLoader. Só está disponível após API 11. Portanto, para APIs < 11 tens que usar o Cursor Adapter.

#5 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 27 July 2012 - 09:30

Atenção ao uso do CursorLoader. Só está disponível após API 11. Portanto, para APIs < 11 tens que usar o Cursor Adapter.


Olá r3pek.

Não tenho a certeza mas acho que não usei nem um nem outro. Pode não ser como deve ser feito mas está a funcionar como eu pretendia.

Criei uma nova classe que guarda os valores de cada resultado da query, neste caso o id e o nome.
A função baseDados.getGroupData() deixou de devolver um cursor mas sim uma Lista<classe que eu criei>. Antes de devolver o resultado, o cursor corre cada linha e adiciona os dados à nova classe.
Depois foi fazer:
List<classe_que_eu_criei> values = baseDados.getGroupData();
 
  ArrayAdapter<classe_que_eu_criei> adapter = new ArrayAdapter<classe_que_eu_criei>(this,
    android.R.layout.simple_list_item_1, values);
  setListAdapter(adapter);
Está a funcionar tal e qual como eu queria...

Obrigado pela ajuda,

kodiak

#6 Driver

Driver

    Membro

  • Membros
  • PipPip
  • 51 mensagens

Mensagem publicada 28 July 2012 - 10:22

Atenção ao uso do CursorLoader. Só está disponível após API 11. Portanto, para APIs < 11 tens que usar o Cursor Adapter.

Podes sempre utilizar o Support Package -> http://developer.and...rt-library.html

#7 Driver

Driver

    Membro

  • Membros
  • PipPip
  • 51 mensagens

Mensagem publicada 28 July 2012 - 10:28

Olá r3pek.

Não tenho a certeza mas acho que não usei nem um nem outro. Pode não ser como deve ser feito mas está a funcionar como eu pretendia.

Criei uma nova classe que guarda os valores de cada resultado da query, neste caso o id e o nome.
A função baseDados.getGroupData() deixou de devolver um cursor mas sim uma Lista<classe que eu criei>. Antes de devolver o resultado, o cursor corre cada linha e adiciona os dados à nova classe.
Depois foi fazer:

List<classe_que_eu_criei> values = baseDados.getGroupData();

ArrayAdapter<classe_que_eu_criei> adapter = new ArrayAdapter<classe_que_eu_criei>(this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
Está a funcionar tal e qual como eu queria...

Obrigado pela ajuda,

kodiak

Boas, mesmo estando a funcionar, aconselho-te a implementação em CursorAdapter ou CursorLoader para optimização de recursos. Esta implementação de copiar todos os resultados da Base de Dados para um Objecto tem um overhead mt grande em termos de memória e cpu( Para cada query, percorrer os resultados todos e copiá-los ), tens que ter em mente que tás a programar para um dispositivo com recursos limitados (Alimentado por Bateria). A utilização do CursorAdapter é semelhante à do SimpleAdapter, por isso não deves ter muitas chatisses em implementá-lo.

Cumps
Driver

Editado por Driver, 28 July 2012 - 10:30.


#8 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 28 July 2012 - 15:32

Boas, mesmo estando a funcionar, aconselho-te a implementação em CursorAdapter ou CursorLoader para optimização de recursos. Esta implementação de copiar todos os resultados da Base de Dados para um Objecto tem um overhead mt grande em termos de memória e cpu( Para cada query, percorrer os resultados todos e copiá-los ), tens que ter em mente que tás a programar para um dispositivo com recursos limitados (Alimentado por Bateria). A utilização do CursorAdapter é semelhante à do SimpleAdapter, por isso não deves ter muitas chatisses em implementá-lo.

Cumps
Driver


Imagino que sim mas estive a ver o CursorAdapter e não entendi o que é, o que faz e nem como o dedo implementar na minha situação

kodiak

#9 Driver

Driver

    Membro

  • Membros
  • PipPip
  • 51 mensagens

Mensagem publicada 28 July 2012 - 19:51

Imagino que sim mas estive a ver o CursorAdapter e não entendi o que é, o que faz e nem como o dedo implementar na minha situação

kodiak



Boas, um Cursor é o que é devolvido quando fazes uma Query à base de dados, o equivalente a um ResultSet em Java (uma tabela). A diferença do ArrayAdapter para o CursorAdapter é que o ArrayAdapter chama o toString() de cada objecto e coloca na TextView de cada elemento da ListView com a representação do objecto em String. O SimpleCursorAdapter (implementação simplificada do CursorAdapter), recebe 3 parâmetros para além do Context, são eles o Cursor (resultado da query), um Array de Strings que corresponde aos nomes das colunas, e um Array de inteiros, que corresponde aos Resourde ID's do Layout que representa cada elemento.
Podes ver um exemplo nestes links:
  • https://github.com/driverpt/PDM-1112SV-Yamba/blob/master/workspace/YambdaPDM/src/pt/isel/pdm/yamba/TimelineActivity.java
Vê no TimelineCursorUpdateTask como o update é feito. A inicialização do Adapter é feita no onCreate().
Isto foi uma aplicação que foi feita no contexto de uma cadeira da faculdade que frequentei este semestre ( Programação em Dispositivos Móveis ). Atenção que posso ter alguns bugs aí, mas podes tomar isso como exemplo, visto que funciona :P e é eficiente.

Cumps

#10 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 29 July 2012 - 09:24

........


Obrigado pela ajuda Driver.
Estive a ver o teu exemplo e ainda fiquei com dúvidas.
Dei com um exemplo na net parecido. Pode ser assim:



Cursor cursor =db.getData();
ArrayList<MyData> listData = new ArrayList<MyData>();
while (cursor.moveToNext()) {
	 MyData temp = new MyData();
	 temp.id = cursor.getInt(0);
	 temp.name = cursor.getString(1);
	 temp.age= cursor.getInt(2);
	 temp.phone= cursor.getInt(3);
	 listData .add(temp);
				 temp =null;
}
cursor.close();
MyAdapter myAdapter = new MyAdapter(this,
		 R.layout.list_item, listData); //use custom adapter
listView.setAdapter(myAdapter);
Class MyData:
private class MyData{
   int id ;
   String name;
   int age;
   int phone;
}
Class MyAdapter:
private class MyAdapter extends ArrayAdapter<projData> {
    private ArrayList<MyData> items;
    private Context context;
    public MyAdapter(Context context, int textViewResourceId,
		    ArrayList<MyData> items) {
	    super(context, textViewResourceId, items);
	    this.context = context;
	    this.items = items;
	  }
    public View getView(final int position, View convertView,
		    ViewGroup parent) {
	    View view = convertView;
	    if (view == null) {
		    LayoutInflater inflater = (LayoutInflater) context
				    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		    view = inflater.inflate(R.layout.list_item, null);
	    }
   
	    }
    return view;
    }
}

Pelo que percebi, isto faz aquilo que tinhas dito, recebe o contexto, o cursor e a view.
Mas já tendo o cursoradapter é necessário criar a classe MyData? Não vai usar demasiados recursos (memória)?

kodiak

#11 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 29 July 2012 - 10:39

Isso é exactamente o que ele disse para não fazer :)
A ideia é usar o Cursor directamente e não passar o resultado do Cursor para um ArrayList porque quando fazes isso estás a duplicar informação e portanto desperdicio de recursos (na cópia e em memória)

#12 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 29 July 2012 - 13:01

Isso é exactamente o que ele disse para não fazer :)
A ideia é usar o Cursor directamente e não passar o resultado do Cursor para um ArrayList porque quando fazes isso estás a duplicar informação e portanto desperdicio de recursos (na cópia e em memória)

Tb pensei nisso.

Imagina que não crio a classe MyData e que a query à bd devolve um cursor.

Posso usar a classe MyAdapter para fazer isso, isto é, sem muitas alterações?

kodiak

Editado por kodiak, 29 July 2012 - 13:03.


#13 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 29 July 2012 - 17:51

Tb pensei nisso.

Imagina que não crio a classe MyData e que a query à bd devolve um cursor.

Posso usar a classe MyAdapter para fazer isso, isto é, sem muitas alterações?

kodiak

A classe MyData é um exemplo e para entenderes podes substituir por String, Intenger, Float, etc.
O objectivo é usar o CursorLoader/CursorAdapter, portanto usando o cursor, e nunca usar ArrayLists para depois serem usadas num ArrayAdapter que contem cópias dos dados duma DB. Isso é que está mal.

#14 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 30 July 2012 - 10:49

Viva.
Antes de mais obrigado pelas vossas respostas e paciência.
Estou a dar em maluco que não consegui chegar lá.
Não é a maneira mais correcta mas como não consegui fazer com o CursorAdapter, fiz com o SimpleCursorAdapter (que já está deprecated).
Ficou assim:
baseDados.open();
  ListView listContent = (ListView)findViewById(android.R.id.list);
  Cursor query = baseDados.getData();
  startManagingCursor(query);
 
	   String[] from = new String[]{Database._GROUP_NAME};
	   int[] to = new int[]{android.R.id.text1};
	   SimpleCursorAdapter cursorAdapter =
	    new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, query, from, to);
	   listContent.setAdapter(cursorAdapter);
	  
	   baseDados.close();

Sei que deveria utilizar o CursorAdapter mas não consegui dar como o fazer.
Assim é melhor do que criar a classe que eu tinha criado e usar o ArrayList?

kodiak

#15 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 30 July 2012 - 11:30

Assim é muito, mas muito, melhor :)

#16 kodiak

kodiak

    Membro

  • Membros
  • PipPip
  • 167 mensagens

Mensagem publicada 30 July 2012 - 15:28

Assim é muito, mas muito, melhor :)


Estava a ver que não :)

E assim:


baseDados.open();
ListView listContent = (ListView)findViewById(android.R.id.list);
Cursor query = baseDados.getData();
MyAdapt cursorAdapter = new MyAdapt(this, query);

listContent.setAdapter(cursorAdapter);
	
	 baseDados.close();


Com a classe MyAdapt assim:
public class MyAdapt extends CursorAdapter {
private final LayoutInflater mInflater;
public MyAdapt(Context context, Cursor c) {
super(context, c);
mInflater = LayoutInflater.from(context);
mContext = context;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView groupName = (TextView) view.findViewById(android.R.id.text1);
groupName.setText(cursor.getString(cursor
.getColumnIndex(Database._GROUP_NAME)));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view = mInflater.inflate(
android.R.layout.simple_list_item_1, parent, false);
return view;
}
}

É melhor?
O construtor é deprecated mas tb está a funcionar...

kodiak

#17 r3pek

r3pek

    Guru de Android

  • Former Staff
  • PipPipPipPipPip
  • 1560 mensagens
  • LocalizaçãoBA4 - Terceira - Açores
  • Nexus One + Motorola XOOM

Mensagem publicada 30 July 2012 - 15:52

Basicamente é a mesma coisa, mas com mais código :)
Não adicionaste funcionalidade nenhuma, portanto, em principio, não seria necessário usares esse método. Pode ser necessário se precisares de fazer alguma coisa que o SimpleCursorAdapter não te faça.

De qualquer maneira, ambos os métodos estão correctos.