Show values of same key in one TextView in Recyclerview

℡╲_俬逩灬. 提交于 2021-01-27 11:29:53

问题


I have the following JSON which I'm parsing in GSON and Retrofit. I want to display the values of the same id in one TextView. What's happening right now is that all the values are added to the array and they are being displayed in separate TextViews. I want to show all values which have the same id to be displayed in one TextView. For eg. id: 240 ab values should be in one TextView. Currently, all ab values are in separate TextView.

This is how the data is currently displaying:

This is how I want the data to be:

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"
      }
    }
  }
}

POJOContent::

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;
}

MyMap::

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:

    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();
    }

}

回答1:


EDIT:

I added nested RecyclerView inside ViewHolder, so the content and value fields will be displayed dynamically. I'm Adding full code of 2 Adapter and 2 ViewHolder classes, 2 xml layouts and screenshot

I'm pretty sure it will run really smoothly with very large list too.

Everything under ID(240,241) is another recyclerView.

The idea is that list's size, for adapter to populate itself, should be as many as the number of distinct ids, so that only that many Viewholders are inflated.

 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 layout for main recyclerView viewholder

    <?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 layout for inner recylerVoews' viewholder

    <?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>



回答2:


create another pojo class for RV List Items with content and keys list

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

then validate and map MyContentWrapper object to List<POJOListItem>, check the list if it contains the id, if it has the same id then add them to the contents and keys list or create a new item and add it. based on the list size dynamically add item in a container layout inside the view holder.

Check out this small demo project https://github.com/toxic-charger/SO-63045236

Tested with 50000+ items no performance issus




回答3:


Based on your question, you have separate issues to tackle:

1 - Define better the data you have. You currently have POJOContent but you want to display something like a POJOContentGroup, so before getting to the view part you pre-process your data and map it from MyContentWrapper to List<POJOContentGroup>.

data class POJOContentGroup(val id: String, val contentList: List<POJOContent>) (This is in Kotlin for brevity)

2 - Having the previous defined, now you have a simple task: Display a list of POJOContentGroup. Create your recycler view based on that, similar to what you've done initially, with the difference that the item layout will be a TextView for the id and a RecyclerView for the contentList, which is the one you currently have.




回答4:


As per my understanding of problem description, you are trying to construct array like this

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

and you want to show these 2 values in list of TextView

but the array you constructed will be,

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

and hence each entry is displayed in separate TextView

To debug further, please check value of pojo list after

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



回答5:


Hi its late but i would say create one class ListItem

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

and overwrite toString in your POJOContent to return the values you want to display on text view like

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

Then use List in your adapter Then in bind

//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:


I think nested-expandable-recyclerview should be the right choice to achieve your goal, which will also increase your user experience. By default can open the nested recycler with your child data of the JSON.



来源:https://stackoverflow.com/questions/63045236/show-values-of-same-key-in-one-textview-in-recyclerview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!