Páginas

Pesquisar neste blog

Mostrando postagens com marcador Android. Mostrar todas as postagens
Mostrando postagens com marcador Android. Mostrar todas as postagens

30 de setembro de 2011

Como Utilizar BroadcastReceiver para Iniciar uma Aplicação

Recentemente necessitei incluir em um projeto Android a possibilidade de fazer a aplicação ser inicializada logo após o boot do dispositivo.

Para este post criei uma aplicação que faz uso da classe BroadcastReceiver para iniciar uma Activity toda vez que o dispositivo faz um boot.

No projeto criei duas classes Principal e IniciarApp.

A classe Principal é a Activity que será exibida logo após o boot do dispositivo. Utilizei a Activity padrão que é criada pelo Eclipse quando criamos um projeto Android, e exibe a mensagem Hello World, Principal!

A classe IniciarApp é a classe que é inicializada quando recebe a mensagem de Boot do sistema. (Listagem 1)

Listagem 1:
public class IniciarApp extends BroadcastReceiver {
 
    @Override
    public void onReceive(Context ctx, Intent i) {
        
        // Exibe a Activity Principal após o boot
        // do Android
        Intent intent = new Intent(ctx, Principal.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        ctx.startActivity(intent);        
    }
}


A classe IniciarApp estende a classe BroadcastReceiver. O método onReceive(Context, Intent) é chamado quando a aplicação recebe a mensagem de broadcast de boot do sistema. Neste método é declarada a intent que vai exibir a Activity Principal. Neste caso é necessário configurar o flag Intent.FLAG_ACTIVITY_NEW_TASK, para que a aplicação seja inicializada como uma nova tarefa na pilha do stack das Activities.

Para registrar um BroadcastReceiver podemos utilizar a API ou publicar o receiver no arquivo AndroidManifest.xml. Neste caso é mais simples publicar pelo arquivo AndroidManifest.xml. (Listagem 2)

Listagem 2:
 <!-- Permissão necessária para receber a mensagem de boot completado -->
 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     
 <application android:icon="@drawable/icon" android:label="@string/app_name">
 
     <!-- Publicação do Broadcast -->
  <receiver android:name=".IniciarApp">
      <intent-filter>
          <action android:name="android.intent.action.BOOT_COMPLETED" />
          <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
  </receiver>
 
     <activity android:name=".Principal"
               android:label="@string/app_name">
         <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
     </activity>    
 </application>


No arquivo AndroidManifest.xml foi incluída a tag de permissão do usuário para receber a mensagem de boot completado, e também foi incluída a tag <receiver> que registra a classe IniciarApp como a classe que vai ser chamada quando a mensagem de boot do sistema for recebida.
Para testar, primeiro rode a aplicação uma vez no emulador para que o arquivo .apk da aplicação seja instalado, depois feche o emulador do Eclipse e abra novamente.

Quando o emulador finalizar o boot a Activity Principal será exibida apresentando a mensagem Hello World, Principal! Pronto agora a aplicação será inicializada após cada boot do sistema. Após os testes não esqueça de desinstalar a aplicação, senão toda vez que o emulador for inicializado vai apresentar a Activity Principal.

Uma observação importante é que esta utilização do BroadcastReceiver é muito particular e deve ser utilizada com cuidado, para que a experiência do usuário não seja prejudicada, porque desta forma a aplicação não segue o padrão das aplicações do Android.

A utilização recomendada para o BroadcastReceiver é trabalhar em conjunto com a classe Service e Notification. O ideal é que o BroadcastReceiver inicie um serviço em background e informe o usuário utilizando as mensagens de notificação do dispositivo.

23 de agosto de 2011

Como Customizar a Cor de Fundo de um Botão no Android

Neste post vou demonstrar como configurar a cor de fundo de um botão utilizando duas técnicas diferentes. Uma utilizando figuras com cor em degradê, para dar o efeito do botão sendo pressionado, e outra utilizando o mesmo efeito degradê só que, fazendo a configuração do botão utilizando um arquivo xml.

Para este exemplo criei um projeto com dois botões com cor em degradê como demonstrado na Figura 1.

background1
Figura 1
Para montar o layout dois dois botões foi utilizado o seguinte código no arquivo main.xml (Listagem 1):

Listagem 1:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
 
