Aumentando a produtividade no Android com o Butter Knife
Como melhorar a nossa produtividade de desenvolvimento de apps no Android ?
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);
}
});
}
}
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) {
convertView = 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":
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":
Selecionando o primeiro item, clique em "OK":
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 View
s, 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 View
s. 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:
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 View
s 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 na formação Android aqui da Alura?