Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple row of chips in chipgroup

I'm fetching a list from my API of for example 25 tags of country such as, Germany, England, France, Italy etc... I'd like to have 2 row with 10 chips per row, and if I get 30 tags the next time I fetch I want to have 3 row of 10 chips etc...

So far I haven't found anything allowing me to do that. I've taken a quick look at Flexbox-Layout but it doesn't seem to fit my needs, I currently have the code below, but I was thinking of doing such logic with a Recyclerview

Fragment

viewModel.videoSelected.observe(viewLifecycleOwner, object : Observer<VideoPage> {
            override fun onChanged(videoPage: VideoPage?) {
                videoPage?.tags ?: return
                val chipGroup = binding.chipGroup
                val chipGroupInflater = LayoutInflater.from(chipGroup.context)
                val children = videoPage.tags.map { tagName ->
                    val chip = chipGroupInflater.inflate(R.layout.chip_video_tag, chipGroup, false) as Chip
                    chip.text = tagName
                    chip.tag = tagName
                    chip.setOnClickListener {
                        Toast.makeText(context, tagName, Toast.LENGTH_SHORT).show()
                    }

                    chip
                }

                for (chip in children) {
                    chipGroup.addView(chip)
                }
            }
        })

and the result is the 25 chips in one line. How can I make it so that it's divide on multiple line ?

like image 286
Biscuit Avatar asked May 09 '20 23:05

Biscuit


2 Answers

As commented you can use Flexbox LayoutManager with layout_wrapBefore flag on chips 11,21,etc

or it is also possible with a standard recyclerview if you reformat your data.

To do it with a standard recyclerview you need to arrange your data in a 2D data structure, this can be an ArrayList of Arraylists or with Arrays or a set of POJO classes

In the outer Arraylist would be the rows and the inner Arraylist you would have the items to display in the row.

So the RecyclerView item is just a Horizontal Linear Layout to produce the row, to which you add your Chips from the Inner ArrayList

example using a generated 2D structure

MainActivity

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private RAdapter adapter;
    private ArrayList<ArrayList<String>> rowsArrayList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);

        rowsArrayList = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            ArrayList<String> row = new ArrayList<>();
            for (int j = 1; j <= 10; j++) {
                row.add(String.valueOf(j));
            }
            rowsArrayList.add(row);
        }

        adapter = new RAdapter(this, rowsArrayList);
        recyclerView.setAdapter(adapter);
    }
}

RAdapter

public class RAdapter extends RecyclerView.Adapter<RAdapter.RHolder>{

    class RHolder extends RecyclerView.ViewHolder {
        private LinearLayout line;

        public RHolder(View itemView) {
            super(itemView);
            line = itemView.findViewById(R.id.line);
        }
    }

    private Context context;
    private ArrayList<ArrayList<String>> rowsArrayList;

    public RAdapter(Context context, ArrayList<ArrayList<String>> rowsArrayList) {
        this.context = context;
        this.rowsArrayList = rowsArrayList;
    }

    @Override
    public RHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout, parent, false);
        return new RHolder(view);
    }

    @Override
    public void onBindViewHolder(RHolder holder, int position) {
        // Because we might be recycling the LinearLayout that might have had chips added to it already
        holder.line.removeAllViews();
        ArrayList<String> row = rowsArrayList.get(position);
        for (String text: row) {
            Chip chip = new Chip(context);
            chip.setText(text);
            holder.line.addView(chip);
        }
    }

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

}

row_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/line">

</LinearLayout>

This Produces
screenshot

Update:
With a bit more Maths you can do it without re-arranging the data

Showing 25 items to cover not full row usecase

MainActivity

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private RAdapter adapter;
    private ArrayList<String> itemsArrayList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);

        itemsArrayList = new ArrayList<>();
        for (int i = 1; i <= 25; i++) {
            itemsArrayList.add(String.valueOf(i));
        }

        adapter = new RAdapter(this, itemsArrayList);
        recyclerView.setAdapter(adapter);
    }
}

RAdapter

public class RAdapter extends RecyclerView.Adapter<RAdapter.RHolder>{

    class RHolder extends RecyclerView.ViewHolder {
        private LinearLayout line;

        public RHolder(View itemView) {
            super(itemView);
            line = itemView.findViewById(R.id.line);
        }
    }

    private Context context;
    private ArrayList<String> itemsArrayList;
    private int itemsPerRow = 10;

    public RAdapter(Context context, ArrayList<String> itemsArrayList) {
        this.context = context;
        this.itemsArrayList = itemsArrayList;
    }

    @Override
    public RHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout, parent, false);
        return new RHolder(view);
    }

    @Override
    public void onBindViewHolder(RHolder holder, int position) {
        // Because we might be recycling the LinearLayout that might have had chips added to it already
        holder.line.removeAllViews();
        int adjustedPosition = position + 1;
        int rangeEnd = Math.min(itemsArrayList.size(), adjustedPosition * itemsPerRow);
        int rangeStart = Math.max((position * itemsPerRow) + 1, rangeEnd - itemsPerRow);
        for (int i = rangeStart; i <= rangeEnd; i++) {
            int arrayPositionAdjusted = i - 1;
            Chip chip = new Chip(context);
            chip.setText(itemsArrayList.get(arrayPositionAdjusted));
            holder.line.addView(chip);
        }
    }

    @Override
    public int getItemCount() {
        return return (int) Math.ceil(itemsArrayList.size()/ (double) itemsPerRow);
    }

}

Produces
screenshot2

like image 73
Andrew Avatar answered Nov 20 '22 22:11

Andrew


I've also manage to do it in a more simple way with BindingAdapter:

 <androidx.recyclerview.widget.RecyclerView
                    app:listVideoTagChip="@{viewModel.videoSelected.tags}"
                    android:id="@+id/rv_video_chips"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager"
                    android:orientation="horizontal"
                    tools:listitem="@layout/chip_video_tag"/>
private const val TAG_PER_ROW = 10

@BindingAdapter("listVideoTagChip")
fun RecyclerView.bindRecyclerView(data: List<String>?) {
    val adapter: VideoTagAdapter = this.adapter as VideoTagAdapter
    (layoutManager as StaggeredGridLayoutManager).spanCount =
        data?.size?.let {
            ceil(it.toDouble().div(TAG_PER_ROW)).toInt()
        } ?: 1
    adapter.submitList(data)
}
like image 1
Biscuit Avatar answered Nov 20 '22 20:11

Biscuit