    <!-- View utilizada como separador -->
    <View
        android:layout_width="fill_parent" 
        android:layout_height="10sp"        
        android:id="@+id/view1"
     />
 
    <Button 
        android:id="@+id/btnJpg"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/view1"
        android:text="Design por Figura Jpg"
        android:background="@drawable/botao_azul_selector"
    />
    
        <!-- View utilizada como separador -->
    <View
        android:layout_width="fill_parent" 
        android:layout_height="10sp"        
        android:id="@+id/view2"
        android:layout_below="@+id/btnJpg"
     />
    
    <Button 
        android:id="@+id/btnXml"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/view2"
        android:text="Design por Arquivo Xml"
        android:background="@drawable/botao_verde_selector"
    />    
</RelativeLayout>


No primeiro botão btnJpg foi utilizado como cor de fundo duas figuras em degradê uma para quando o botão for pressionado e outra para quando o botão for solto. É necessário utilizar duas figuras para dar o efeito dinâmico do botão estar sendo pressionado.

Para configurar este efeito utilizei um recurso chamado de StateListDrawable que é um arquivo xml onde podemos configurar figuras diferentes para cada estado, por exemplo de um botão.
Este arquivo xml deve ser colocado em um diretório \drawable. O arquivo para configurar o primeiro botão é o botão_azul_selector (Listagem 2), configurado no atributo android:background="@drawable/botao_azul_selector".

Listagem 2:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
         android:state_pressed="true" 
        android:drawable="@drawable/botao_azul_on"
    />
    <item 
        android:drawable="@drawable/botao_azul_off"
    />
</selector>


No estado android:state_pressed foi utilizada a figura botao_azul_on para representar o botão quando pressionado e a figura botao_azul_off para representar o botão no estado não pressionado. As figuras do botão devem estar em um diretório \drawable.

No segundo botão btnXml foi utilizada uma outra técnica de configurar a cor de fundo do botão. O princípio é o mesmo, foi utilizado um arquivo Xml para configurar a aparência do botão em cada um dos estados (pressionado ou não), só que desta vez ao invés de utilizar figuras .jpg, foram utilizadas tags para construir a forma geométrica do botão. O arquivo botao_verde_selector também deve ser colocado em um diretório \drawable (Listagem 3):

Listagem 3:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" >
        <shape>
            <gradient
                android:startColor="@color/verde_claro" 
                android:endColor="@color/verde_escuro"
                android:angle="270" />
            <stroke
                android:width="1dp"
                android:color="@color/verde_escuro" />
            <corners
                android:radius="10dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
    <item>
       <shape>
            <gradient
                android:startColor="@color/verde_escuro" 
                android:endColor="@color/verde_claro"
                android:angle="270" />
            <stroke
                android:width="1dp"
                android:color="@color/verde_escuro" /> 
            <corners
                android:radius="10dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
</selector>

A aparência do botão verde é construída através da tag de forma <shape> cujo default é o retângulo. Dentro desta tag temos a tag <gradient> que define a cor degradê, a tag <stroke> que define a largura e a cor do contorno do botão, a tag <corners> que define o ângulo da borda arredondada do nosso botão e a tag <padding> que define o posicionamento do conteúdo e não da forma geométrica do botão.

Observe que foram criadas duas tags <item>, uma para o estado pressionado do botão e outra para o estado default do botão.
Para rodar a aplicação foi criada a Activity Principal (Listagem 4):

Listagem 4:
public class Principal extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

9 de agosto de 2011

Como Utilizar o ListView para Selecionar Vários Itens no Android

Neste post vou demonstrar como trabalhar com um ListView que permite a seleção de um ou vários itens, e como tratar o retorno dos itens selecionados.

Criei um projeto onde são exibidas as regiões do Brasil (Figura 1) na Activity principal e quando selecionada uma região é apresentada uma Activity filha (Figura 2) com a relação de estados pertencentes àquela região, com um checkbox que permite a seleção ou não de cada estado (Figura 2).

listviewmultiplechoice4

Quando são selecionados um ou mais estados na Activity filha (Tela com a relação dos estados da região selecionada – Figura 2) e retornamos para a Activity principal (Figura 1) é apresentada uma mensagem com os estados que foram selecionados (Figura 3). No exemplo foi selecionada a região Sudeste na Activity Principal e os estados Minas Gerais, Rio de Janeiro e São Paulo.

listviewmultiplechoice5

No projeto foram criadas duas classes para representar as duas Activities. A primeira classe é a Main.java (Listagem 1)

Listagem 1
public class Main extends ListActivity implements OnItemClickListener {
    private String[] regiao = new String[]{
            "Centro-Oeste", "Nordeste", "Norte",
            "Sudeste", "Sul"};
    private final static int ACTIVITY_STATE = 1;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        setListAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, regiao));
        
        ListView listView = getListView();        
        listView.setOnItemClickListener(this);
    }
 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, 
            int position, long id) {
        String regiao = (String) parent.getItemAtPosition(position);
        
        Intent intent = new Intent(this, StateListActivity.class);
        intent.putExtra("estados", getEstados(regiao));        
        startActivityForResult(intent, ACTIVITY_STATE);
        
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        // Verifica se o retorno veio da Activity chamada
        if (requestCode == ACTIVITY_STATE) {
            if (resultCode == RESULT_OK) {
                if (data != null) {
                    ArrayList<String> estados = data.getStringArrayListExtra("estados");
                    
                    StringBuilder message = new StringBuilder();
                    message.append("Estados selecionados:\n");
                    
                    for (int i = 0; i < estados.size(); i++) {
                            message.append(estados.get(i) + "\n");
                    }
                    
                    // Exibe o nome dos estados selecionados
                    Toast.makeText(this, message.toString(), Toast.LENGTH_SHORT).show();
                }
            }
        }                
        
    }
    
    private String[] getEstados(String regiao){
        
        if (regiao.equals("Centro-Oeste")) {
            return new String[]{
            "Goiás", "Mato Grosso", "Mato Grosso do Sul",
            "Distrito Federal"};
        }
        
        if (regiao.equals("Nordeste")) {
            return new String[]{
                    "Maranhão", "Piauí", "Ceará", "Rio Grande do Norte",
                    "Paraíba", "Pernambuco", "Alagoas", "Sergipe",
                    "Bahia};
        }
        
        if (regiao.equals("Norte")) {
            return new String[]{
                    "Acre", "Amazonas", "Roraima", "Rondônia",
                    "Pará", "Amapá", "Tocantins"};
        }
        
        if (regiao.equals("Sudeste")) {
            return new String[]{
                    "Minas Gerais", "Espírito Santo", "Rio de Janeiro",
                    "São Paulo"};
        }
        
        if (regiao.equals("Sul")) {
            return new String[]{
                    "Paraná", "Santa Catarina", "Rio Grande do Sul"};
        }
        
        return new String[] { "" };
    }
}

