Aumentando a produtividade no Android com o Butter Knife

Durante o desenvolvimento da nossa lista de cursos, vimos diversos assuntos sobre Android, como por exemplo, criação e personalização de uma lista e até nos comunicamos com apps externas… Porém, vamos verificar a quantidade de código que utilizamos para construir essa app simples. Começaremos pela nossa activity:

public class ListaDeCursosActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista_de_cursos);

        List<Curso> cursos = todosOsCursos();

        ListView listaDeCursos = (ListView) findViewById(R.id.lista);

        AdapterCursosPersonalizado adapter = new AdapterCursosPersonalizado(cursos, this);
        listaDeCursos.setAdapter(adapter);

        listaDeCursos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Curso curso = (Curso) parent.getItemAtPosition(position);
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(curso.getUrl()));
                startActivity(intent);

            }
        });

    }

    //métodos

}

Agora o nosso adapter:

public class AdapterCursosPersonalizado extends BaseAdapter {

    private final List<Curso> cursos;
    private final Activity act;

    public AdapterCursosPersonalizado(List<Curso> cursos, Activity act) {
        this.cursos = cursos;
        this.act = act;
    }

    @Override
    public int getCount() {
        return cursos.size();
    }

    @Override
    public Object getItem(int position) {
        return cursos.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        View view = act.getLayoutInflater().inflate(R.layout.lista_curso_personalizada, parent, false);

        Curso curso = cursos.get(position);

        TextView nome = (TextView) convertView.findViewById(R.id.lista_curso_personalizada_nome);
        TextView descricao = (TextView) convertView.findViewById(R.id.lista_curso_personalizada_descricao);
        ImageView imagem = (ImageView) convertView
                .findViewById(R.id.lista_curso_personalizada_imagem);

        nome.setText(curso.getNome());
        descricao.setText(curso.getDescricao());

        Categoria categoria = curso.getCategoria();

        if (categoria.equals(Categoria.JAVA)) {
            imagem.setImageResource(R.drawable.java);
        } else if (categoria.equals(Categoria.ANDROID)) {
            imagem.setImageResource(R.drawable.android);
        } else if (categoria.equals(Categoria.HTML)) {
            imagem.setImageResource(R.drawable.html);
        }

        return convertView;
    }

}

Vamos dar uma olhada nesse trecho de código:

ListView listaDeCursos = (ListView) findViewById(R.id.lista);

Olha o tanto de código que escrevemos apenas para buscar uma referência de uma ListView. Agora vejamos esse outro trecho:

 listaDeCursos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, 
                                               int position, long id) {
                Curso curso = (Curso) parent.getItemAtPosition(position);
                Intent intent = new Intent(Intent.ACTION_VIEW, 
                                               Uri.parse(curso.getUrl()));
                startActivity(intent);
            }
        });

Perceba que para tarefas que são bem comuns como, por exemplo, pegar a referência de uma View ou criar um listener para implementar um clique, estamos escrevendo muito código!

Será que não existe alguma biblioteca que nos ajude com esse tipo de tarefa? Pensando justamente nesse detalhe, o Jake Wharton desenvolveu a biblioteca Butter Knife capaz de facilitar todos esses procedimentos de pegar referência de View ou até mesmo implementar um listener.

Mas como podemos adicioná-la no nosso projeto? Vá em “File > Project Structure”. Vá até o menu “app” e depois na aba “Dependencies”:

android-studio-dependencies

Essa é a janela em que lista todas as dependências do projeto, ou seja, agora precisamos adicionar mais uma clicando no sinal de “soma verde > Library Dependencie”. Então escreva “butterknife”:

android-studio-add-dependencia

Selecionando o primeiro item, clique em “OK”:

android-studio-depencie-added

Observe que agora, o Butter Knife aparece na nossa lista de dependências, basta clicar em “OK”. Pronto! Já podemos utilizá-lo! Então vamos começar por esse trecho de código:

ListView listaDeCursos = (ListView) findViewById(R.id.lista);

Como podemos melhorar esse código com o Butter Knife? Primeiro, precisamos chamar o método estático bind() da classe ButterKnife que recebe a Activity que ele precisa associar às Views, então podemos enviar a nossa própria Activity, ou seja, o this:

public class ListaDeCursosActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista_de_cursos);

        ButterKnife.bind(this);

        List<Curso> cursos = todosOsCursos();

        ListView listaDeCursos = (ListView) findViewById(R.id.lista);

        AdapterCursosPersonalizado adapter = new AdapterCursosPersonalizado(cursos, this);
        listaDeCursos.setAdapter(adapter);

    }

    //código

} 

