Обробка логіки рядків / розділів, схожа на UITableView iOS, не така проста в Android, як в iOS, однак, коли ви використовуєте RecyclerView - гнучкість того, що ви можете зробити, набагато більша.
Зрештою, все полягає в тому, як розібратися, який тип подання відображається в адаптері. Після того, як ви це зрозуміли, плавати слід легко (не дуже, але принаймні у вас буде це відсортовано).
Адаптер розкриває два способи, які слід перекрити:
getItemViewType(int position)
Реалізація цього методу за замовчуванням завжди повертатиме 0, що вказує на те, що існує лише 1 тип перегляду. У вашому випадку це не так, і вам знадобиться знайти спосіб стверджувати, який рядок відповідає якому типу перегляду. На відміну від iOS, який керує цим для вас за допомогою рядків і розділів, тут у вас буде лише один індекс, на який можна покластися, і вам потрібно буде використовувати свої навички розробника, щоб знати, коли позиція корелює з заголовком розділу та коли вона співвідноситься з звичайний ряд.
createViewHolder(ViewGroup parent, int viewType)
Цей спосіб у будь-якому разі потрібно замінити, але зазвичай люди просто ігнорують параметр viewType. Відповідно до типу перегляду, вам потрібно завищити правильний ресурс компонування та створити відповідний власник перегляду відповідно. RecyclerView обробляє переробку різних типів перегляду таким чином, щоб уникнути зіткнення різних типів перегляду.
Якщо ви плануєте використовувати програму LayoutManager за замовчуванням, наприклад LinearLayoutManager
, вам слід погодитися . Якщо ви плануєте зробити власну реалізацію LayoutManager, вам доведеться попрацювати трохи важче. Єдиний API, з яким ви дійсно маєте працювати, - це findViewByPosition(int position)
який дає заданий вигляд у певній позиції. Оскільки ви, мабуть, захочете викласти його по-різному, залежно від типу цього виду, у вас є кілька варіантів:
Зазвичай, використовуючи шаблон ViewHolder, ви встановлюєте тег перегляду разом із власником перегляду. Ви можете використовувати це під час виконання роботи в диспетчері макетів, щоб дізнатися, який тип подання, додавши поле у власнику подання, яке виражає це.
Оскільки вам знадобиться функція, яка визначає, яка позиція корелює з типом перегляду, ви також можете зробити цей метод глобально доступним якось (можливо, однокласний клас, який управляє даними?), А потім ви можете просто запитати той самий метод відповідно до положення.
Ось зразок коду:
// 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;
}
}
Ось зразок того, як повинні виглядати ці власники перегляду:
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());
}
}
Звичайно, цей зразок передбачає, що ви сконструювали джерело даних (myData) таким чином, що таким чином полегшується реалізація адаптера. Як приклад, я покажу вам, як я побудував би джерело даних, яке відображає список імен та заголовок кожного разу, коли перша літера імені змінюється (припустимо, що список буквений) - аналогічно тому, як контакти список виглядатиме так:
// 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();
}
Цей приклад вирішує досить конкретну проблему, але я сподіваюся, що це дає хороший огляд того, як обробляти різні типи рядків у переробнику, і дозволяє зробити необхідні адаптації у власному коді відповідно до ваших потреб.