A classe Main.java constroi a tela Principal e trata o retorno dos dados recebidos da Tela dos Estados.

O método onCreate(Bundle) cria um ListView com o layout padrão do Android (android.R.layout.simple_list_item_1) com os dados obtidos da matriz regiao.

O método onItemClick(AdapterView<?>, View, int, long) é chamado quando uma região é selecionada no ListView. Neste método é armazenada a região selecionada na variável local regiao e a Activity dos Estados é exibida através da chamada do método startActivityForResult(intent, int) que exibe a Activity e aguarda o resultado dos estados selecionados quando a Activity chamada retornar para a Activity principal.

O método onActivityResult(int, int, Intent) é chamado quando a Activity de Estados retorna para a Activity Principal. Este método verifica se a Activity que retornou era a Activity esperada através da verificação do parâmetro requestCode, verifica se o resultado está correto através do parâmetro resultCode, obtêm os dados retornados através do parâmetro data e exibe uma mensagem indicando os estados selecionados na Activity de Estados.

O método getEstados(String) retorna uma matriz com os estados de acordo com a região informada no parâmetro regiao.

A segunda classe é a StateListActivity.java (Listagem 2).

Listagem 2
public class StateListActivity extends ListActivity implements OnItemClickListener {
    ListView listView;
    String[] estados;
    ArrayList<String> stateList;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        Intent intent = getIntent();
        
