Dashboard Layout

O DashboardLayout é um componente utilizado para montar aqueles tradicionais menus principais, onde o usuário escolherá qual funcionalidade do sistema ele irá acessar. Esse menu, geralmente aparecerá logo após o login no sistema e através dele o usuário escolhe para onde ir.
O DashboardLayout é um componente que extende de ViewGroup, como o LinearLayout. E assim como outros ViewGroup ele possui a responsabilidade de gerenciar as views que estiverem ligadas a ele. A particularidade do Dashboard é a sua capacidade de organizar os componentes na tela indiferente da quantidade de elementos gráfico que ele esteja gerenciando e indiferente a orientação e tamanho da tela. Ou seja, se o celular estiver na horizontal ou na vertical, o Dashboard consegue adaptar os elementos para ocuparem o espaço da tela da melhor forma possível.
No mercado conseguimos ver diversos casos de sucesso do Dashboard. Um clássico exemplo é a app do Twitter:

Exemplo de Dashboard Layout na aplicação do Twitter.

Exemplo de Dashboard Layout na aplicação do Twitter.

Veja mais alguns exemplos e explanações sobre o DashboardLayout aqui!

Agora vamos a prática

O Google apresentou o Dashboard Layout em um evento Google IO, através do aplicativo Android feito para esse evento também conseguimos ter acesso ao código fonte do Dashboard. Um ponto importante é que o Dashboard não é um ViewGroup presente na API do Android. Se trata de um padrão de interface gráfica que possui algumas implementações de terceiros, nesse exemplo pegamos o Dashboard de uma aplicação feita pelos próprios desenvolvedores da Google.

Vejamos o código do DashboardLayout

/**
 * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and
 * vertical whitespace.
 */
public class DashboardLayout extends ViewGroup {

    private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10;
    
    private int cols = 2;

    private int mMaxChildWidth = 0;
    private int mMaxChildHeight = 0;

    public DashboardLayout(Context context) {
        super(context, null);
    }

    public DashboardLayout(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mMaxChildWidth = 0;
        mMaxChildHeight = 0;

        // Measure once to find the maximum child size.

        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
            mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight());
        }

        // Measure again for each child to be exactly the same size.

        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildWidth, MeasureSpec.EXACTLY);
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildHeight, MeasureSpec.EXACTLY);

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }

        setMeasuredDimension(
                resolveSize(mMaxChildWidth, widthMeasureSpec),
                resolveSize(mMaxChildHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int width = r - l;
        int height = b - t;

        final int count = getChildCount();

        // Calculate the number of visible children.
        int visibleCount = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            ++visibleCount;
        }

        if (visibleCount == 0) {
            return;
        }

        // Calculate what number of rows and columns will optimize for even horizontal and
        // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on.
        int bestSpaceDifference = Integer.MAX_VALUE;
        int spaceDifference;

        // Horizontal and vertical space between items
        int hSpace = 0;
        int vSpace = 0;

        
        int rows;

        while (true) {
            rows = (visibleCount - 1) / cols + 1;

            hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
            vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));

            spaceDifference = Math.abs(vSpace - hSpace);
            if (rows * cols != visibleCount) {
                spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER;
            }

            if (spaceDifference < bestSpaceDifference) {
                // Found a better whitespace squareness/ratio
                bestSpaceDifference = spaceDifference;

                // If we found a better whitespace squareness and there's only 1 row, this is
                // the best we can do.
                if (rows == 1) {
                    break;
                }
            } else {
                // This is a worse whitespace ratio, use the previous value of cols and exit.
                --cols;
                rows = (visibleCount - 1) / cols + 1;
                hSpace = ((width - mMaxChildWidth * cols) / (cols + 1));
                vSpace = ((height - mMaxChildHeight * rows) / (rows + 1));
                break;
            }

            ++cols;
        }

        // Lay out children based on calculated best-fit number of rows and cols.

        // If we chose a layout that has negative horizontal or vertical space, force it to zero.
        hSpace = Math.max(0, hSpace);
        vSpace = Math.max(0, vSpace);

        // Re-use width/height variables to be child width/height.
        width = (width - hSpace * (cols + 1)) / cols;
        height = (height - vSpace * (rows + 1)) / rows;

        int left, top;
        int col, row;
        int visibleIndex = 0;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }

            row = visibleIndex / cols;
            col = visibleIndex % cols;

            left = hSpace * (col + 1) + width * col;
            top = vSpace * (row + 1) + height * row;

            child.layout(left, top,
                    (hSpace == 0 && col == cols - 1) ? r : (left + width),
                    (vSpace == 0 && row == rows - 1) ? b : (top + height));
            ++visibleIndex;
        }
    }
}

