【问题标题】:Show values of same key in one TextView in Recyclerview在 Recyclerview 的一个 TextView 中显示相同键的值
【发布时间】:2020-07-23 00:54:54
【问题描述】:

我在 GSON 和 Retrofit 中解析了以下 JSON。我想在一个 TextView 中显示相同 id 的值。现在发生的事情是所有值都添加到数组中,并且它们显示在单独的 TextView 中。我想在一个 TextView 中显示所有具有相同 id 的值。例如。 id: 240 ab 值应该在一个 TextView 中。目前,所有ab 值都在单独的TextView 中。

这是数据当前的显示方式:

这就是我希望数据的样子:

JSON::

{
  "abc": {
    "1": {
      "1": {
        "ab": "some content",
        "id": "240",
        "key": "value"
      },
      "2": {
        "ab": "some content",
        "id": "240",
        "key": "value"
      },
      "3": {
        "ab": "some content",
        "id": "240",
        "key": "value"
      }
    },
    "2": {
      "1": {
        "ab": "more content",
        "id": "241",
        "key": "value"
      },
      "2": {
        "ab": "more content 1",
        "id": "241",
        "key": "value"
      },
      "3": {
        "ab": "more content 2",
        "id": "241",
        "key": "value"
      }
    }
  }
}

POJO内容::

public class POJOContent {

    @SerializedName("ab")
    public String content;

    @SerializedName("id")
    public String id;

    @SerializedName("key")
    public String key;

    @Override
    public String toString() {
        return content;
    }

    //getters and setters

}

MyContentWrapper::

public class MyContentWrapper {
    public Map<Integer, MyMap> abc;
}

我的地图::

public class MyMap extends HashMap<Integer, POJOContent> {
    @Override
    public POJOContent put(Integer key, POJOContent value) {
        if(null==value.getContent() || value.getContent().isBlank()) {
            return null;
        }
        // Added only if content = "ab" is not blank.
        return super.put(key, value);
    }
}

回调:

    Callback<MyContentWrapper> myCallback = new Callback<MyContentWrapper>() {
                @Override
                public void onResponse(Call<MyContentWrapper> call, Response<MyContentWrapper> response) {
                    if (response.isSuccessful()) {
                        Log.d("Callback", " Message: " + response.raw());
                        Log.d("Callback", " Message: " + response.body().abc.values());

                        MyContentWrapper contentWrapper = response.body();

                        List<POJOContent> pojo = new ArrayList<>();

                        for (Map.Entry<Integer, MyMap> entry : contentWrapper.abc.entrySet()) {
                            Integer key = entry.getKey();
                            MyMap map = entry.getValue();
                            if (!map.isEmpty()){
                                Log.d("Callback", " Key: " + key);
                                Log.d("Callback", " Value: " + map.values());

                                pojo.addAll(map.values());
                            }
                        }

                        MyContentViewAdapter adapter = new MyContentViewAdapter(pojo);
                        recyclerView.setAdapter(adapter);
                    } else {
                        Log.d("Callback", "Code: " + response.code() + " Message: " + response.message());
                    }
                }
                @Override
                public void onFailure(Call<MyContentWrapper> call, Throwable t) {
                    t.printStackTrace();
                }
            };

RecyclerAdapter::

                public class MyContentViewAdapter extends RecyclerView.Adapter<MyContentViewAdapter.ViewHolder> {
    private List<POJOContent> data;
    private MyClickListener clickListener;


    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView text;
        private LinearLayout itemLayout;

        public ViewHolder(View v) {
            super(v);
            text = (TextView) v.findViewById(R.id.text_content);
        }
    }

    public MyContentViewAdapter(List<POJOContent> data) {
        this.data = data;
        Log.d("Recyclerview Data", data.toString());
    }


    @Override
    public MyContentViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_content_card, parent, false);
        return new MyContentViewAdapter.ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(MyContentViewAdapter.ViewHolder holder, int position) {
        POJOContent pojo = data.get(position);
        Log.d("Recyclerview", pojo.getContent());
        holder.text.setText(pojo.getContent());
        holder.itemView.setTag(pojo.getContent());
    }

    public void setOnItemClickListener(MyClickListener clickListener) {
        this.clickListener = clickListener;
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

}

【问题讨论】:

  • 您能分享一下您现在拥有的以及您正在努力实现的目标的屏幕截图吗?我不明白你的问题是什么......因为你在 ViewHolder 中只有一个 TextView,你说它们显示在单独的 TexViews 中是什么意思?也许您希望在单个 ViewHolder 中显示具有相同 id 的值?
  • @Rinat,是的,这就是我想要的。
  • 在“abc”键之后的 json 中,在“1”下:键有 3 个 id 为 240 的元素,在“2”键下有 3 个 id 为 241 的元素。我的问题是它保证所有具有相同 id 的元素都以相同的模式分组?
  • 以及像“1”、“2”、“3”这样的嵌套数字键,你是否重命名了它们,这样你就不会泄露敏感信息,或者它是来自服务器的真实 json 响应的真实结构?因为如果他们有像“产品”“子产品”这样的常量名称,可能会更容易达到预期的结果
  • @Rinat,保证所有具有相同 id 的元素都被分组。那些嵌套的数字键就像“1”、“2”、“3”……我没有重命名它们。不幸的是,我无法控制 json 的结构。

标签: android android-recyclerview pojo


【解决方案1】:

编辑:

我在ViewHolder 中添加了嵌套的RecyclerView,因此内容和值字段将动态显示。我正在添加 2 Adapter 的完整代码和 2 个ViewHolder 类,2 个 xml 布局和屏幕截图

我很确定它也会非常顺利地运行在非常大的列表中。

ID(240,241) 下的所有内容都是另一个 recyclerView。

这个想法是 list 的大小,为了让适配器自己填充,应该与不同 id 的数量一样多,这样只有那么多 Viewholders 被夸大了。

 List<List<POJOContent>> listOfPojoLists = new ArrayList<>();

    for (Map.Entry<Integer, MyMap> entry : contentWrapper.abc.entrySet()) {
        Integer key = entry.getKey();
        MyMap map = entry.getValue();
        if (!map.isEmpty()){
            Log.d("Callback", " Key: " + key);
            Log.d("Callback", " Value: " + map.values());

            listOfPojoLists.add(new ArrayList<>(map.values()));
        }
    }

    MyContentViewAdapter adapter = new MyContentViewAdapter(listOfPojoLists);
    recyclerView.setAdapter(adapter);

MyContentViewAdapter.java

public class MyContentViewAdapter extends RecyclerView.Adapter<MyContentViewAdapter.ViewHolder> {
private List<List<POJOContent>> data;
private MyClickListener clickListener;


MyContentViewAdapter(List<List<POJOContent>> data) {
    this.data = data;
}

@Override
public MyContentViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.fragment_content_card, parent, false);
    return new MyContentViewAdapter.ViewHolder(v);
}

@Override
public void onBindViewHolder(MyContentViewAdapter.ViewHolder holder, int position) {
    holder.bind(data.get(position));
}

public void setOnItemClickListener(MyClickListener clickListener) {
    this.clickListener = clickListener;
}

@Override
public int getItemCount() {
    return data.size();
}


class ViewHolder extends RecyclerView.ViewHolder {

    private TextView textId;
    private InnerListAdapter innerAdapter;

    // inside constructor we are initializing inner recyclerView and inner Adapter.
    // there will only be 3 - 5 instances of them ever created(using this 
    // particular viewHolder layouts), no more.
    // might be slightly more if u use layouts with lower height
    ViewHolder(View v) {
        super(v);
        textId =  v.findViewById(R.id.tv_Id);
        RecyclerView innerRecycler = v.findViewById(R.id.rv_inner_list);
        // I added DividerItemDecoration so it would be clear that there are actually different viewHolders
        // displayed by recyclerView
        innerRecycler.addItemDecoration(new DividerItemDecoration(v.getContext(), DividerItemDecoration.VERTICAL));
        innerAdapter = new InnerListAdapter();
        innerRecycler.setAdapter(innerAdapter);
    }

    /* We just submit new list for our inner adapter
    so it will handle rebinding values to its viewHolders */
    void bind(List<POJOContent> pojoList){
        textId.setText(pojoList.get(0).id);
        innerAdapter.setNewItems(pojoList);
    }
}

}


InnerListAdapter.java

public class InnerListAdapter extends RecyclerView.Adapter<InnerListAdapter.InnerViewHolder> {

private List<POJOContent> items = new ArrayList<>();

@NonNull
@Override
public InnerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new InnerViewHolder(LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_inner_list, parent, false));
}

@Override
public void onBindViewHolder(@NonNull InnerViewHolder holder, int position) {
    holder.bind(items.get(position));
}

@Override
public int getItemCount() {
    return items.size();
}

void setNewItems(List<POJOContent> newItems){
    items.clear();
    items.addAll(newItems);
    notifyDataSetChanged();
}

class InnerViewHolder extends RecyclerView.ViewHolder{
    TextView tv_value;
    TextView tv_content;

    InnerViewHolder(@NonNull View itemView) {
        super(itemView);
        tv_value = itemView.findViewById(R.id.tv_value);
        tv_content = itemView.findViewById(R.id.tv_content);
    }

    void bind(POJOContent pojoContent){
        tv_value.setText(pojoContent.getKey());
        tv_content.setText(pojoContent.getContent());
    }
}

}


fragment_content_card.xml 主 recyclerView 视图的布局

    <?xml version="1.0" encoding="utf-8"?>
   <androidx.cardview.widget.CardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    android:padding="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tv_Id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:textSize="32sp"
            android:textColor="@color/black"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="ID" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_inner_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_Id" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

item_inner_list.xml 内部 recylerVoews 视图的布局

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:textColor="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="value" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="8dp"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_value"
        tools:text="content" />

</androidx.constraintlayout.widget.ConstraintLayout>