        if (intent != null) {
            estados = intent.getStringArrayExtra("estados");
            
            // Exibe o listview com botão tipo checkbox
            setListAdapter(new ArrayAdapter<String>(this, 
                    android.R.layout.simple_list_item_multiple_choice,
                    estados));
            
            listView = getListView();
            
            // Desabilita o foco no item
            listView.setItemsCanFocus(false);
            
            // Configura o listview para permitir a seleção de
            // vários itens simultaneamente
            listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
            
            listView.setOnItemClickListener(this);
        }
 
    }
 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, 
            int position, long id) {
        stateList = new ArrayList<String>();
        
        // Mapa de Booleans com a informação dos estados selecionados
        SparseBooleanArray status = listView.getCheckedItemPositions();
        
        // Limpa a lista anterior para manter atualizada
        // a última seleção
        stateList.clear();
        
        for (int i = 0; i < status.size(); i++) {
            if (status.valueAt(i) == true) {
                stateList.add((String)listView.getItemAtPosition
                        (status.keyAt(i)));
            }
        }
    }
    
    @Override
    public void onBackPressed() {
        // Verifica se algum estado foi selecionado
        if (stateList != null) {
            // Passa as informações dos estados selecionados para
            // a activity pai
            Intent intent = new Intent();
            intent.putStringArrayListExtra("estados", stateList);
            setResult(RESULT_OK, intent);
        }
        else
        {
            setResult(RESULT_CANCELED);            
        }
 
        super.onBackPressed();
    }
}

A classe StateListActivity.java é responsável por exibir a tela com a relação de estados e permite a seleção ou não de um ou mais estados.

O método onCreate(Bundle) utiliza o método getIntent() para obter a relação de estados que foi passada pela Activity principal (classe Main.java) e exibe a tela com o layout configurado para exibir um listview com botão tipo checkbox, configurado com a opção de selecionar vários itens simultaneamente.

O método onItemClick(AdapterView, View, int, long) é chamado quando um item do listview é selecionado. Neste método é utilizada a classe SparseBooleanArray que faz o mapeamento de boolean para integer e é utilizada para armazenar as informações de quais itens da relação de estados foi selecionado. Com o método valueAt(int) da classe SparseBooleanArray, obtemos o valor se true ou false para cada item, e com o método keyAt(int) obtemos a chave dentro da lista de itens. Esta chave é utilizada para determinar qual o estado que está com o status selecionado.

O método onBackPressed() é chamado quando é acionado o botão “Voltar” do emulador. Este método  passa as informações dos estados selecionados para a Activity principal.

Posts Relacionados:


28 de março de 2011

Como Criar um ListAdapter Customizado para o ListView do Android

O widget ListView é um componente poderoso e flexível do Android, normalmente utilizado para exibir uma simples lista de dados. Este post descreve como criar um ListAdapter para exibir várias informações e uma figura em cada linha do ListView.

Para demonstrar a criação do ListAdapter customizado foi criado um projeto para exibir em um ListView os estados do Brasil, juntamente com a abreviação, nome da capital, área em km2 e a imagem da bandeira do estado. O projeto rodando apresenta a seguinte tela:

device3

O projeto possui somente uma Activity que carrega o ListView no método OnCreate() demonstrado no arquivo Main.java.

