sexta-feira, 30 de novembro de 2012

Criando Views

Olá pessoal,

Depois de algum tempo venho falar sobre a criação de views e encerrar uma sequência de 3 posts relacionados a este assunto. Nos posts anteriores mostrei como customizar uma View e também como criar uma View composta de outras Views. Neste post vou mostrar como criar uma View a partir da extensão da classe android.view.View.

A View criada neste post não terá funcionalidade, será apenas um desenho multi direcional, pois vou abordar em um post mais a frente como dar vida a este tipo de controle utilizando o acelerômetro do dispositivo. Este post servirá como base para o conhecimento da estrutura de uma View criada do zero.

No final nossa View ficará como a imagem abaixo:


Aproveito este post para fazer um convite a vocês a instalar a versão Jelly Bean 4.1.2 de testes da Samsung. Está fantástica. Veja sobre no site SamMobile.com

O primeiro passo para se construir uma View do zero é criar uma classe fazer a extensão da classe android.view.View. Fazendo isso, você está herdando todos os comportamentos básicos de uma View. O ponto seguinte é se preocupar me sobrecarregar todos os construtores da classe.

public class DirectionalView extends View



public DirectionalView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initDirectionalView();

}

public DirectionalView(Context context, AttributeSet attrs) {
super(context, attrs);
initDirectionalView();
}

public DirectionalView(Context context) {
super(context);
initDirectionalView();
}


Repare que todos o três construtores fazem chamada ao initDirectionalView. Este procedimento é utilizando para inicializar as variáveis que serão utilizadas pelo sistema. É uma boa prática fazer a inicialização das várias logo no início do aplicativo, assim você não evita de executar uma ação de inicialização várias vezes desnecessariamente, lembre-se é importante se preocupar com o processo durante a execução de um aplicativo móvel.

O procedimento initDirectionalView está definindo que o controle não irá receber foco, definindo os objetos que irão desenhar as linhas na tela, carregando os textos dos Resources para exibição em tela e definindo a cor da tela baseando se nos Resources.


private void initDirectionalView()
{
if (this.isInEditMode())
return;

// esta ação define que o validador não receberá foco...
setFocusable(false);

// referencia todos os itens...
Resources r = this.getResources();

linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(r.getColor(R.color.borderColor));
linePaint.setStyle(Paint.Style.FILL);
linePaint.setStrokeWidth(1);

textHeight = 20;
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(r.getColor(R.color.foreColor));
textPaint.setTextSize(textHeight);



textEast = r.getString(R.string.east);
textNorth = r.getString(R.string.north);
textSouth = r.getString(R.string.south);
textWest = r.getString(R.string.west);

this.setBackgroundColor(r.getColor(R.color.backcolor));

}

Até o momento descrevi apenas como a View é inicializada, agora precisamos ver como a View é desenhada. O desenho da View é feito dentro da chamada onDraw, que recebe como parâmetro o Canvas, que é o local onde fazemos o desenho da View. Esta chamada é feita sempre que for necessário redesenhar a tela.


@Override
protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

/* define os valores que podem virar parametros*/
int lineMargin = 40;
int textMargin = 20;

int height = this.getHeight();
int width = this.getWidth();

/* encontra os centros */
int centerX = width/2 ;
int centerY = height/2;

/* define os pontos*/
int topX = centerX;
int topY = lineMargin;

int bottomX = centerX;
int bottomY = height - lineMargin;

int leftX = lineMargin;
int leftY = centerY;

int rightX = width - lineMargin;
int rightY = centerY;

int topLeftX = width / 3;
int topLeftY = height /3;

int bottomRightX = (width * 2)/3;
int bottomRightY = (height * 2)/3;

int topRightX = (width * 2)/3;
int topRightY = height/3;

int bottomLeftX = width / 3 ;
int bottomLeftY =  (height * 2)/3;