Agora precisamos modificar todas as nossas referências de View de variável local, para variável membro, ou seja, um atributo da nossa Activity:

public class ListaDeCursosActivity extends AppCompatActivity {

    ListView listaDeCursos;

    //métodos

}

Mas porque tivemos que fazer essa alteração? É justamente pela questão do Butter Knife utilizar annotations para associar as Views. Mas e agora? O que fazemos? Simples! Basta apenas adicionarmos a anotação @Bind passando por parâmetro o id dessa View:

public class ListaDeCursosActivity extends AppCompatActivity {

    @Bind(R.id.lista)
    ListView listaDeCursos;

    //métodos

}

Só isso? Sim! Apenas isso já faz todo aquele código que escrevíamos por de traz dos panos! Podemos até apagar aquele trecho do nosso código. Vejamos o resultado da nossa Activity:

public class ListaDeCursosActivity extends AppCompatActivity {

    @Bind(R.id.lista)
    ListView listaDeCursos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista_de_cursos);

        List<Curso> cursos = todosOsCursos();

        AdapterCursosPersonalizado adapter = new AdapterCursosPersonalizado(cursos, this);
        listaDeCursos.setAdapter(adapter);

        listaDeCursos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Curso curso = (Curso) parent.getItemAtPosition(position);
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(curso.getUrl()));
                startActivity(intent);

            }
        });

    }

    //métodos

}

Você pode estar se perguntando sobre o motivo de um atributo não ser private e é justamente pelo motivo do Butter Knife não funcionar com o modificador de acesso private, ou seja, é aquele famoso “Trade Off”: “ganhamos em um lado, porém perdemos em outro…”.

O nosso código já melhorou um pouco, vamos verificar esse outro trecho de código:

listaDeCursos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          Curso curso = (Curso) parent.getItemAtPosition(position);
          Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(curso.getUrl()));
          startActivity(intent);

     }
});

Como será que podemos melhorar essa implementação toda? Vamos criar um método que descreve o que esse listener representa:

public void acessaUrl() {

}

E agora? O que faremos? Da mesma forma que usamos uma anotação para pegar a referência de uma View, usaremos a @OnItemClick para representar o clique de um elemento de uma lista passando o id dessa lista por parâmetro:

@OnItemClick(R.id.lista)
public void acessaUrl(int position) {

}

Por fim, basta apenas transferirmos o mesmo código dentro do antigo listener para dentro do método acessarUrl():

@OnItemClick(R.id.lista)
public void acessaUrl(int position) {
     Curso curso = (Curso) parent.getItemAtPosition(position);
     Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(curso.getUrl()));
     startActivity(intent);
}

Pronto! Já podemos até apagar o listener antigo. Se testarmos nossa app novamente:

tela-lista-personalizada2

abrindo-pagina

A nossa app funciona normalmente! Porém agora vamos ver como ficou a nossa Activity:


public class ListaDeCursosActivity extends AppCompatActivity {


    @Bind(R.id.lista)
    ListView listaDeCursos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lista_de_cursos);

        ButterKnife.bind(this);

        List<Curso> cursos = todosOsCursos();

        AdapterCursosPersonalizado adapter = new AdapterCursosPersonalizado(cursos, this);

        listaDeCursos.setAdapter(adapter);

    }

    @OnItemClick(R.id.lista)
    public void acessaUrl(int position) {
        Curso curso = (Curso) listaDeCursos.getItemAtPosition(position);
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(curso.getUrl()));
        startActivity(intent);
    }

    //métodos

}

O que achou? Bem mais elegante e de fácil compreensão! O Butter Knife é uma biblioteca que faz a “injeção” de View que é justamente pegar todas as referências das Views de uma Activity e permitir o acesso por meio de anotações que facilitam, e tanto, a nossa vida!

O que você achou do Butter Knife? Gostaria de aprender mais sobre android? Que tal dar uma olhada nos cursos de Android do Alura? Compartilhe conosco, nos comentários, sobre quais frameworks/libs para Android você já utilizou!


Content Editor at Alura and Software Developer

  • AVELINO

    Interessantíssimo.

  • Yuri Cavalcante

    Muito bom o post e a lib é realmente excelente para deixar o código mais limpo e legível.

    • Alex Felipe

      Obrigado pelo feedback Yuri! Realmente facilita a nossa vida na hora de desenvolver Apps 😀

Próximo ArtigoMesma rota, parâmetros diferentes