public class Main extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        List<State> stateList = new ArrayList<State>();
        
        for (int i = 0; i < states.length; i++) {
            State state = new State();
            state.setState(states[i][0]);
            state.setAbbreviation(states[i][1]);
            state.setCapital(states[i][2]);
            state.setArea(Float.parseFloat(states[i][3]));
            state.setBanner(images[i]);
            
            stateList.add(state);
        }  
        
        setListAdapter(new StateAdapter(this, stateList));
    }
    
    private String[][] states = new String[][]{
           {"Acre", "AC", "Rio Branco", "152581.4"},
           {"Alagoas", "AL", "Maceió", "27767.7"},
           {"Amapá", "AP", "Macapá", "142814.6"},
           {"Amazonas", "AM", "Manaus", "1570745.7"},
           {"Bahia", "BA", "Salvador", "564692.7"},
           {"Ceará", "CE", "Fortaleza", "148825.6"},
           {"Distrito Federal", "DF", "Brasília", "5822.1"},
           {"Espírito Santo", "ES", "Vitória", "46077.5"},
           {"Goiás", "GO", "Goiânia", "340086.7"},
           {"Maranhão", "MA", "São Luís", "331983.3"},
           {"Mato Grosso", "MT", "Cuiabá", "903357.9"},
           {"Mato Grosso do Sul", "MS", "Campo Grande", "357125.0"},
           {"Minas Gerais", "MG", "Belo Horizonte", "586528.3"},
           {"Pará", "PA", "Belém", "1247689.5"},
           {"Paraíba", "PB", "João Pessoa", "56439.8"},
           {"Paraná", "PR", "Curitiba", "199314.9"},
           {"Pernambuco", "PE", "Recife", "98311.6"},
           {"Piauí", "PI", "Teresina", "251529.2"},
           {"Rio de Janeiro", "RJ", "Rio de Janeiro", "43696.1"},
           {"Rio Grande do Norte", "RN", "Natal", "52796.8"},
           {"Rio Grande do Sul", "RS", "Porto Alegre", "281748.5"},
           {"Rondônia", "RO", "Porto Velho", "237576.2"},
           {"Roraima", "RR", "Boa Vista", "224299.0"},
           {"Santa Catarina", "SC", "Florianópolis", "95346.2"},
           {"São Paulo", "SP", "São Paulo", "248209.4"},
           {"Sergipe", "SE", "Aracaju", "21910.3"},
           {"Tocantins", "TO", "Palmas", "277620.9"}
       };
    
    private int[] images = new int[]{
            R.drawable.acre,
            R.drawable.alagoas,
            R.drawable.amapa,
            R.drawable.amazonas,
            R.drawable.bahia,
            R.drawable.ceara,
            R.drawable.distritofederal,
            R.drawable.espiritosanto,
            R.drawable.goias,
            R.drawable.maranhao,
            R.drawable.matogrosso,
            R.drawable.matogrossosul,
            R.drawable.minasgerais,
            R.drawable.para,
            R.drawable.paraiba,
            R.drawable.parana,
            R.drawable.pernambuco,
            R.drawable.piaui,
            R.drawable.riojaneiro,
            R.drawable.riograndenorte,
            R.drawable.riograndesul,
            R.drawable.rondonia,
            R.drawable.roraima,
            R.drawable.santacatarina,
            R.drawable.saopaulo,
            R.drawable.sergipe,
            R.drawable.tocatins
    };
}


No método OnCreate() foi criado um ArrayList com as informações dos estados. Normalmente este tipo de informação é carregada de uma base de dados ou de alguma outra fonte de informação, mas como o objetivo é demonstrar a utilização do ListView, foi criado um array com as informações dos estados (states) e outro array com as imagens das bandeiras (images). As imagens foram obtidas dos arquivos colocados na pasta de resource res/drawable-mdpi.

Para montar o ArrayList foi utilizada a classe State, descrita no arquivo State.java:

public class State {
    private String state;
    private String abbreviation;
    private String capital;
    private float area;
    private int banner;
    
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getAbbreviation() {
        return abbreviation;
    }
    public void setAbbreviation(String shortening) {
        this.abbreviation = shortening;
    }
    public String getCapital() {
        return capital;
    }
    public void setCapital(String capital) {
        this.capital = capital;
    }
    public float getArea() {
        return area;
    }
    public void setArea(float area) {
        this.area = area;
    }
    public int getBanner() {
        return banner;
    }
    public void setBanner(int banner) {
        this.banner = banner;
    }
}


O cursor para as informações do ListView é montado utilizando o método setListAdapter(ListAdapter adapter) da classe ListActivity. No parâmetro adapter foi utilizada a classe StateAdapter que recebe como parâmetros o contexto da Activity e o ArrayList com as informações dos estados. O arquivo StateAdapter.java descreve os métodos que são necessários para montar o cursor do ListView. 

/** 
 * Adapter utilizado para exibir as informações dos Estados
 * no ListView.
 * @author Administrador
 *
 */
public class StateAdapter extends BaseAdapter {
    private Context context;
    private List<State> stateList;
    
    public StateAdapter(Context context, List<State> statelist){
        this.context = context;
        this.stateList = statelist;
    }
    
    @Override
    public int getCount() {
        return stateList.size();
    }
 
    @Override
    public Object getItem(int position) {
        return stateList.get(position);
    }
 
    @Override
    public long getItemId(int position) {
        return position;
    }
 
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Recupera o estado da posição atual
        State state = stateList.get(position);
        