/* desenha as linhas principais */
canvas.drawLine(topX, topY,bottomX ,bottomY, linePaint);
canvas.drawLine(leftX, leftY,rightX ,rightY, linePaint);
canvas.drawLine(topLeftX, topLeftY,bottomRightX ,bottomRightY, linePaint);
canvas.drawLine(topRightX, topRightY, bottomLeftX, bottomLeftY, linePaint);

/* desenha as linhas secundárias */
canvas.drawLine(topX, topY, topLeftX, topLeftY, linePaint);
canvas.drawLine(topLeftX, topLeftY, leftX, leftY, linePaint);
canvas.drawLine(leftX, leftY, bottomLeftX, bottomLeftY, linePaint);
canvas.drawLine(bottomLeftX, bottomLeftY, bottomX, bottomY, linePaint);
canvas.drawLine(bottomX, bottomY, bottomRightX, bottomRightY, linePaint);
canvas.drawLine(bottomRightX, bottomRightY, rightX, rightY, linePaint);
canvas.drawLine(rightX, rightY, topRightX, topRightY, linePaint);
canvas.drawLine(topRightX, topRightY, topX, topY, linePaint);

canvas.drawText(textNorth, topX, textMargin, textPaint);
canvas.drawText(textSouth, bottomX,bottomY + textMargin+textHeight, textPaint);
canvas.drawText(textEast, rightX + textHeight, rightY, textPaint);
canvas.drawText(textWest, textMargin, leftY, textPaint);


}



Repare no código acima que a partir do canvas todas as linhas são feitas e os textos escritos.

Outro ponto importante é o redimensionamento da View. Todas as vezes que a View for redimensionada é necessário verificar o tamanho e reaplicar o redimensionamento. Repare que propositalmente eu criei uma função que verifica o tamanho retornado pelo chamada onMeasure e atribui o tamanho. Se não tiver um tamanho especificado utiliza 400dp.


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


/*captura as medidas de altura e largura...*/ 
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);

/*define a medida baseando-se na menor*/
int d = Math.min(measuredWidth, measuredHeight);
setMeasuredDimension(d, d);

}

private int measure(int measureSpec) {

/* verifica o modo de medida e se não tiver tamanho define o valor 200 como padrão */
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);

if (mode == MeasureSpec.UNSPECIFIED)
size = 400;

return size;
}

A utilização da View é do mesmo formato que defini nos posts anteriores. Mas basta relembrar:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="@color/backcolor"
    
    >
       <com.example.criandoviews.DirectionalView 
        android:id="@+id/directional1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" 
        />

</RelativeLayout>

Pessoal este foi o último post em português, este blog está passando por reestruturação e o próximo post será em inglês. Espero que tenham gostado.

O download do código fonte pode ser feito clicando aqui.

Abraço

sábado, 3 de novembro de 2012

Views Compostas

Olá pessoal,

Conforme mencionei no artigo anterior, hoje estou escrevendo o segundo artigo sobre a criação de Views, hoje vamos falar de Views compostas. No artigo anterior descrevi como podemos customizar uma View e fazer uso das propriedades já existentes. Views compostas são aquelas que utilizam mais de uma View em sua composição.

O exemplo que preparei para este artigo é uma View para mostrar resumidamente as informações de contatos. Por enquanto ele irá utilizar dados fictícios, mais adiante irei postar um exemplo com a listagem real dos contatos do telefone.

As Views compostas são construídas por extensão de uma classe ViewGroup (como as classes de Layout vistas aqui neste blog) pois elas irão conter outras Views internamente. No exemplo que desenvolvi para este artigo eu utilizei do GridLayout, devido a flexibilidade que este possui para o encaixe dos objetos.

public class ContactView extends GridLayout

Assim como fizemos quando customizamos a View, podemos utilizar da boa prática de criar um arquivo XML para definir o layout e depois utilizar o serviço de "inflater" para aplicar a visualização do controle.


