Lidar com a lógica de linhas / seções semelhante ao UITableView do iOS não é tão simples no Android quanto no iOS, no entanto, quando você usa o RecyclerView - a flexibilidade do que você pode fazer é muito maior.
No final, é tudo sobre como você descobre que tipo de exibição está exibindo no adaptador. Depois que você descobrir isso, deve ser fácil navegar (não realmente, mas pelo menos você terá isso resolvido).
O adaptador expõe dois métodos que você deve substituir:
getItemViewType(int position)
A implementação padrão desse método sempre retornará 0, indicando que há apenas 1 tipo de visualização. No seu caso, não é assim e, portanto, você precisará encontrar uma maneira de afirmar qual linha corresponde a qual tipo de visualização. Ao contrário do iOS, que gerencia isso para você com linhas e seções, aqui você terá apenas um índice em que confiar e precisará usar suas habilidades de desenvolvedor para saber quando uma posição se correlaciona com um cabeçalho de seção e quando se correlaciona com uma linha normal.
createViewHolder(ViewGroup parent, int viewType)
Você precisa substituir esse método de qualquer maneira, mas geralmente as pessoas simplesmente ignoram o parâmetro viewType. De acordo com o tipo de exibição, você precisará aumentar o recurso de layout correto e criar seu suporte de exibição de acordo. O RecyclerView lidará com a reciclagem de diferentes tipos de vistas, de forma a evitar conflitos de diferentes tipos de vistas.
Se você planeja usar um LayoutManager padrão, como LinearLayoutManager
você deve estar pronto. Se você planeja criar sua própria implementação do LayoutManager, precisará trabalhar um pouco mais. A única API com a qual você realmente precisa trabalhar é a findViewByPosition(int position)
que fornece uma determinada visualização em uma determinada posição. Como você provavelmente deseja definir de maneira diferente, dependendo do tipo dessa visualização, você tem algumas opções:
Geralmente, ao usar o padrão ViewHolder, você define a etiqueta da vista com o suporte da vista. Você pode usar isso durante o tempo de execução no gerenciador de layout para descobrir que tipo de visualização é adicionando um campo no suporte da visualização que expressa isso.
Como você precisará de uma função que determine qual posição se correlaciona a qual tipo de exibição, você também pode tornar esse método acessível globalmente de alguma forma (talvez uma classe singleton que gerencia os dados?) E, em seguida, você pode simplesmente consultar o mesmo método de acordo com a posição.
Aqui está um exemplo de código:
// in this sample, I use an object array to simulate the data of the list.
// I assume that if the object is a String, it means I should display a header with a basic title.
// If not, I assume it's a custom model object I created which I will use to bind my normal rows.
private Object[] myData;
public static final int ITEM_TYPE_NORMAL = 0;
public static final int ITEM_TYPE_HEADER = 1;
public class MyAdapter extends Adapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_NORMAL) {
View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
return new MyNormalViewHolder(normalView); // view holder for normal items
} else if (viewType == ITEM_TYPE_HEADER) {
View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
return new MyHeaderViewHolder(headerRow); // view holder for header items
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final int itemType = getItemViewType(position);
if (itemType == ITEM_TYPE_NORMAL) {
((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);
} else if (itemType == ITEM_TYPE_HEADER) {
((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);
}
}
@Override
public int getItemViewType(int position) {
if (myData[position] instanceof String) {
return ITEM_TYPE_HEADER;
} else {
return ITEM_TYPE_NORMAL;
}
}
@Override
public int getItemCount() {
return myData.length;
}
}
Aqui está uma amostra de como esses detentores de exibição devem se parecer:
public MyHeaderViewHolder extends ViewHolder {
private TextView headerLabel;
public MyHeaderViewHolder(View view) {
super(view);
headerLabel = (TextView)view.findViewById(R.id.headerLabel);
}
public void setHeaderText(String text) {
headerLabel.setText(text);
}
}
public MyNormalViewHolder extends ViewHolder {
private TextView titleLabel;
private TextView descriptionLabel;
public MyNormalViewHolder(View view) {
super(view);
titleLabel = (TextView)view.findViewById(R.id.titleLabel);
descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);
}
public void bindData(MyModel model) {
titleLabel.setText(model.getTitle());
descriptionLabel.setText(model.getDescription());
}
}
Obviamente, este exemplo assume que você construiu sua fonte de dados (myData) de uma maneira que facilita a implementação de um adaptador dessa maneira. Como exemplo, mostrarei como construir uma fonte de dados que mostre uma lista de nomes e um cabeçalho para cada vez que a primeira letra do nome for alterada (suponha que a lista esteja em ordem alfabética) - semelhante à maneira como os contatos lista ficaria assim:
// Assume names & descriptions are non-null and have the same length.
// Assume names are alphabetized
private void processDataSource(String[] names, String[] descriptions) {
String nextFirstLetter = "";
String currentFirstLetter;
List<Object> data = new ArrayList<Object>();
for (int i = 0; i < names.length; i++) {
currentFirstLetter = names[i].substring(0, 1); // get the 1st letter of the name
// if the first letter of this name is different from the last one, add a header row
if (!currentFirstLetter.equals(nextFirstLetter)) {
nextFirstLetter = currentFirstLetter;
data.add(nextFirstLetter);
}
data.add(new MyModel(names[i], descriptions[i]));
}
myData = data.toArray();
}
Este exemplo vem para resolver um problema bastante específico, mas espero que isso lhe dê uma boa visão geral sobre como lidar com diferentes tipos de linhas em uma recicladora e permita que você faça as adaptações necessárias em seu próprio código para atender às suas necessidades.