        // Cria uma instância do layout XML para os objetos correspondentes
        // na View
        LayoutInflater inflater = (LayoutInflater)
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.listview_states, null);
        
        // Estado - Abreviação
        TextView textState = (TextView)view.findViewById(R.id.textState);
        textState.setText(state.getState() + " - " + state.getAbbreviation());
        
        // Capital
        TextView textCapital = (TextView)view.findViewById(R.id.textCapital);
        textCapital.setText(state.getCapital());
        
        // Área
        TextView textArea = (TextView)view.findViewById(R.id.textArea);
        textArea.setText(String.valueOf(state.getArea()));
        
        // Bandeira
        ImageView img = (ImageView)view.findViewById(R.id.imageState);
        img.setImageResource(state.getBanner());
 
        return view;
    }
}


A classe StateAdapter extende a classe BaseAdapter que deve implementar os métodos:
  •  getCount(): retorna o número de itens.
  • getItem(int position): retorna o item de uma posição específica. 
  •  getItemId(int position): retorna o Id de um item de uma posição específica.
  • getView(int position, View convertView, ViewGroup parent): retorna a View com as informações posicionadas de acordo com o layout montado no arquivo listview_states.xml
O arquivo listview_states.xml monta o layout que é utilizado em cada linha do ListView, para posicionar as informações em um formato customizado.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:padding="5dp"
  android:background="#cccccc"
  >
  
  <ImageView
      android:id="@+id/imageState"
      android:layout_width="60dp"
      android:layout_height="42dp"
      android:background="#ffffff"
      android:scaleType="centerCrop"
  />
  
  <TextView
      android:id="@+id/textState"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:layout_toRightOf="@+id/imageState"
      android:layout_alignTop="@+id/imageState"
      android:text="São Paulo - SP"
      android:textSize="20sp"    
      android:textColor="#333333"
      android:paddingLeft="5dp"  
  />
  
  <TextView
      android:id="@+id/textCapital"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@+id/textState"
      android:layout_toRightOf="@+id/imageState"
      android:text="São Paulo"
      android:textColor="#333333"
      android:paddingLeft="5dp" 
  />
  
  <TextView
      android:id="@+id/textArea"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_below="@+id/textState"
      android:layout_alignParentRight="true"
      android:text="100000"
      android:textColor="#333333"
  />  
</RelativeLayout>


No método getView utilizando a classe LayoutInflater e a classe View podemos associar cada widget do layout à informação que deve ser exibida no ListView.

No exemplo deste post, quando um item do ListView recebe o foco a tradicional cor “Laranja” de fundo não aparece. Isto acontece porque o ListView possui uma cor de fundo opaca, neste caso “cinza”. Na verdade o foco “Laranja” é exibido, mas somente atrás do background do ListView.

Uma possível solução para exibir o fundo “Laranja” neste caso, é fazer uma pequena alteração da definição do layout da tela.

Primeiro precisamos criar um arquivo tipo StateList para definir as cores que serão apresentadas quando o item do ListView for pressionado, selecionado ou receber foco. Para isso foi criado o arquivo list_selector.xml, que deve ser colocado no diretório res/drawable

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


Os itens relativos aos estados pressionado, selecionado ou com focus devem ser configurados com o atributo android:drawable com cor transparente, somente o último item que representa o item sem mudança de estado deve ser configurado com a cor cinza. A cor cinza foi declarada no arquivo color.xml que deve ser colocado no diretório res/values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="cinza">#cccccc</color>
</resources>


Para que o fundo “laranja” funcione altere no arquivo listview_states.xml o atributo android:drawable da tag RelativeLayout de android:background="#cccccc" para android:background="@drawable/list_selector".
Agora quando um item for clicado teremos a seguinte tela:

23 de março de 2011

Como Usar uma Figura de Fundo em uma Tela com ListView no Android

Por padrão o Android apresenta a tela com cor de fundo escuro, e a maioria dos widgets possuem cor de fundo transparente. O widget ListView obedece a mesma regra, normalmente sendo utilizado em uma tela com cor de fundo sólida ou transparente. Caso seja utilizado em uma tela com cor de fundo diferente de uma cor sólida, como por exemplo usando uma figura, é necessário tomar um certo cuidado no projeto, para que o ListView no momento do scroll não apresente um fundo escuro no meio da figura.

Este post descreve como exibir uma figura de fundo em uma tela com ListView e como corrigir o problema ocasionado durante o scroll do ListView. Observe na figura abaixo uma tela com ListView e uma figura de fundo mostrando o efeito do scroll. A figura da esquerda mostra a tela exibida na iniciação da aplicação e a figura da direita mostra a tela no momento é quem é feito o scroll.