<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp" >

    <ImageView
        android:id="@id/id_contact_view_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_column="0"
        android:layout_gravity="fill_vertical"
        android:layout_row="0"
        android:layout_rowSpan="2"
        android:contentDescription="@string/string_contact_view_image_contentDescription"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@id/id_contact_view_name"
        android:layout_column="1"
        android:layout_height="30dp"       
        android:layout_marginLeft="5dp"
        android:layout_row="0" />

    <TextView
        android:id="@id/id_contact_view_phoneNumber"
        android:layout_column="1"
        android:layout_height="30dp"
        android:layout_marginLeft="5dp"
        android:textColor="@android:color/holo_blue_light"
        android:layout_row="1" />

</GridLayout>

Repare que eu utilizei valores fixos para a largura, altura e margem de alguns itens, isso foi proposital pois não utilizamos isso antes e também para que o layout ficasse definido no formato que eu necessitava.

Agora repare abaixo que eu defini todo o comportamento da View no código e no trecho em destaque é o ponto onde eu associo o XML ao controle.


package com.example.viewscompostas;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;

public class ContactView extends GridLayout {

TextView contactName;
TextView contactPhoneNumber;
ImageView contactImage;


public ContactView(Context context) {
super(context);
init();

}

public ContactView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

public ContactView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {

if (this.isInEditMode())
return;

String infService = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li = (LayoutInflater) getContext().getSystemService(
infService);
li.inflate(R.layout.contact_view, this, true);

contactName = (TextView) findViewById(R.id.id_contact_view_name);
contactPhoneNumber = (TextView) findViewById(R.id.id_contact_view_phoneNumber);
contactImage = (ImageView) findViewById(R.id.id_contact_view_image);

}

public static ContactView Create(Context context, Drawable image, String name,
String phoneNumber) {

ContactView contactView = new ContactView(context);
contactView.setContact(image, name, phoneNumber);
return contactView;
}

public void setContact(Drawable image, String name, String phoneNumber) {
contactName.setText(name);
contactPhoneNumber.setText(phoneNumber);
contactImage.setImageDrawable(image);
}

}


No ponto em destaque há alguns pontos importantes. Enquanto eu estava desenvolvendo, reparei que havia uma mensagem de aviso para mim sobre o modo de edição, resolvi verificar o que era e descobri que você pode fazer uma condição para que o Eclipse entenda que você está no modo de edição e assim não tente efetuar funções internas da View. Isso está definido pelas linhas amarelas.

Já as linhas em verde estão descrevendo o momento onde o serviço de layout é chamado e associa a View ao controle.

As linhas em azul mostram o momento em que a referencia aos controles são capturadas para efetuar ações.

Ai está uma view composta criada. A utilização é do mesmo modo como anterior, mas neste exemplo eu fiz diferente e estou adicionando-as através do código.


LinearLayout parent = (LinearLayout) findViewById(R.id.layoutParent);

for (int index = 0; index < 3; index++) {
ContactView cv = ContactView
.Create(parent.getContext(),
getResources().getDrawable(
ContactReference.Image[index]),
ContactReference.Name[index],
ContactReference.Phone[index]);
parent.addView(cv);
}


O exemplo deste artigo pode ser baixado aqui.

O próximo post irei falar sobre como escrever uma nova View.

Abraço a todos!




quinta-feira, 18 de outubro de 2012

Customizando Views

Olá pessoal,

Este é o primeiro de 3 artigos que irei escrever sobre como trabalhar com as views que a API do Android nos proporciona. Se você leu os artigos anteriores irá saber que as views são os componentes que fazem a interface de interação com o usuário, são como os tão conhecidos "controles" presentes em linguagens como Visual Basic e C#.

Hoje vou escrever sobre como customizar uma View já existente, já os próximos dois posts serão sobre como criar views compostas e como criar novas views.

Não é minha intenção descrever todas as views da API do Android mas como é importante que você saiba que elas existem, e são muitas, segue o link oficial com a definição de todas as Views.

http://developer.android.com/reference/android/view/View.html

No post de hoje vamos ver como funciona a customização de uma View. Vamos neste exemplo vamos criar uma View que será uma caixa de texto que muda de cor ao receber o foco e retorna ao seu estado original quando o perde. É um exemplo simples, mas que nos trará entendimento sobre conceitos interessantes e sobre este processo.

O processo de customizar uma View é feito através da estensão de uma classe para o tipo desejado. No nosso exemplo estamos customizando uma View do tipo EditText, então temos que criar uma classe que estenda-a, isso faz com que possamos ter uma View com todas as funcionalidades da View que queremos customizar.

public class FocusableText extends EditText

Depois de ter a classe estendida, temos que configurar os seus construtores e o seu método onDraw. Repare no código abaixo que eu criei também uma função chamada Init, que será usada para inicializar variáveis de memória logo após o construtor ser chamado.


public FocusableText(Context context, AttributeSet ats, int ds) {
super(context, ats, ds);
init();
}

public FocusableText(Context context) {
super(context);
init();
}

public FocusableText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
this.setFocusable(true);
this.setFocusableInTouchMode(true);



myResources = getResources();
borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

marginPaint = new Paint(Paint.ANTI_ALIAS_FLAG);


margin = myResources.getDimension(R.dimen.marginFocusableText);
this.setBackgroundColor( myResources.getColor(R.color.defaultTextBackcolor));

}




É importante saber que todos os métodos que estendemos chamam a rotina principal da classe estendida.

Agora é o momento onde a customização visual da View acontece. No método onDraw é onde definimos o layout da nossa View.


@Override
public void onDraw(Canvas canvas) {

borderPaint.setColor(myResources.getColor(R.color.focusableTextBorderColor));

marginPaint.setColor( myResources.getColor(R.color.focusableTextBorderColor));


canvas.drawLine(0, 0, getMeasuredWidth(), 0, borderPaint);
canvas.drawLine(0, 0, 0, getMeasuredHeight(), borderPaint);
canvas.drawLine(0, getMeasuredHeight(),getMeasuredWidth(), getMeasuredHeight(), borderPaint);
canvas.drawLine(getMeasuredWidth(),getMeasuredHeight(),getMeasuredWidth(), 0, borderPaint);
canvas.drawLine(0, 0, getMeasuredWidth(), 0, borderPaint);


canvas.save();
canvas.translate(margin, 0);

super.onDraw(canvas);
canvas.restore();
}
Este código a principio parece complicado, mas é de simples entendimento. Aqui estamos definindo a borda do controle, a margem, escrevendo as linhas que irão formar a borda através do canvas.drawLine, salvando a área do controle, chamado aqui de canvas e chamando o método onDraw da classe estendida. Neste momento a interface do controle é construida e aplicada.

Não é necessário entrar em maiores detalhes sobre cores ou medidas, pois tudo aqui está vindo dos arquivos de Resources.

O objetivo principal do nosso controle é feito pela simples ação de sobrecarregar o método onFocusChanged da classe estendida. 

@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {

if (focused)
this.setBackgroundColor(getResources().getColor(R.color.focusableTextBackcolor));
else
this.setBackgroundColor(getResources().getColor(R.color.defaultTextBackcolor));

// TODO Auto-generated method stub
super.onFocusChanged(focused, direction, previouslyFocusedRect);

}

Repare que sempre será verificado o foco e depois será chamada a função da classe estendida.