Nesse artigo não vamos entrar nos detalhes do código dessa classe. Apenas utilizaremos esse componente como se fosse um elemento gráfico proveniente da própria API do Android.
Após a criação do Dashboard Layout, precisaremos de uma Activity que representará o nosso Menu e nos servirá de exemplo. Mas antes de criar a nossa Activity vamos criar o Layout xml que será utilizado por ela.

<br.com.lvc.app.DashboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
   android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
     <Button
         android:background="@android:color/transparent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Botão Um"
            android:drawableTop="@drawable/image_dois"
            android:onClick="onClickUm"
            />
     
     <Button
         android:background="@android:color/transparent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Botão Dois"
            android:drawableTop="@drawable/image_um"
            android:onClick="onClickDois"
             />

</br.com.lvc.app.DashboardLayout>

Veja que utilizamos o DashboardLayout da mesma forma na qual utilizamos outros ViewGroups como o LinearLayout.
Porém, por não se tratar de um componente nativo da API do Android, devemos informar o caminho completo dele no momento em que o referenciamos no nosso Layout.xml. No caso da minha aplicação de exemplo eu apontei para o pacote br.com.lvc.app e a minha classe DashboardLayout.

Outro detalhe, é que utilizei dois ícones que encontrei na internet que chamei de image_um e image_dois. Por ser somente um exemplo, fica a critério do leitor escolher um ícone que o agrade.

Outro ponto que deve ser levado em consideração é que preenchemos a tag onClick dos nossos componentes Button presentes no DashboardLayout. Isso implica que o texto que foi passado para essa tag representa um nome de um método que será acionado na Activity que utilizará esse Layout.xml.

O resultado do nosso layout.xml será uma tela assim:

Nosso DashboarLayout.

Nosso DashboarLayout.

Agora que o Layout esta pronto vamos criar a Activity que irá consumi-lo:

public class MainMenu extends Activity {
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main_menu);
	}

	
	public void onClickUm(View view) {
		Log.i(“CLICK”,”Clicou na primeira opção”);
	}
	
    public void onClickDois(View view) {
		Log.i(“CLICK”,”Clicou na segunda opção”);
	}
    
}

Podemos observar que se trata de uma Activity muito simples. Apenas utilizamos o layout xml (main_menu.xml) que criamos logo acima. E implementamos as ações dos botões. Veja que o nome dos métodos onClickUm e onClickDois, batem com o nome especificado na tag onClick do nosso layout.xml.

Pronto, agora você tem um exemplo de DashboardLayout para utilizar em suas aplicações.

Até a próxima.

Anúncios

, , ,

  1. #1 by Jackson on Setembro 26, 2014 - 4:11 pm

    Muito bom! Tem como adicionar um conteúdo no topo da tela e no rodapé também? ( header/footer )

    • #2 by Leonardo Casasanta on Setembro 26, 2014 - 4:20 pm

      Tem sim Jackson,
      Você pode envolver o layout do Dashboard com um LinearLayout.

      A estrutura do XML ficaria assim:

      LinearLayout
      Cabeçalho
      Dashboard
      Elementos do Dashboard
      fecha o Dashboard
      Rodapé
      Fecha o LinearLayout

      Tentei postar um exemplo de código no comentário, mas não rolou =[
      Espero ter ajudado

      Abraço.

  2. #3 by Luan on Abril 17, 2013 - 7:56 pm

    Olá…
    Criei um novo pacote com uma nova classe (para adicionar o código do Dashboard) dentro da pasta src. No arquivo xml quando passo o caminho do meu pacote e da minha classe e tento visualizar como ficou o xml, me retorna um erro acusando que não encontrou a classe citada no xml, conferi já o pacote e a classe esta tudo certo. Tem alguma ideia?

    Valeu

    • #4 by Leonardo Casasanta on Abril 17, 2013 - 11:06 pm

      Se você passou o caminho certinho tipo no exemplo

      Vai dar certinho, a visualização pode falhar devido a algum bug no eclipse. Mas não pode ocorrer falha de compilação. Ou seja, executa que vai funcionar no emulador ou no dispositivo.

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: