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.
Related
My DB is like this.
I will show you DATA when I search my name on SearchView.
This is the layout of the Adapter.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="#+id/nameText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_green_light"
android:gravity="center"
android:text="Name"
android:textColor="#android:color/white"
android:textSize="30sp" />
<TextView
android:id="#+id/dataText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_light"
android:gravity="center"
android:text="Data No."
android:textColor="#android:color/white"
android:textSize="20sp" />
</LinearLayout>
One nameText and one dataText.
If I search for JOHN1, I wish he would appear like this.
Like GridView.
JOHN1 JOHN1 JOHN1 JOHN1
10 20 30 40
But I don't know what to do with Adapter's onBindViewHolder.
It's Adapter.
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
List<Data> dataList;
Context context;
public Adapter(List<Data> dataList, Context context) {
this.dataList = dataList;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
// holder.nameText.setText(dataList.get(position).getName());
// holder.dataText.setText(dataList.get(position).getData1());
// holder.dataText.setText(dataList.get(position).getData2());
// holder.dataText.setText(dataList.get(position).getData3());
// holder.dataText.setText(dataList.get(position).getData4());
}
#Override
public int getItemCount() {
return dataList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView nameText, dataText;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
nameText = itemView.findViewById(R.id.nameText);
dataText = itemView.findViewById(R.id.dataText);
}
}
}
Plz help me...
The table like structure is a very old pattern and is not accepted as per the design guidelines. Suggestion is to use a single card with multiple textviews whose data can be set using the viewHolder in onBindViewHolder method.
Check this link for Cards
Check this link for how to create a card-based layout
You can use a Staggered or a Grid Layout Manager for your RecyclerView which would give you a good look for showing the data.
You can check both styles in this article
Now if all this does not convince you and you want to stick to the design that you mentioned, then you can have a ViewHolder with LinearLayout and horizontal orientation in which you can add the view with title and description dynamically by looping over the object and setting the title and description values. Title would be repetitive in this case and use unnecessary space on the screen as per the design. So it is not recommended to go with this design.
Let me know if this helps.
you can use Multi Type holders
good sample is here:
sample
To use RecyclerView like GridView:
mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), NUMBER_OF_COLUMN));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter);
But in your case, I saw your database is stored like 1 object response contains name, data1, data2, data3, data4 fields.
{
"response":{
"name":"John",
"data1":10,
"data2":20,
"data3":30,
"data4":40
}
}
If you want display as
JOHN1 JOHN1 JOHN1 JOHN1
10 20 30 40
DONT use Gridlayout. Gridlayout should be use in case your data is like
{
"response":[
{
"name":"John",
"data":10
},
{
"name":"John",
"data":20
},
{
"name":"John",
"data":30
},
{
"name":"John",
"data":40
}
]
}
Of course you can convert your object response to array object like below json, but it isn't necessary. Just use RecyclerView as normal is better idea
i have lots of adapters and views, viewsmodels and so on. Since its hard to maintain those i would like to use databinding and mvvm for that case. Now i tried to forward the item clicks into the viewmodel. Since its a recycleview i would lovely not loose the functionality to have less memory usage.
Currently i have a view (Activity) which sets the ViewModel. The ViewModel itself has an Adapter. The adapter has a Constructor which receives the viewModel and set this into the item.
The Item uses this to send the events back to the ViewModel. How does it affect the memory? Is there a better way doing this? I used RXJava before but this looks like the same concept, doesnt it? Here's my sample code (truncated).
View
public class ScenesFragment extends BaseFragment implements Observer {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.scenesFragmentBinding = DataBindingUtil.inflate(inflater, R.layout.scenes_fragment, container, false);
this.scenesListViewModel = new ScenesListViewModel(getContext());
this.scenesFragmentBinding.setViewModel(this.scenesListViewModel);
View view = this.scenesFragmentBinding.getRoot();
return view;
}
}
BaseLayout
<layout ... >
<data><variable name="viewModel" type=".viewmodel.ScenesListViewModel"/></data>
<android.support.v7.widget.RecyclerView
app:adapter="#{viewModel.adapter}"
app:layoutManager="#{viewModel.layoutManager}" />
</layout>
ViewModel
public class ScenesListViewModel extends Observable implements IViewModel {
public final SceneAdapter adapter;
private List<Scene> scenes = new ArrayList<>();
public ScenesListViewModel(#NonNull Context context) {
this.adapter = new SceneAdapter(context, scenes, this);
}
public void onRemoveClick(Scene scene) {
Timber.d("Clicked remove in the scene:" + scene);
}
}
Item Layout
<layout>
<data>
<variable name="scene"type=".model.Scene"/>
<variable name="viewModel" type=".viewmodel.ScenesListViewModel"/>
</data>
<ImageButton
android:id="#+id/sceneDelete"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="#null"
android:onClick="#{() -> viewModel.onRemoveClick(scene)}"
android:src="#drawable/ic_delete_forever_white_48px"/>
</LinearLayout>
</layout>
and finally the adapter which set the viewModel into the item.
Adapter
public class SceneAdapter extends RecyclerView.Adapter<SceneAdapter.BindingHolder> {
private Context context;
private List<Scene> scenes;
private ScenesListViewModel scenesListViewModel;
public SceneAdapter(Context context, List<Scene> list, ScenesListViewModel scenesListViewModel) {
this.context = context;
this.scenes = list;
this.scenesListViewModel = scenesListViewModel;
}
#Override public void onBindViewHolder(SceneAdapter.BindingHolder holder, int position) {
final Scene scene = scenes.get(position);
holder.binding.setScene(scene);
holder.binding.setViewModel(scenesListViewModel);
holder.binding.executePendingBindings();
}
Another way doing it is to set a Listener in the ViewModel, but this is more likely mvp then mvvm. I could also use RXJava again and create a Subject within the adapter, but i would like to solve it with the android on-board tools.
in your BindingHolder subclass implement onClickListener
class BindingHolder extends RecyclerView.ViewHolder
implements OnClickListener {
// code code code
}
#Override
public void onClick(View v) {
//code code code, use getAdapterPosition() to get the... adapter position
}
}
You can get the same ViewModel in the ViewHolder by using "by activityViewModels".
I am just an Android beginner & trying to work out RecyclerView with ViewHolder pattern
with the sample at : https://guides.codepath.com/android/using-the-recyclerview#create-the-recyclerview-within-layout
While implementing the code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView
android:id="#+id/rvContacts"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
it is asked to implement in res/layout/activity_users.xml
I have created the project with Basic Activity & has the files
activity_main.xml & content_main.xml
Is activity_users.xml another custom file? or a default file?
Can you tell me where it is supposed to implement the above code?
Once you defined your RecyclerView you have to define the single row layout.
Then, you reference it in your adapter. Something like it:
public class ContactAdapter extends
RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {
private List<ContactInfo> contactList; // your item list
public ContactAdapter(List<ContactInfo> contactList) {
this.contactList = contactList;
}
#Override
public int getItemCount() {
return contactList.size();
}
#Override
public void onBindViewHolder(ContactViewHolder contactViewHolder, int i) {
// binding
}
#Override
public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).
inflate(**your_row_layout**, viewGroup, false);
return new ContactViewHolder(itemView);
}
public static class ContactViewHolder extends RecyclerView.ViewHolder {
...
}
}
Hope it helps you.
You can read at here. i see it is so clear.
http://www.androidhive.info/2016/01/android-working-with-recycler-view/
Thanks.
You can add the recycler view in the content_main layout. Or if you dont want to complicate, remove the reference for content_main in your activity_main and add the recycler view in in the activity_main itself.
Use activity_main in your BaseActivity.
I have the following scenario: I have a LinearLayout on which I then add "cards" to which is a custom class which extends LinearLayout.
The problem is that each card contains an image. Now if I have too many cards to display I get an out of memory error because of the size of the images.
How can I dynamically see which cards are currently displayed on the screen and only load the images of those cards and keep the rest null?
I am struggling to detect which card is currently displayed on the screen and which ones are not. And then also to have an event load and clear images as the user scrolls though the list.
You have to implement a RecyclerView, which does the job for you.
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
final Adapter adapter = new Adapter();
recyclerView.setAdapter(adapter);
The adapter:
private class Adapter extends RecyclerView.Adapter<MyViewHolder> {
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_main, viewGroup, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(final MyViewHolder myViewHolder, int i)
// set the content of the card
}
#Override
public int getItemCount() {
return // number of cards
}
}
The ViewHolder
private class MyViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public TextView text2;
public ImageView imageView;
public MyViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(/* your textView */);
text2 = (TextView) itemView.findViewById(/* another textView */);
imageView = (ImageView) itemView.findViewById(/* an image */);
}
}
The Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:design="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivityFragment">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"
/>
</RelativeLayout>
For something like this, you should probably use a Recycler View. This way you can recycle the views and ideally not run into memory issues and not have to have hacky solutions that check what is on the screen and what isn't.
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.