Quando você for utilizar a View basta abrir uma tag dentro do arquivo de layout começando pelo nome do package do seu aplicativo e finalizando com o nome da sua classe criada. Veja no exemplo abaixo:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="@color/linearLayoutBackcolor"
    >

    <com.example.customizandoviews.FocusableText
        android:id="@+id/focusableText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hintFirstName"
        android:singleLine="true"
        android:layout_margin="@dimen/marginFocusableText" />

    <com.example.customizandoviews.FocusableText
        android:id="@+id/focusableText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hintLastName"
        android:singleLine="true"
        android:layout_margin="@dimen/marginFocusableText" />

    <com.example.customizandoviews.FocusableText
        android:id="@+id/focusableText3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hintAge"
        android:inputType="number"
        android:layout_margin="@dimen/marginFocusableText" />



</LinearLayout>


Atente há alguns detalhes que citei no começo do artigo e são comprovados aqui. Quando estendi a classe herdei todas as funcionalidades da classe pai e estou fazendo uso delas, note a utilização do hint, singleline e até mesmo do inputType.

Depois de pronto temos esta visualização, o campo focado sempre assume outra cor.



Abraço e boa sorte!

sábado, 13 de outubro de 2012

Fragments

Olá pessoal,

Conforme prometido há algumas semanas atrás venho falar hoje de um recurso muito interessante chamado Fragments.

O recurso Fragments foi adicionado a API do Android a partir da versão 3.0 (Honeycomb) e também está disponível nas bibliotecas de suporte, o que permite ao desenvolvedor utilizar estes recursos a partir da versão 1.6 do Android.

Os Fragments permitem ao desenvolvedor encapsular código criando componentes reutilizáveis que possuem seu próprio ciclo de vida e sua própria interface de usuário. Este recurso se assemelha muito aos User Controls presentes nas linguagens de desenvolvimento .NET da Microsoft.

Uma grande vantagem desse recurso é que ele permite ao desenvolvedor criar aplicações que suportam múltiplos layouts, onde você pode definir o posicionamento dos objetos de acordo com o tamanho da tela do usuário. Imagine um aplicativo que irá exibir uma lista de imagens e quando o usuário selecionar uma delas esta imagem será mostrada, simples não? Agora vamos pensar em otimizar este cenário, condicionando a tela de nosso aplicativo de acordo com a tela do dispositivo do usuário. Em um smartphone comum, onde a visualização é geralmente em retrato, nosso aplicativo irá mostrar somente a lista de imagens, e quando o usuário selecionar uma imagem, o nosso aplicativo irá limpar tela e mostrar a imagem, já em um tablet a tela pode ser melhor aproveitada, mostrando do lado esquerdo a lista de imagens e do lado direto a imagem selecionada pelo usuário. Esta situação pode ser resolvida utilizando os Fragments e combinando o layout. A aplicação disponível para download deste artigo faz exatamente isso. Veja abaixo a visualização do mesmo aplicativo em duas telas diferentes.

Normal

Tablet

Esta aplicação tem dois tipos de Fragments, um que possui layout definido nos resources (PictureViewFragment) e o outro (ListPictureFragment) que não tem layout definido mas é representado normalmente na tela. Como isso é possível? O ListPictureFragment é uma extensão do ListView, por isso ele já tem a sua definição de layout. Já o PictureViewFragment é composto somente por um ImageView.

public class ListPictureFragment extends ListFragment

Vamos pensar em layout neste momento. Na pasta de resources, temos layout para landscape, grande e normal. Como já escrevi neste blog, os nomes das pastas definem os layouts de acordo com a situação. Vamos analisar o layout normal do Activity principal comparando com o layout large da mesma Activity.

Normal

<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@id/fragment_container"
        android:name="com.example.fragments.PictureViewFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</GridLayout>


Large

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:layout_weight="1"
        android:id="@id/ListPictureFragmentId"
        android:name="com.example.fragments.ListPictureFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

    <fragment
        android:layout_weight="2"
        android:id="@id/PictureViewFragmentId"
        android:name="com.example.fragments.PictureViewFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

</LinearLayout>


Repare que é muito diferente um layout do outro. O primeiro tem apenas um FrameLayout e o outro possui os dois Fragments que serão mostrados na tela. A principio, você deve ter ficado com a mesma dúvida que eu tive quando vi isso. Mas há uma explicação muito lógica para isso. Na visualização normal, este FrameLayout colocado na tela serve como um ponto de transição entre os objetos que serão mostrado dentro da Activity, isso faz com que você possa trabalhar com a troca de Fragments dentro da mesma Activity. Já o segundo caso, mostra que os dois Fragments já estão na tela e neste caso não haverá transição de objetos. Mesmo sabendo deste detalhe, ainda há partes que ficam nebulosas. Então vamos ver este código e entender como a Activity trata a visualização do layout quando é criada.


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// se tiver o container de fragments...
if (findViewById(R.id.fragment_container) != null) {

// se tiver restaurando de um estado anterior não necessita de
// mudanças...
if (savedInstanceState != null)
return;

ListPictureFragment listPictureFragment = new ListPictureFragment();

// cria um novo fragmento e adiciona pois se não encontrou, este é a visualização Normal
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, listPictureFragment).commit();

}

}

Os dois pontos chaves deste código estão destacados. O primeiro é a condição que verifica o tipo de layout de acordo com os objetos presentes na tela. O segundo é utilizado se o FrameLayout foi encontrado na tela, indica que este layout será o normal, então irá adicionar o Fragment a tela. Você pode reparar também que o objeto FragmentManager é o responsável pela manipulação dos Fragments, o objeto FragmentManager é retornado pelo método getSupportFragmentManager. Ainda pensando no formato de tela de um smartphone comum, se o usuário clicar em um item da lista, a imagem será mostrada em outro Fragment, como mostrado no código abaixo:


public void onPictureItemClicked(int position) {

PictureViewFragment pictureViewFragment = (PictureViewFragment) getSupportFragmentManager().findFragmentById(R.id.PictureViewFragmentId);

if (pictureViewFragment != null) {

pictureViewFragment.ShowImage(position);

} else {

PictureViewFragment newFragment = new PictureViewFragment();
Bundle args = new Bundle();
args.putInt(PictureViewFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}

Quando o usuário clica no item é verificado se há o PictureViewFragment na tela, se tiver (tela grande) irá mostrar a imagem usando o método ShowImage(), se não tiver (tela normal) irá criar o Fragment, definir a posição inicial atráves de argumentos e em seguida utiliza uma transaction para substituir o FrameLayout pelo novo Fragment. O ponto em destaque é de extrema importância, pois através desta chamada o usuário pode utilizar o botão Voltar para retornar a tela anterior.

Toda a transação de troca de um Fragment deve estar entre o beginTransaction e o commit do objeto FragmentTransaction. 

Voltando um pouco na explicação sobre os Fragments, é importante saber que as classes que criamos com o intuito de ser um Fragment deve ser herdada da classe Fragment ou de uma classe que seja herdada de Fragment.

public class ListPictureFragment extends ListFragment

public class PictureViewFragment extends Fragment

Observe que a ListPictureFragment herda de ListFragment que é uma classe com operações de lista para ser implementada como um Fragment. Além da ListFragment, há também a WebViewFragment e a DialogFragment que serão discutidas mais para frente aqui mesmo neste blog.

É importante conhecer o ciclo de vida do Fragment que é um pouco diferente do ciclo de vida da atividade. Pelo diagrama abaixo você pode verificar que há alguns outros estados. 


No nosso aplicativo, utilizamos o método onAttach do Fragment para definir o callback da nossa aplicação para a classe da Activity principal.

Espero que este post tenha sido útil para vocês. 


*se você fizer o teste em um smartphone, tente colocar a tela em landscape, você irá utilizar o segundo layout, no entanto há um bug neste ponto que não consegui resolver a tempo de escrever este post, mas vou procurar a correção resolver este problema.

Abraço a todos.






quarta-feira, 26 de setembro de 2012

Construindo interfaces - Layouts

Olá pessoal,

Após algumas semanas sem escrever venho com mais um post interessante sobre desenvolvimento Android, vou falar sobre a utilização de layouts. Antes é importante entender que a programação para desenvolvimento Android trás conceitos de nomes diferentes dos que já estamos acostumados, mas com funcionalidades muito semelhantes. Os controles que conhecemos como objetos visíveis uteis para a interação entre o usuário e o aplicativo no Android são chamados de Views. Como disse no post anterior, as Activities funcionam de modo muito parecido ao que chamamos de forms. Bom estes são dois conceitos fáceis de entender pois são muito parecidos com os que os programadores já conhecem de outras linguagens e tipos de programação.

Além destes há também as View Groups que são extensões das Views que podem conter outras views internamente. A partir do Android 3.0 foram introduzidos também os Fragments que são partes da interface com "vida própria" e que interagem com a Activity.

Neste post vou falar de Layouts que são extensões de View Groups, que por sua vez são extensões de Views.

Os Layouts são utilizados para posicionar as Views dentro da interface visível ao usuário. As classes mais comuns de layout são:

  • LinearLayout
  • RelativeLayout
  • GridLayout

Vamos falar um pouco de cada uma delas com exemplo de código.

LinearLayout

A classe LinearLayout permite que sejam construídas interfaces alinhando controles na orientação horizontal ou vertical. É um tipo de layout muito simples de utilizar, porém limitado e geralmente é utilizado em conjunto com outros tipos de layout. No nosso exemplo teremos 3 botões na horizontal preenchendo completamente a largura da tela do dispositivo. Repare no código que não há nenhum número definindo explicitamente a posição ou largura dos botões.



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <Button
        android:id="@+id/btn1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/bt1txt"
        android:layout_weight="1"
       />

    <Button
        android:id="@+id/btn2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/bt2txt"
 />

    <Button
        android:id="@+id/btn3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/bt3txt"
   />

</LinearLayout>


Repare no resultado:

Vamos as considerações. Se você reparar no código, irá notar que o parametro  android:orientation="horizontal" define a orientação do nosso layout. Todos os botões utilizam a largura como fill_parent, que é uma constante que define que a view utilizará a medida do parent para se redimensionar. Pensando assim, teriamos um problema de layout, pois todos os botões ficariam sobrepostos e apenas o último adicionado seria visível. Mas o que acontece aqui? Estamos utilizando uma propriedade muito interessante, o layout_weight que define o "peso" que cada controle tem dentro do layout. Como defini o valor 1 para cada um dos botões, todos eles ficaram um ao lado do outro ocupando a largura da tela.
A propriedade height está definida como wrap_content que define a altura como o tamanho suficiente para caber o conteúdo interno da view.

RelativeLayout

Com o RelativeLayout você pode posicionar suas Views dentro da Activity relacionando com outras Views ou com o seu parent. No nosso exemplo abaixo, estou alinhando os botões de acordo com posições relativas na tela.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/bt1txt" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/bt2txt" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:text="@string/bt3txt" />

</RelativeLayout>


GridLayout

O GridLayout é um dos layouts mais flexíveis e com ele podemos alinhar nossos controles utilizando linhas e colunas arbitrárias. Repare que no caso do GridLayout nós utilizamos a propriedade gravity para definir o tamanho dos botões.

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill_horizontal"
        android:text="@string/bt1txt" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill"
        android:text="@string/bt2txt" />

    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="fill_horizontal"
        android:text="@string/bt3txt" />

</GridLayout>


Todos estes layouts estão no arquivo deste artigo. Eu deixei comentada as linhas que carregam os layouts no arquivo MainActivity.java, você pode descomentá-los para testar. Eu separei os arquivos propositalmente.

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        
        //setContentView(R.layout.linear_layout);
        //setContentView(R.layout.relative_layout);
        setContentView(R.layout.grid_layout);
    }


Abraço