imagem1

Na figura onde é feito o scroll podemos observar que a figura deixa de ser exibida corretamente. Isto ocorre devido a uma otimização de exibição de tela no sistema do Android. Para solucionar este problema, no framework do Android foi criado o método setCacheColorHint(int) que pode ser configurado via código, ou podemos utilizar o atributo android:cacheColorHint no arquivo XML do layout.

Para demonstrar a utilização do ListView com figura de fundo foi criado um projeto para exibir a relação de estados do Brasil em um ListView cuja figura de fundo é o mapa do Brasil.

Para exibir a figura de fundo foi criado um arquivo theme.xml na pasta res/values que configura a figura de fundo na janela do Android.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Imagem de fundo da tela do listview -->
    <style name="MapBackground" parent="android:Theme">
        <item name="android:windowBackground">@drawable/mapa</item>
        <item name="android:windowNoTitle">true</item> 
    </style>
</resources>


Observe que foi criada a tag <style> com o nome MapBackground, e a janela do Android foi configurada com a figura do mapa através da tag:

<item name="android:windowBackground">@drawable/mapa</item>

Também foi configurado para que a janela não exiba título, através da tag:

<item name="android:windowNoTitle">true</item>

O tema deve ser aplicado na Activity que vai exibir o ListView. Esta configuração pode ser feita no arquivo AndroidManifest.xml.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="br.com.romar"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Main"
                  android:label="@string/app_name"
                  android:theme="@style/MapBackground">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
    </application>
    <uses-sdk android:minSdkVersion="8" />
 
</manifest> 


No arquivo AndroidManifest.xml na tag <activity> da tela de iniciação (.Main) foi inserido o atributo android:theme="@style/MapBackground" que configura o tema da Activity para exibir a figura do mapa.

No arquivo Main.java no método OnCreate foi inserido o código para exibição dos estados no ListView e a configuração do ListView para corrigir o efeito do scroll.

public class Main extends ListActivity {
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // Obtêm a relação de estados que está armazenado
        // no arquivo strings.xml
        String[] states = getResources().getStringArray(
                R.array.state_array);
        
        // Preenche o ListView com os estados
        setListAdapter(new ArrayAdapter<String>(this, 
                android.R.layout.simple_list_item_1, states));
        
        // Obtêm o ListView
        ListView lv = getListView();
        // Configurando o método setCacheColorHint para 
        // o valor 0 informamos que a cor de fundo do listview
        // possui não possui cor única, sólida ou opaca
        lv.setCacheColorHint(0);
    }
}


No método onCreate(Bundle savedInstanceState) foi utilizado como fonte de dados um array chamado state_array, configurado no arquivo strings.xml localizado na pasta res/values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ListViewI</string>
    <string-array name="state_array">
        <item>Acre</item>
        <item>Alagoas</item>
        <item>Amapá</item>
        <item>Amazonas</item>
        <item>Bahia</item>
        <item>Ceará</item>
        <item>Distrito Federal</item>
        <item>Espírito Santo</item>
        <item>Goiás</item>
        <item>Maranhão</item>
        <item>Mato Grosso</item>
        <item>Mato Grosso do Sul</item>
        <item>Minas Gerais</item>
        <item>Pará</item>
        <item>Paraíba</item>
        <item>Paraná</item>
        <item>Pernambuco</item>
        <item>Piauí</item>
        <item>Rio de Janeiro</item>
        <item>Rio Grande do Norte</item>
        <item>Rio Grande do Sul</item>
        <item>Rondônia</item>
        <item>Roraima</item>
        <item>Santa Catarina</item>
        <item>São Paulo</item>
        <item>Sergipe</item>
        <item>Tocatins</item>
    </string-array>
</resources>


Para exibir o ListView foi utilizado o layout padrão do sistema Android, simple_list_item_1, configurado no método setListAdapter().

Após criado o ListView, foi utilizado o método getListView()  para obter o ListView da Activity e  poder desabilitar a otimização da tela através do método setCacheColorHint(). O valor utilizado foi 0 que representa cor de fundo transparente.

Com a inserção do método setCacheColorHint() o scroll pode ser feito normalmente sem apresentar o defeito na figura de fundo.

Posts Relacionados: