I am making an app with 100 list items and was wondering if I could get away with not implementing the RecyclerView as I find it hard to implement it.
Quite frankly it depends up to you, Listview makes it easy for you by taking a lot of responsibility which makes it slow at time when you have to show a lot of data, on other hand RecyclerView does what it is best at make's things fast by taking care or bare minimum structure.
RecyclerView is quite easy to implement and you will get chance to learn some of the touch framework of Android because of it.
And performing Animation on RecyclerView is quite easy as well and way better than Listview
Making a custom listview is piece of cake with RecyclerView
here's an example for RecyclerView
private RecyclerView recyclerView;
recyclerView = (RecyclerView)findViewById(R.id.recycler);
MyViewComplainAdapter adapter = new MyViewComplainAdapter(getApplicationContext(), createComplainList());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
in XML
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical" />
your Adapter (Whatever you want to call this thing... lol )
private class MyViewComplainAdapter extends RecyclerView.Adapter<MyViewComplainAdapter.MyViewComplainViewHolder>{
private Context _Context;
private ArrayList<ViewMyComplainData> _List;
private LayoutInflater _Inflater;
public MyViewComplainAdapter(Context context, ArrayList<ViewMyComplainData> list){
_Context = context;
_List = list;
_Inflater = LayoutInflater.from(_Context);
}
#Override
public MyViewComplainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layout = _Inflater.inflate(R.layout.single_item_view_my_complain,parent,false);
MyViewComplainViewHolder holder = new MyViewComplainViewHolder(layout);
return holder;
}
#Override
public void onBindViewHolder(MyViewComplainViewHolder holder, int position) {
ViewMyComplainData data = _List.get(position);
holder.complaint_number.setText(data.getComplaint_Number()+"");
holder.complaint_type.setText(data.getComplaint_Type()+"");
holder.status.setText(data.getStatus()+"");
}
#Override
public int getItemCount() {
return _List.size();
}
public class MyViewComplainViewHolder extends RecyclerView.ViewHolder{
TextView complaint_number;
TextView complaint_type;
TextView status;
public MyViewComplainViewHolder(View itemView) {
super(itemView);
complaint_number = (TextView)itemView.findViewById(R.id.textView_complaint_number_single_item_view_my_complain);
complaint_type = (TextView)itemView.findViewById(R.id.textView_complaint_type_single_item_view_my_complain);
status= (TextView)itemView.findViewById(R.id.textView_status_single_item_view_my_complain);
}
}
}
yes you will have to make a ArrayList<ViewMyComplainData> using this method createComplainList(), you should figure this out
Technically speaking, RecyclerView doesn't need anything like "notifyDataSetChanged()" when an item is added or deleted from your List, which is a huge improvement performance-wise.
Related
My app has more than 4 lists of different data models.
I want to create a more generic CommonAdapter that extends PagedListAdapter
Here is my current code
public abstract class CommonPagedListAdapter<T, VH extends RecyclerView.ViewHolder>
extends PagedListAdapter<T, VH> {
private Context mContext;
private ArrayList<T> mArrayList;
public abstract void onItemClick(T model, int position);
public abstract int getLayoutResId();
protected CommonPagedListAdapter(Context context, ArrayList<T> arrayList,
#NonNull DiffUtil.ItemCallback<T> diffCallback) {
super(diffCallback);
this.mContext = context;
this.mArrayList = arrayList;
}
#NonNull
#Override
public VH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//what should i return here?
View view = LayoutInflater.from(mContext).inflate(getLayoutResId(),parent,false);
return (VH) new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull VH holder, int position) {
//what should i do here?
// holder
}
class ItemViewHolder extends RecyclerView.ViewHolder {
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
I'm using PagelListAdapter from the Android Paging Library
I would like to know a few things:
- What should be set in the onCreateViewHolder as I'll be having different ViewHolders?
- What should be set in onBindViewHolder?
- Is this really the right way that makes the CommonPagedListAdapter extensible and maintainable?
I had a similar issue where I tried to create a single adapter to use for multiple different list types. I ultimately came to the conclusion that it is best to use a separate adapter for each list type solely because it avoids having to make a very big "common" adapter class which goes against the "single-responsibility" principle. That way, each adapter is a lot smaller, more maintainable, and flexible.
However, if you really want to use a single adapter for similar items, the way I usually do it is by creating unique ViewHolders for each item type and then binding them accordingly in onBindViewHolder using a switch statement or something similar. In order to do this, you would need to override an additional method called getItemViewType in your adapter.
There's a really good guide that goes over how to create an adapter that can handle different view types on Codepath: https://guides.codepath.com/android/Heterogenous-Layouts-inside-RecyclerView
My app is all about performance, so I would really like to optimize this RecyclerView as much as possible. I have measured how long every part takes to complete, and the whole thing needs about 150ms to load. Here is the RELEVANT code:
public class AppAdapter extends RecyclerView.Adapter<AppAdapter.TabViewHolder> {
private Context context;
private MenuActivity menuActivity;
private Intent intent;
private ArrayList<String> stringys = new ArrayList<>();
public void setUpAdapter(Context mContext, MenuActivity mMenuActivity, Intent mIntent, ArrayList<String> mString) {
this.menuActivity = mMenuActivity;
this.context = mContext;
this.intent = mIntent;
stringys.addAll(mString);
}
#Override
public TabViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
Log.d(TAG, "CreatingViewholder " + "Time: " + menuActivity.deltaTime());
View view = inflater.inflate(R.layout.item_tab, parent, false); //Here is where the wait happens
Log.d(TAG, "ViewHolderCreated " + "Time: " + menuActivity.deltaTime());
return new TabViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull TabViewHolder holder, int position) {
holder.bind(position, holder);
}
#Override
public int getItemCount() {
return stringys.size();
}
class TabViewHolder extends RecyclerView.ViewHolder {
TextView text;
ImageView image;
TabViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.label);
image = itemView.findViewById(R.id.icon);
}
void bind(int position, TabViewHolder holder) {
new LongOperation(text, image).execute(stringys.get(position));
}
}
private class LongOperation extends AsyncTask<String, Void, Void> {
TextView text;
ImageView image;
CharSequence textToSet;
Drawable imageToSet;
public LongOperation(TextView text, ImageView image) {
super();
this.text = text;
this.image = image;
}
#Override
protected Void doInBackground(String... params) {
textToSet = params[0].getTitle(context.getDefaultSharedPreferences());
imageToSet = params[0].getIcon(context.getDefaultSharedPreferences());
return null;
}
#Override
protected void onPostExecute(Void result) {
text.setText(textToSet);
image.setImageDrawable(imageToSet);
}
}
}
I have several ideas/questions about this:
Is it possible to reuse this ViewHolder? I'm using it every time, and each inflation takes about 5ms, which adds up quickly because this is a grid, and I have about 40 holders loading when I launch this.
If it helps, I am also ready to use another kind of view. I took recyclerview as it made the most sense imo, but if there is a better-performing view I can change to that.
Would it help if I used a Linear Layout, and put 4 of the ViewHolders I currently use next to each other? Would my time be then reduced by 4?
In the asynctask, I call context.getDefaultSharedPreferences() twice. Would it load faster (the async part) if I did it once and had it as a variable to pass?
The asynctask it might leak if it isn't static. I assume that's not a problem because it finished very quickly anyways, right?
RecyclerView is used for long lists, implementation of ViewHolder pattern improves performance by re-using Views after they leave the screen. If you have don't have enough items to fill the viewport there's no reason to use it.
Also keep in mind while debugging setting bitmaps is much slower, if possible try building as "release" and see how it performs.
Your ViewHolder will be reused as you scroll down, but you need at least enough view holders to display what is currently on screen, plus a few more that are just off screen. So when you first load this are there 40 views displayed on the screen? If not, you have an issue.
It depends if you can scroll your view a lot.
You might get a slight improvement but I doubt it.
Shared preferences calls are cached. You don't have to worry about them taking a long time after the first access.
Not sure
Other improvements:
You could move your LayoutInflater creation to setUpAdapter()
What is in R.layout.item_tab? Make sure your hierarchy of views is as flat as possible. Use Constraint Layout if you can.
I have recycler-view with items in it and can be scrolled vertically. Currently what i achieved is items are added one after another like a list. By i need to place them side by side.
Like the image below
And my output is
My recycler-view setup code:
topicAdapter = new TopicAdapter(topicList, getActivity());
topicListView.setLayoutManager(new LinearLayoutManager(getActivity()));
topicListView.setAdapter(topicAdapter);
and adapter code is:
public class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.CategoryViewHolder> {
private List<Topic> topicList;
Context context;
public TopicAdapter(List<Topic> topicList, Context context) {
this.topicList = topicList;
this.context = context;
}
#Override
public CategoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//inflate the layout file
View groceryProductView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_suggested_topics, parent, false);
CategoryViewHolder holder = new CategoryViewHolder(groceryProductView);
return holder;
}
#Override
public void onBindViewHolder(CategoryViewHolder holder, final int position) {
holder.txtview.setText(topicList.get(position).getName());
}
#Override
public int getItemCount() {
return topicList.size();
}
public class CategoryViewHolder extends RecyclerView.ViewHolder {
TextView txtview;
public CategoryViewHolder(View view) {
super(view);
txtview = view.findViewById(R.id.titleView);
}
}
}
I can suggest you with a simple solution but, you cant achieve complete requirement with this code. You'll get side by side.
Replace
topicListView.setLayoutManager(new LinearLayoutManager(getActivity()));
with
topicListView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
// 3 denotes the number of rows per column
You can do this using Google's latest design component ChipGroup
Else you can use Flexbox-Layout by showing your tags in Grid Layout.
If you wish to go for Flexbox-Layout, check answer of avik
Add This
topicListView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL,false));
Use StaggeredGridLayoutManager for recyclerview
I think a good way to do this is by using Material Choice Chips, you can learn how to use them here. You can then use a ChipGroup to group them and allow them to flow across multiple lines.
However, to solve your question at hand, you can use a GridLayoutManager and then supply a SpanSizeLookup.
I have fragment "Incoming" on one slide of a viewpager. This fragment contains a RecyclerView populated with custom-relative-layouts. The LinearLayoutManager orientation is Vertical.
I have a second fragment "Find" on the next slide of the said viewpager. "Find" will consist of two recyclerviews. It will have a Horizontal recyclerview filled with cardviews (fast loading of profile pictures). Underneath that, I am loading more slowly another recyclerview with a custom-relative-layout, the same as in the "incoming" fragment.
Does that make sense? I'll elaborate some more:
The question is for these three recyclerviews, should I declare a new RecyclerAdapter for each one? The reason I ask is that they'll all have unknown variable item_counts.
Here is the RecyclerAdapter I have for "Incoming":
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
private Context mContext;
public RecyclerAdapter(Context context, List<Incoming> items) {
mContext = context;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
super(v);
// Define all of the components in the view
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater mInf = LayoutInflater.from(mContext);
View customView = mInf.inflate(R.layout.item_layout_incoming, parent, false);
final ViewHolder vh = new ViewHolder(customView);
return vh;
}
#Override
public int getItemCount(){ return 6; } // THIS IS TEMPORARY; WILL BE VARIABLE
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Replace contents
}
For my criteria, should I create another Adapter for my horizontal-cardview-recyclerview? It seems repetitive, but otherwise, how would I handle either inflating cardview or item_layout_incoming?
Seems like there should be a DRY way to do this, without hits to performance. Thanks
You are using fragments so you will create 2 objects of that class. So it's the same thing. you just reduce compiler load by reducing the task of loading the new class into memory and then create its object.
It's better to use two different Adapter because of 2 reasons.
Your code will become ugly I mean so much congested and so many if
else condition.
In future, if you need to change something in layouts then again it
will affect all objects if same adapter class.
So my advice do developer friendly code and create two classes.
I have created a basic app using RecyclerView and CardView from get tutorials from websites.
App is working fine and I have some confusion.(I am showing my whole code here)
confusion is that how code works step by step. So please clear my concept on it.
Basic Structure of my App :
I have created a row_data_layout xml file to bind on recycler_view.
Created an Data class file (Here I have defined my variable that I used in App).
Created an Adapter file (here I want to clear how it works step by step first which class gets called and why?).
Bind Data to RecyclerView on MainActivity file.
row_data_layout.xml file:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/CardView"
android:paddingBottom="16dp"
android:layout_marginBottom="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/txt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
</android.support.v7.widget.CardView>
Data Class File:
public class Data {
public String Name;
Data(String Name)
{
this.Name=Name;
}
}
Data_Adapter Class file:
public class Data_Adapter extends RecyclerView.Adapter<Data_Adapter.View_holder> {
List<Data> list = Collections.emptyList();
Context context;
public Data_Adapter(List<Data> list, Context context) {
this.list = list;
this.context = context;
}
#Override
public Data_Adapter.View_holder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_data_layout,parent,false);
View_holder holder=new View_holder(v);
return holder;
}
#Override
public void onBindViewHolder(Data_Adapter.View_holder holder, int position) {
holder.name.setText(list.get(position).Name);
}
#Override
public int getItemCount() {
return list.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
public class View_holder extends RecyclerView.ViewHolder{
CardView cv;
TextView name;
public View_holder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.CardView);
name = (TextView) itemView.findViewById(R.id.txt_name);
}
}
}
MainActivity File:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<Data> data = fill_data();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
Data_Adapter adapter = new Data_Adapter(data,getApplicationContext());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
public List<Data> fill_data()
{
List<Data> data = new ArrayList<>();
data.add(new Data("Bred Pit"));
data.add(new Data("Leonardo"));
return data;
}
}
Once you have a basic understanding of how a RecyclerView.Adapter works, it would make sense to take a deeper dive into the documentation.
What the adapter does is keep a pool of inflated views (this can be as many different types of ViewHolder as you would like) that it populates with the data you supply. When the adapter does not have an empty view in the pool it creates a new one.
When a view is attached to the RecyclerView, it is removed from the pool, and when it is detached (scrolls beyond view, to some distance), it is added back to the pool of empty views--this is why it is important to reset everything when you populate your ViewHolders.
The onCreateViewHolder() function is where a new, empty view (wrapped by a RecyclerView.ViewHolder) is created and added to the pool.
The onBindViewHolder() function gets a view from the empty pool and populates this view using the data you supplied to the adapter.\
You can use the onViewRecycled() method to perform specific actions like setting an ImageView's bitmap to null (on detach) in order to reduce memory usage.
I don't normally override onAttachedToRecyclerView(), but if you need to do something specific when your adapter is associated with the RecyclerView, you would do it here.