I am trying to create something similar to this:
RecyclerView
Instead of folders and files I want to have incomplete items and completed items.
I am new to RecyclerViews, how would I manage to get two unrelated lists such as folders and files into one RecyclerView that scrolls as one?
You could use heterogenous RecyclerView which supports more than one viewType or view holders. Your dataSet could be List<Object> or a marker class which supports the models Files and Folders for example and then you could do something like this in your adapter :
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class ViewHolderFolders extends RecyclerView.ViewHolder {
...
public ViewHolderFolders(View itemView){
...
}
}
class ViewHolderFiles extends RecyclerView.ViewHolder {
...
public ViewHolderFiles(View itemView){
...
}
#Override
public int getItemViewType(int position) {
//Let us say you return 0 for folders and 1 for files
//This is just an example you could write your own logic but make sure to differenciate the two
//Folders and Files in here are model class used to populate the
//recyclerview with. This is just an example.
if (yourDataSet.get(position) instanceof Folders) {
return 0;
} else{
return 1;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0: return new ViewHolderFolders(...);
case 1: return new ViewHolderFiles(...);
//Your code here
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolderFolders viewHolderFolders = (ViewHolderFolders)holder;
...
break;
case 1:
ViewHolderFiles viewHolderFiles = (ViewHolderFiles)holder;
...
break;
}
}
}
You can use a sectioned recyclerView for this. Where you can have section as header and each header with its own items.
Refer to this library: Sectioned RecyclerView
Related
I have a RecyclerView with some images,here i want to open different Activitys by clicking on different images...
So, i think using the switch statement in the onClick of the adapter will solve my problem but i don't know how to add a switch-if statement in a RecyclerView adapter.i am a beginer in android development so i need some help...
myadapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ImageViewHolder> {
#NonNull
private int[] images;
public RecyclerAdapter(int[] images){
this.images =images;
}
#Override
public ImageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item2,parent,false);
ImageViewHolder imageViewHolder = new ImageViewHolder(view);
return imageViewHolder;
}
#Override
public void onBindViewHolder(#NonNull ImageViewHolder holder, int position) {
int image_id =images[position];
holder.imagess.setImageResource(image_id);
}
#Override
public int getItemCount() {
return images.length;
}
public static class ImageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView imagess;
TextView titless;
public ImageViewHolder(View itemView) {
super(itemView);
imagess = itemView.findViewById(R.id.image);
titless = itemView.findViewById(R.id.title);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Toast.makeText(itemView.getContext(), "DOWNLOAD ANY TORRENT DOWNLOADER AND OPEN", Toast.LENGTH_LONG).show();
}
}}
So what i want is :
I want to open different activities if the user click the cat image,it should open a activity named cats and if the user clicks the dog image it should open a activity named dogs ...
#Override
public void onClick(View v) {
switch(getAdapterPosition()) {
case 0:
Intent intent = new Intent(context, Cat.class);
context.startActivity(intent);
break;
case 1: // Open second activity
};
}
Returns the Adapter position of the item represented by this ViewHolder.
I have very little experience with Java, I write only in Kotlin. Here is what I have come up with.
Setting the click-events within the Adapter itself is not the best practise.According to the recommended way you should add a callback method and let the Activity
\ Fragment to which the Recycler is attached handle after the click events.
How to Proceed,
Step 1: Create an Interface which loosely binds your Adapter to Activity or Fragment.
interface AdapterListener{
void afterAdapterItemClicked(int adapterPosition);
}
This Interface can be created within Adapter itself as an inner member.
Step 2: Let the Activity or Fragment to which the Recycler is attached implement this Interface,So let assume your Activity is named as MenuActivity
class MenuActivity extends Activity implements AdapterListener{
}
Step 3: Now inside the Activity / Fragment implement the override method
#Override
void afterAdapterItemClicked(int adapterPosition){
switch(adapterPosition) {
case 0: // Move to activity1
break;
case 1: // Move to activity2
break;
}
}
Step 4 : Now calling the method afterAdapterClicked() after the click event
public static class ImageViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
ImageView imagess;
TextView titless;
public ImageViewHolder(View itemView) {
super(itemView);
imagess = itemView.findViewById(R.id.image);
titless = itemView.findViewById(R.id.title);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.afterAdapterItemClicked(getAdapterPosition());
}
}
Step 5: Now to all the curious faces thinking, where in the world mListener landed from, don't worry I saved it for the last.
Now when you create the RecyclerAdapter object(instance) inside your Activity / Fragment you need to pass the current context or this in its constructor.
RecyclerAdapter(arrayOfImages,this);
Now create a new state variable inside your RecyclerAdapter class such as
private AdapterListener mListener;
And then in the constructor of RecyclerAdapter you need to add a variable of type
AdapterListener like this and then assign mListener the received value
public RecyclerAdapter(int[] images,AdapterListener mListener){
this.images = images;
this.mListener = mListener;
}
And then use mListener inside your inner class ImageViewHolder.
I am implementing recyclerview with multiple layouts.Usually we have multiple viewholders for different layouts and override other methods as per the required layout.I have successfully implemented this.But now i have a different scenario like: A recyclerview that shows some videos (say 3) then another layout(say layout x), again 3 videos and then again layout x and so on.Suppose i have 10 videos then in this case the itemcount would be 10 + 3 as 3 layout x would be displayed.But the videos are loaded while scrolling.So how can i determine the number of views to return in getItemCount();
I mean
#Override
public int getItemCount() {
return ListofVideos.size() + "WHAT??"
}
layout is like this
If all the videos are loaded at at once then it is easy to calculate the number of views like if i have 21 videos i would have total 27 views(i.e 21 videos and 6 layout X views). But when the list is loaded on scroll how can i determine the number of views?
Your Adapter is responsible to populate view so it has all views of your RecyclerView while your ListofVideos (may) have only video links.
Whenever you scroll your RecyclerView, Adapter is responsible to inflate views.
What you should do?
Create an interface
public interface BaseItem {
int ITEM_TYPE_HEADER = 0;
int ITEM_TYPE_SUB_HEADER = 1;
int ITEM_TYPE_ROW_NORMAL = 2;
int getItemType();
}
And implement this interface with your adapter's video item like
public class YourAdapterVideoItem implements BaseItem {
// rest of your code
#Override
public int getItemType() {
return ITEM_TYPE_ROW_NORMAL;
}
}
Create your adapter's header item
public class YourAdapterHeaderItem implements BaseItem {
// rest of your code
#Override
public int getItemType() {
return ITEM_TYPE_HEADER;
}
}
Update your adapter with
public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<BaseItem> items = new ArrayList<BaseItem>();
#Override
public BaseRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseRecyclerViewHolder holder;
switch (viewType) {
case BaseItem.ITEM_TYPE_ROW_NORMAL:
default:
// inflate your default items
break;
case BaseItem.ITEM_TYPE_HEADER:
// inflate your default items
break;
}
return holder;
}
#Override
public void onBindViewHolder(BaseRecyclerViewHolder viewHolder, int position) {
BaseItem base = getItemAt(position);
switch (base.getItemType()) {
case BaseItem.ITEM_TYPE_HEADER:
// populate your header view
break;
case BaseItem.ITEM_TYPE_ROW_NORMAL:
// populate your actual view
break;
}
}
#Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
#Override
public int getItemViewType(int position) {
return getItemAt(position).getItemType();
}
public BaseItem getItemAt(int position) {
return items == null ? null : items.get(position);
}
}
When you want to add header use YourAdapterHeaderItem for your videos use YourAdapterVideoItem.
Hope this helps
Edit
For adding headers in GridLayoutManager have a look at RecyclerView GridLayoutManager with full width header
I am using Recyclerview to display a list of item in my android application. I am giving the option for the user to switch between list and grid. That is working fine but the grid layout is fine but for list it is not good.
I want to use different layout when the user is switched to List. Please any one tell me how I can achieve that from Recyclerview.
FYI :- I am using mRecyclerView.setLayoutManager(mLayoutManager); to switch between grid and list
You can add a flag in your adapter and notify the adapter to update the view when you change your LayoutDataManager.
private static final int TYPE_LINEAR = 0;
private static final int TYPE_GRID = 1;
private int type;
public void setType(int type) {
this.type = type;
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
return type;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_LINEAR:
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_linear), null);
case TYPE_GRID:
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid), null);
}
return null;
}
Hope it helps:)
i want to show list as per image for that i am using recycler view and showing row its easy .but inside each row i want to showing many rows
say
i have 10 rows and each row has different row inside
so 1 row have 3 rows where as 2nd have 2 as on
so what is best way to do this
is it possible we can have one more listview inside that row ?
or inside onBindViewHolder i have to manually loop
and inflate layout
Edit :-
when i am trying this is always shuffles
#Override
public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
for (int i = 0; i < position; i++) {
View c = ((Activity) mContext).getLayoutInflater().inflate(R.layout.row2, null);
// ((TextView) c.findViewById(R.id.mis)).setText(data.get(position) + "");
holder.inner.addView(c);
}
holder.n.setText(position+"");
holder.itemView.setTag(position);
}
image as follows
Yes you can use recyclerview inside recycler view just need to maintain separate adapter for that.
Or in this case you can also use expandable list view which will be much easier to use in this case.
If in your case, you don't have many rows, you can apply this:
Use NestedScrollview and add 2 RecyclerViews inside of it.
If you have specific number of rows like 2-3, it will be easy to implement.
Add layout_behavior to your RecyclerViews like below:
<android.support.v7.widget.RecyclerView
android:id="#+id/myRecyclerView"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And wrap content for layout height is important.
android:layout_height="wrap_content"
And last, you should add this, so scroll works only for NestedScrollView
myRecyclerView.setNestedScrollingEnabled(false);
If you have many items use Single RecyclerView with multiple types of viewholders.
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int TYPE_MAIN = 0;
private final int TYPE_SUB = 1;
private ArrayList<Object> dataSet;
class ViewHolderMain extends RecyclerView.ViewHolder {
...
}
class ViewHolderSub extends RecyclerView.ViewHolder {
...
}
#Override
public int getItemViewType(int position) {
if(dataSet.get(position) instance of MainRowObject){
return TYPE_MAIN;
}else{
return TYPE_SUB;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_MAIN: return new ViewHolderMain(...);
case TYPE_SUB: return new ViewHolderSub(...);
...
}
}
}
With the library SectionedRecyclerViewAdapter you can group your items in sections:
class MySection extends StatelessSection {
List<String> list;
public MySection(List<String> list) {
// call constructor with layout resource for this Section items
super(R.layout.section_item);
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
}
Then you set up the RecyclerView with your sections:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Create your sections with the list of data per row
MySection row1Section = new MySection(data1List);
MySection row2Section = new MySection(data2List);
// Add your Sections to the adapter
sectionAdapter.addSection(row1Section);
sectionAdapter.addSection(row2Section);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
Overview: I'm having a chat application. Till now, I was using CursorAdapter with a Listview to load my chat items in the list. But now, I'm planning to refactor the code to use RecyclerView with RecyclerView.Adapter and a "Load More" functionality like whatsapp.
Issue: Memory consumption. With CursorAdapter, items not in viewable area were getting Garbage Collected, but now since I'm using an ArrayList of my CustomModal, once you load all the items in the list (by clicking on the "Load More" button) I'm seeing high memory consumption in the memory logs (No Garbage Collection).
My guess is now, I'm loading all the items in an ArrayList and that is causing the issue. Is that it?
Is there a way to avoid the issue or optimize the problem?
EDIT:
Can't post the complete code here, but here is a snippet of the kind of Adapter that I've implemented:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private ArrayList<MyModal> mMyModals;
public MessageAdapter(ArrayList<MyModal> mMyModals) {
this.mMyModals = mMyModals;
//... Some fields initialization here
}
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
public void toggleLoadMore(boolean isLoadMoreEnabled){
if(isLoadMoreEnabled){
//..Checks if load more is already enabled or not
//..If not then enables it by adding an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}else{
//..Checks if load more is already disabled or not
//..If not then disables it by removing an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder messageViewHolder = null;
View itemLayoutView = null;
MyModal.MessageType messageType = MyModal.MessageType.getMessageTypeFromValue(viewType);
switch (messageType){
case MESSAGE_TYPE1:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout1, null);
messageViewHolder = new Type1ViewHolder(itemLayoutView);
break;
case MESSAGE_TYPE2:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout2, null);
messageViewHolder = new Type2ViewHolder(itemLayoutView);
break;
}
return messageViewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final MyModal myModal = mMyModals.get(position);
MyModal.MessageType messageType = myModal.getMessageType();
holder.initialize(myModal);
}
#Override
public int getItemCount() {
return (mMyModals != null)?mMyModals.size():0;
}
#Override
public int getItemViewType(int position) {
return mMyModals.get(position).getMessageType().getValue();
}
public abstract class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemLayoutView) {
super(itemLayoutView);
}
public abstract void initialize(MyModal myModal);
}
class Type1ViewHolder extends MyViewHolder {
//...Variables
public Type1ViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
class Type2ViewHolder extends MyViewHolder {
//...Variables
public TextViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
}
First of all :
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
Here you are creating a new arraylist and assigning it to your mMyModals. This means there are 2 arraylists at this point and they take up twice the amount of space than required. GC doesnt work the way you expect it to. Since the arraylist is initialized in your activity it will persist as long as the arraylist persists and so will the initial arraylist.
Instead of creating a new arraylist in your activity and passing it to changeList. Just clear your old arraylist and pass that.And also in adapter changeList method you can do the below
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals.clear();
this.mMyModels.addAll(myModels);
//... Some fields initialization here
notifyDataSetChanged();
}
Please let me know if i am not clear. Also show your activity code if this does not work.
Instead of replacing the whole ArrayList and calling notifyDataSetChanged, try adding the items to the ArrayList and then call notifyItemRangeInserted(int positionStart, int itemCount), maybe that could work. Also, you dont have to replace the Adapter's ArrayList. Your Activity/Fragment probably has the same ArrayList, just editing this list in your Activity/Fragment and then calling notifyItemRangeInserted(int positionStart, int itemCount) should do the trick. Also, instead of retrieving all the messages, you could also try to only get the next X amount of messages, so you wont retrieve the messages you already retrieved before (if you didn't do that already).