У меня есть приложение, которое отправляет изображения на наш сервер и получает информацию о том, что идентифицировал сервер. Иногда сервер ничего не идентифицирует (неинтересный случай), иногда одно, иногда более одного.
Таким образом, данные, которые я должен показать в списке (RecyclerView), представляют собой фотографию (плюс отметку времени), а затем одну или несколько «вещей», идентифицированных сервером.
Моя структура данных выглядит так:
List<List<Thing>> masterList;
Итак, у меня есть RecyclerView с двумя возможными элементами: либо элемент с одной вещью, где я показываю изображение, время и информацию о вещи, либо элемент с несколькими вещами, где я показываю изображение, отметку времени и список вещей.
Вот адаптер более высокого уровня
public class ResultsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<List<Thing>> resultList;
private Context context;
public ResultsAdapter(List<List<Thing>> resultList) {
this.resultList = resultList;
}
@Override
@NonNull
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
RecyclerView.ViewHolder holder;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case 0: // regular single thing card
{
View itemView = inflater.inflate(R.layout.thing_item, parent, false);
holder = new ResultsHolder(itemView, listener);
break;
}
default: // multi thing card
{
View itemView = inflater.inflate(R.layout.compound_item, parent, false);
holder = new MultiThingResultsHolder(itemView, listener);
break;
}
}
return holder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
Resources res = context.getResources();
switch (viewHolder.getItemViewType()) {
case 0: // regular single thing card
{
ResultsHolder holder = (ResultsHolder) viewHolder;
holder.time.setText(resultList.get(position).get(0).getTime());
if (resultList.get(position).get(0).getImage() != null) {
File thumbnailFile = new File(context.getFilesDir(), resultList.get(position).get(0).getImage());
holder.thumbnail.setImageURI(Uri.fromFile(thumbnailFile));
}
holder.amount.setText(res.getString(R.string.format_blahs, resultList.get(position).get(0).getAmount()));
.
.
.
.
break;
}
case 2: // multi thing card
{
MultiResultsHolder holder = (MultiResultsHolder) viewHolder;
holder.time.setText(resultList.get(position).get(0).getTime());
if (resultList.get(position).get(0).getImage() != null) {
File thumbnailFile = new File(context.getFilesDir(), resultList.get(position).get(0).getImage());
holder.thumbnail.setImageURI(Uri.fromFile(thumbnailFile));
}
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
holder.list.setLayoutManager(layoutManager);
List<Thing> subThingList = resultList.get(position);
MultiResultsAdapter adapter = new MultiResultsAdapter(subThingList, subListener, position);
holder.list.setAdapter(adapter);
break;
}
}
}
@Override
public int getItemViewType(int position) {
if (resultList.get(position).size() == 1) {
// this is a simple item
return 0;
}
// if we got here it's a multithing item
return 1;
}
@Override
public int getItemCount() {
return resultList.size();
}
public class ResultsHolder extends RecyclerView.ViewHolder {
private ImageView deleteX;
public TextView time;
private ImageView thumbnail;
private TextView amount;
.
.
.
.
private ResultsHolder(View view) {
super(view);
deleteX = view.findViewById(R.id.delete_button);
deleteX.setOnClickListener(this);
time = view.findViewById(R.id.tv_time);
thumbnail = view.findViewById(R.id.thumbnail);
amount = view.findViewById(R.id.tv_amount);
.
.
.
.
}
}
private class MultiResultsHolder extends RecyclerView.ViewHolder {
private ImageView thumbnail;
public TextView time;
public RecyclerView list;
private MultiResultsHolder(View view) {
super(view);
time = view.findViewById(R.id.tv_time);
list = view.findViewById(R.id.list); // <-- the inner Recycler
thumbnail = view.findViewById(R.id.thumbnail);
}
}
}
Итак, у меня есть внутренний адаптер, который работает на простом List<Thing>
public class MultiResultsAdapter extends RecyclerView.Adapter<MultiResultsAdapter.MultiResultsHolder> {
private List<Thing> thingList;
private Context context;
public MultiResultsAdapter(List<Thing> thingList) {
this.thingList = thingList;
}
@NonNull
@Override
public MultiResultsHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
context = parent.getContext();
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_sub_item, parent, false);
return new MultiResultsHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull MultiResultsHolder holder, int position) {
Resources res = context.getResources();
holder.amount.setText(res.getString(R.string.format_blah, thingList.get(position).getAmount()));
.
.
.
.
}
@Override
public int getItemCount() {
return thingList.size();
}
public class MultiResultsHolder extends RecyclerView.ViewHolder {
private ImageView deleteX;
private TextView amount;
private TextView fatAmount;
.
.
.
.
private MultiResultsHolder(View view) {
super(view);
deleteX = view.findViewById(R.id.delete_button);
deleteX.setOnClickListener(this);
amount = view.findViewById(R.id.tv_amount);
.
.
.
}
}
}
Итак, при запуске элементы Single-thing отображаются просто отлично. Элементы Multi-thing отображаются с верхней частью (которая представляет собой изображение и отметку времени и достаточный размер, отведенный для остальной части списка (сколько бы элементов в нем ни было), но остальная часть пуста. Код для внутреннего вызывается второй адаптер, и я вижу, что onBindViewHolder
вставляет правильные значения для всех подэлементов, но они не отображаются!
Там просто большой элемент с пустым пространством.
Отдельные элементы до и после просто прекрасны, общая прокрутка в порядке.
Я видел ответ SO, в котором предлагалось использовать CustomLinearLayoutManager для внутреннего RecyclerView, например, здесь. Это не помогло, но я увидел интересное исключение — при попытке измерить высоту элементов вызов recycler.getViewForPosition(0)
выдает исключение за пределами границ, потому что каким-то образом RecycleView.Recycler не знает, что в списке есть какие-либо элементы. думал, что это может быть подсказкой, но я не знаю, как ее использовать, чтобы помочь
Есть идеи?
Спасибо, Виталий, это была моя глупая ошибка.
Вау — еще одна дурь — оказалось, что я не сделал линейный макет для своего составного элемента «ориентация: вертикаль» — и это то, что меня напортачило — ну. это исправляет это
попробуй посмотреть на этот github.com/vivchar/RendererRecyclerViewAdapter/wiki/…