【讨论】:

  • 感谢您的回复,非常感谢。但是我不想使用 StringBuilder 将数据附加到一个视图中,因为我可能还想添加其他键/值对。我正在寻找的是将值作为嵌套数据添加到 Recyclerview 中。我想出了一种方法,那就是在 xml 中创建一个布局并在适配器中对其进行膨胀并将其添加到这个 recyclerview 布局中。
  • 但是我读到这不是最好的方法,最有效的方法是使用多个视图,因此每个动态数字整数都是“标题”,嵌套数据是标头的数据。对不起,如果问的太多了,你能帮我解决这个问题吗?
  • 我真的很愿意帮助你,如果我能帮助这么高分的人就太好了:) 但我不太明白你的意思:(
  • 嗯。那么在 StringBuilder 之前的一切都很好吗?或者“List>”也不可取?我想,也许你想要单个 ViewHolder 中每个“ab”(内容)的动态 texViews 数量?
  • 也许我们可以继续聊天?
【解决方案2】:

使用内容和键列表为 RV 列表项创建另一个 pojo 类

public class POJOListItem {
  public String id;
  public List<String> contents = new ArrayList<>();
  public List<String> keys = new ArrayList<>();
}

然后验证并将MyContentWrapper对象映射到List&lt;POJOListItem&gt;,检查列表是否包含id,如果它具有相同的id,则将它们添加到contentskeys列表中或创建一个新项目并添加它。根据列表大小在视图持有者内的容器布局中动态添加项目。

查看这个小型演示项目https://github.com/toxic-charger/SO-63045236

测试了 50000 多个项目,没有性能问题

【讨论】:

  • 我最初使用这种方法实际上已经解决了这个问题,但不确定在BindViewHolder 上扩展布局是否有效并且是最好的方法。我想知道是否可以使用类似于此的多种视图类型来做到这一点:stackoverflow.com/questions/26245139/…
  • @input 可以使用多种视图类型来实现...但在这种情况下,您需要创建具有不同数量的文本视图的多个 viewHolder。
  • @input 我已经更新了演示项目,所以不是每次都膨胀视图,而是将膨胀的视图列表存储在 View 标签中并从视图标签中检索视图列表并重用 github.com/toxic-charger/SO-63045236/commit/…跨度>
【解决方案3】:

根据您的问题,您有单独的问题需要解决:

1 - 更好地定义您拥有的数据。您当前拥有POJOContent,但您想显示类似POJOContentGroup 的内容,因此在进入视图部分之前,您需要预处理数据并将其从MyContentWrapper 映射到List&lt;POJOContentGroup&gt;

data class POJOContentGroup(val id: String, val contentList: List&lt;POJOContent&gt;)(为简洁起见,这是在 Kotlin 中)

2 - 有了前面的定义,现在您有一个简单的任务:显示POJOContentGroup 的列表。在此基础上创建您的回收站视图,类似于您最初所做的,不同之处在于项目布局将是 TextView 用于 idRecyclerView 用于 contentList,这是一个你目前有。

【讨论】:

  • 我有一个扩展 HashMap&lt;Integer, POJOContent&gt;MyMap 类。当我更改它List&lt;POJOContent&gt; 时,我收到以下错误:Expected BEGIN_ARRAY but was BEGIN_OBJECT
  • 您仍然需要MyMap,因为它是 JSON 数据的表示形式。我的建议是创建一个新类 POJOContentGroup 来表示您要显示的数据,然后将函数映射为从 MyContentWrapperList&lt;POJOContentGroup&gt;
【解决方案4】:

根据我对问题描述的理解,您正在尝试像这样构造数组

[
"some content"+"some content"+"some content", // id = 240
"more content"+"more content 1"+"more content 2" // id = 241
]

并且您想在 TextView 列表中显示这 2 个值

但是你构造的数组会是,

[
"some content",
"some content",
"some content",
"more content",
"more content 1",
"more content 2"
]

因此每个条目都显示在单独的 TextView 中

要进一步调试,请检查pojo列表的值

pojo.addAll(map.values())
// may be if you want to group entries with comma delimiter, use
// pojo.addAll(TextUtils.join(", ", map.values()))

【讨论】:

  • 是的,这正是数组的构造方式,但我希望数组的构造方式与您在第一个数组中演示的方式一样,并将其显示在单个视图中。
  • 在添加到 pojo 列表之​​前,您需要将 map.values() 合并为单个值。我在最后一行“TextUtils.join(”, “, map.values())”中提到了用逗号合并值。请检查它是否适用于您的用例。
  • 感谢您的回复,但是有没有办法通过膨胀 textview 并添加嵌套数据来做到这一点?
  • 我添加了两张图片以更好地传达我希望输出显示的方式。希望这能说明我的问题。
【解决方案5】:

你好,很晚了,但我想说创建一个类 ListItem

class ListItem {
    private var item: HashMap<Int, POJOContent>? = null
}

并覆盖您的 POJOContent 中的 toString 以返回您希望在文本视图上显示的值,例如

@Override
public String toString() {
    return value + "\n" + content;
}

然后在你的适配器中使用 List 然后在绑定中

//take care of null checks and you can use System.getProperty("line.separator"); in place of line saperator also don't forgate to set maxLine property of your text view to eome higher value
void bind(ListItem item){
    Array<POJOContent> contents = item.values().toArray()
    tv_value.setText(contents[0].getKey());
    tv_content.setText(contents.toString().replace("[","").replace("]","").replace(",","\n"));
}

【讨论】:

    【解决方案6】:

    我认为nested-expandable-recyclerview 应该是实现您的目标的正确选择,这也将增加您的用户体验。默认情况下可以使用您的 JSON 子数据打开嵌套回收器。

    【讨论】:

      猜你喜欢
      • 2020-06-09
      • 2019-02-06
      • 1970-01-01
      • 1970-01-01
      • 2013-03-27
      • 2018-11-22
      • 1970-01-01
      • 1970-01-01
      • 2020-07-31
      相关资源
      最近更新 更多