What I am doing: I have displayed the list view using data binding
What I am trying to find: How to properly add a on click event and display a toast as student name
Student.java
public class Student {
private String name;
private String email;
public Student() {
}
public Student(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
ActDemoListView.java
public class ActDemoListView extends AppCompatActivity {
private ActDemoListViewViewModel actDemoListViewViewModel;
private ActDemoListViewBinding actDemoListViewBinding;
private RecyclerView recyclerView;
private AdptStudent adptStudent;
private List<Student> studentList = new ArrayList<>();
/************************************* Life Cycle Methods *************************************/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initOnCreate();
}
/************************************* Life Cycle Methods *************************************/
/************************************* Init Methods *******************************************/
/** Init OnCreate **/
private void initOnCreate() {
setContentView(R.layout.act_two_way_display_data);
//Connect the view model to activity
connectViewModel();
//Bind the layout to activity
bindLayoutToActivity();
recyclerView = actDemoListViewBinding.recyclerList;
adptStudent = new AdptStudent(studentList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adptStudent);
prepareMovieData();
}
/************************************* Init Methods *******************************************/
private void connectViewModel() {
actDemoListViewViewModel = ViewModelProviders.of(this).get(ActDemoListViewViewModel.class);
}
private void bindLayoutToActivity() {
actDemoListViewBinding = DataBindingUtil.setContentView(this,R.layout.act_demo_list_view);
}
private void prepareMovieData() {
Student movie = new Student("Shruthi", "user11#google.com");
studentList.add(movie);
movie = new Student("Shalvi", "user1#google.com");
studentList.add(movie);
movie = new Student("Pavan", "user2#google.com");
studentList.add(movie);
movie = new Student("Brijesh", "user3#google.com");
studentList.add(movie);
movie = new Student("Anudeep", "user4#google.com");
studentList.add(movie);
adptStudent.notifyDataSetChanged();
}
}
AdptStudent.java
public class AdptStudent extends RecyclerView.Adapter<AdptStudent.MyViewHolder> {
private List<Student> studentsList = new ArrayList<>();
public AdptStudent(List<Student> studentsList) {
this.studentsList = studentsList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ListItemBinding listItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.list_item, parent, false);
return new MyViewHolder(listItemBinding);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Student student = studentsList.get(position);
holder.listItemBinding.setStudent(student);
}
#Override
public int getItemCount() {
return studentsList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private ListItemBinding listItemBinding;
public MyViewHolder(ListItemBinding ListItemBinding) {
super(ListItemBinding.getRoot());
this.listItemBinding=ListItemBinding;
}
}
}
There are several ways to do it. When i want an item on a recycler view to be clickable i do this.
Firstly i create an interface
public interface ItemClickListener {
void onClick(View view, int position, boolean click);
}
Secondly, on the view holder class i add these methods
#Override
public void onClick(View view) {
itemClickListener.onClick(view, getAdapterPosition(), false);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
And lastly on the onBindViewHolder
#Override
public void onBindViewHolder(#NonNull final CategoryAdapter.MyViewHolder myViewHolder, int i) {
myViewHolder.setItemClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean click) {
Intent intent = new Intent(myViewHolder.context, Myclass.class);
myViewHolder.context.startActivity(intent);
}
});
}
If you want me to provide all the code for the adapter just ask.
I have used the following approach. Note that your "Item" can be your viewmodel if you like which you have access to inside the bound layout for each item. And from there you can call whatever method you want inside the vm, or set a LiveData or whatever to tell the view to display the toast. I recomment to use SingleLiveEvent for this purpose.
First I created a BaseAdapter.
public class BaseAdapter<T> extends ListAdapter<T, SingleItemViewHolder<T>> {
private final int variableId;
protected BaseAdapter(#NonNull DiffUtil.ItemCallback<T> diffCallback, int variableId) {
super(diffCallback);
this.variableId = variableId;
}
#NonNull
#Override
public SingleItemViewHolder<T> onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), viewType, parent, false);
return new SingleItemViewHolder<>(binding, variableId);
}
#Override
public void onBindViewHolder(#NonNull SingleItemViewHolder<T> holder, int position) {
holder.bind(getItem(position));
}
}
This adapter uses the following SingleItemViewHolder.
public final class SingleItemViewHolder<T> extends RecyclerView.ViewHolder {
private final ViewDataBinding binding;
private final int variableId;
/**
* Constructor
*
* #param binding the binding to use
* #param variableId variable to set on the binding
*/
public SingleItemViewHolder(ViewDataBinding binding, int variableId) {
super(binding.getRoot());
this.binding = Objects.requireNonNull(binding);
this.variableId = variableId;
}
/**
* Sets the data binding variable to the provided item
* and calls {#link ViewDataBinding#executePendingBindings()}.
*
* #param item item to bind
* #throws NullPointerException if item is null ({#code item == null})
*/
public void bind(#NonNull T item) {
Objects.requireNonNull(item);
binding.setVariable(variableId, item);
binding.executePendingBindings();
}
}
Then you use it by subclassing the BaseAdapter and providing your own DiffCallback and layout like this.
public final class ModelAdapter extends BaseAdapter<Model> {
public ModelAdapter() {
super(new DiffCallback(), BR.item);
}
#Override
public int getItemViewType(int position) {
return R.layout.item_model;
}
private static final class DiffCallback extends DiffUtil.ItemCallback<Model> {
#Override
public boolean areItemsTheSame(#NonNull Model oldItem, #NonNull Model newItem) {
return oldItem.id.equals(newItem.id);
}
#Override
public boolean areContentsTheSame(#NonNull Model oldItem, #NonNull Model newItem) {
return oldItem.equals(newItem);
}
}
}
Where Model is a simple java object class with some fields (not included for brevity).
Then the layout which shows the actual model and allows for the data binding.
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.example.models.Model" />
</data>
....
Then you can simply use that item inside the layout.
Bonus usage example
Then instantiate it however you want. My recommendation, how I did it, was to instantiate in the view (fragment) the attach it using data binding.
<data>
<variable
name="adapter"
type="com.example.ModelAdapter" />
</data>
....
<androidx.recyclerview.widget.RecyclerView
recycler_view_base_adapter_items="#{vm.models}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="#{adapter}"
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
With the following #BindingAdapter.
#BindingAdapter(value = {"recycler_view_base_adapter_items"})
public static <T> void setRecyclerViewBaseAdapterItems(RecyclerView view,
#Nullable final List<T> items) {
final RecyclerView.Adapter viewAdapter = view.getAdapter();
if (viewAdapter == null || items == null) {
Timber.w("recycler_view_base_adapter_items did nothing.");
return;
}
try {
#SuppressWarnings("unchecked") final BaseAdapter<T> adapter = (BaseAdapter<T>) viewAdapter;
adapter.submitList(items);
} catch (ClassCastException e) {
Timber.e(e);
}
}
Related
I,ve tried everything and nothing is working... Any solution please. if i use listview it works perfectly but on recycler view i'm getting an error.
the code is below
home fragment
recyclerViewOne = view.findViewById(R.id.recyclerViewOne);
collectionReference = firebaseFirestore.collection("Trending songs");
recyclerViewOne.setHasFixedSize(true);
recyclerViewOne.setLayoutManager(new
LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL,false));
mUpload = new ArrayList<>();
gridViewHolder = new GridViewHolder(getActivity(), mUpload, new
GridViewHolder.ItemClickListeners() {
#Override
public void onClicke(GridModel model, int post) {
((DashboardActivity) getActivity()).method();
((DashboardActivity) getActivity()).playO(post);
}
});
recyclerViewOne.setAdapter(gridViewHolder);
i need some help please
According to the naming, you are trying the set a ViewHolder as an adapter to the recycler. If that is the case, then you should implement it the correct way: a custom Adapter that extends RecyclerView.Adapter and that add that adapter to the recycler view
this is the GridViewHolder
public class GridViewHolder extends RecyclerView.Adapter<GridViewHolder.SongsAdapterViewHolder> {
Context context;
List<GridModel> gridModels;
private ItemClickListeners listeners;
private int selected;
#NonNull
#Override
public SongsAdapterViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.grid_item, parent,false);
return new SongsAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull SongsAdapterViewHolder holder, int position) {
GridModel gridModel = gridModels.get(position);
holder.mDesc.setText(gridModel.getmDesc());
holder.mTitle.setText(gridModel.getmTitle());
String duration = Utility.convertion(Long.parseLong(gridModel.getDuration()));
Glide.with(context).load(gridModel.imageurl).into(holder.imageurl);
holder.bind(gridModel, listeners);
}
public GridViewHolder(Context context, List<GridModel> gridModels, ItemClickListeners listeners) {
this.context = context;
this.gridModels = gridModels;
this.listeners = listeners;
}
#Override
public int getItemCount() {
return gridModels.size();
}
public class SongsAdapterViewHolder extends RecyclerView.ViewHolder{
TextView mTitle, mDesc;
ImageView imageurl;
public SongsAdapterViewHolder(#NonNull View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.gridTitle);
mDesc= itemView.findViewById(R.id.gridMusician);
imageurl = itemView.findViewById(R.id.gridImageView);
}
public void bind(final GridModel gridModel, final ItemClickListeners listeners) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listeners.onClicke(gridModel, getAdapterPosition());
}
});
}
}
public interface ItemClickListeners {
void onClicke(GridModel model, int post);
}
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected = selected;
}
}
How do you use DiffCallback to load a newList in RecyclerView when DiffUtil ItemCallback is being used.
I would like to give the user the option to return different size lists from the database when the user selects a different size I want the RecyclerView to update.
RecyclerViewAdatper
RecyclerViewAdapter extends ListAdapter<WordEntity, RecyclerViewAdapter.ViewHolder> {
private RecyclerViewAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK = new DiffUtil.ItemCallback<WordEntiti>() {
#Override
public boolean areItemsTheSame...
#Override
public boolean areContentsTheSame...
};
#Override
public viewHolder onCreateViewHolder...
#Override
public void onVindViewHolder ...
class ViewHolder extends RecyclerView.ViewHolder ...
public void updateWordList(List<WordEntity> words) {
final WordDiffCallBack diffCallBack = new WordDiffCallBack(list???, words);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallBack);
this.list???.clear();
this.addAll(words);
diffResult.dispatcheUpdatesTo(this);
}
WordsDiffCallBack
private final List<WordEntity> mOldList;
private final List<WordEntity> mNewList;
public WordsDiffCallBack(List<WordEntity> oldList, List<WordEntity> newList) {
this.mOldList = oldList;
this.mNewList = newList;
}
#Override
public int getOldListSize() {
return mOldList.size();
}
#Override
public int getNewListSize() {
return mNewList.size();
}
#Override
public boolean areItemsTheSame(int OldItemPostion, int newItemPosition) ...
#Override boolean areContentsTheSame(int oldItemPosition, int newItemPosition)...
#Override getChangePayload(int oldItemPosition, int newItemPosition) ...
}
I want the RecycelView to update automatically when the size of the list gets changed by the user. How do I call the old list from the ListAdapter and will that even update the RecyclerView
You can create a template just like shown in this video in youTube:
https://www.youtube.com/watch?v=y31fzLe2Ajw
Here is an example of adapter.
public class CartFragAdapter extends RecyclerView.Adapter<CartFragAdapter.CartFragViewHolder> {
private static final String TAG = "debinf PurchaseAdap";
private static final DiffUtil.ItemCallback<ProductsObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<ProductsObject>() {
#Override
public boolean areItemsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areItemsTheSame: old is "+oldProduct.getCode()+" ; new is "+newProduct.getCode());
return oldProduct.getCode().equals(newProduct.getCode());
}
#Override
public boolean areContentsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areContentsTheSame: old is "+oldProduct.getPrice()+" ; new is "+newProduct.getPrice());
return oldProduct.getPrice() == newProduct.getPrice();
}
};
private AsyncListDiffer<ProductsObject> differ = new AsyncListDiffer<ProductsObject>(this, DIFF_CALLBACK);
#NonNull
#Override
public CartFragViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_purchase, parent, false);
return new CartFragViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CartFragViewHolder holder, int position) {
final ProductsObject purchaseList = differ.getCurrentList().get(position);
holder.mCode.setText(purchaseList.getCode());
holder.mPrice.setText(String.valueOf(purchaseList.getPrice()));
holder.mDescription.setText(purchaseList.getDescription());
}
#Override
public int getItemCount() {
Log.i(TAG, "getItemCount");
return differ.getCurrentList().size();
}
public void submitList(List<ProductsObject> products){
Log.i(TAG, "submitList: products.size is "+products.size());
differ.submitList(products);
}
public class CartFragViewHolder extends RecyclerView.ViewHolder {
public TextView mCode, mPrice, mDescription;
public CartFragViewHolder(#NonNull View itemView) {
super(itemView);
mCode = (TextView) itemView.findViewById(R.id.item_productCode);
mPrice = (TextView) itemView.findViewById(R.id.item_productPrice);
mDescription = (TextView) itemView.findViewById(R.id.item_productDescription);
}
}
}
In MainActivity you call adapter like this:
CartFragAdapter adapter = new CartFragAdapter();
adapter.submitList(inputData);
I hope it helps!
observe this code
here's my diffutil callback
public class MyDiffUtilCallback extends DiffUtil.Callback {
List<String> oldlist;
List<String > newlist;
public MyDiffUtilCallback(List<String> oldlist, List<String> newlist) {
this.oldlist = oldlist;
this.newlist = newlist;
}
#Override
public int getOldListSize() {
return oldlist.size();
}
#Override
public int getNewListSize() {
return newlist.size();
}
#Override
public boolean areItemsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Override
public boolean areContentsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
here's the recyclerview adpater which uses diffutilcallback
public class recyclerviewAdapter extends RecyclerView.Adapter<recyclerviewAdapter.Viewholder> {
List<String> datasource;
public recyclerviewAdapter(List<String> datasource) {
this.datasource = datasource;
}
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.demorow,viewGroup,false);
return new Viewholder(itemView);
}
#Override
public void onBindViewHolder(#NonNull Viewholder viewholder, int i) {
viewholder.tv_demo_data_row.setText(datasource.get(i));
}
#Override
public int getItemCount() {
return datasource.size();
}
public static class Viewholder extends RecyclerView.ViewHolder {
TextView tv_demo_data_row;
public Viewholder(#NonNull View itemView) {
super(itemView);
tv_demo_data_row=itemView.findViewById(R.id.tv_demo_data_row);
}
}
//DIFF CALLBACK STATE
public void insertdata(List<String> insertList){
/**
* Insert list insert data to list
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,insertList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.addAll(insertList);
diffResult.dispatchUpdatesTo(this);
}
public void updateList(List<String> newList){
/**
* update list clear old data and update new data
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,newList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.clear();
datasource.addAll(newList);
diffResult.dispatchUpdatesTo(this);
}}
and here i can update list in activity on button clickevent
insert_data.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//inserting the data
List<String> insertlist = new ArrayList<>(); //asign old list
for(int i=0;i<2;i++){
insertlist.add(UUID.randomUUID().toString()); // insert new list
adapter.insertdata(insertlist);
faster_recyclerview.smoothScrollToPosition(adapter.getItemCount()-1); //auto scroll to last postion
}
}
});
replace List of string with your modelclass
And in addition to the above (using DiffUtils), if you use Clicks to change RecyclerView's content, make
ViewHolder implements View.OnClickListener
and use this to set OnClickListener in onBindViewHolder
.setOnClickListener(holder);
and get your current position by
this.getAdapterPosition()
private class HistoryRecyclerAdapter extends RecyclerView.Adapter<HistoryRecyclerAdapter.ViewHolder> {
private List<HistoryEntry> entries;
HistoryRecyclerAdapter(List<HistoryEntry> entries) {
this.entries = entries;
}
public void updateContainer(List<HistoryEntry> list){
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
#Override
public int getOldListSize() { return entries.size(); }
#Override
public int getNewListSize() { return list.size(); }
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return entries.get(oldItemPosition).getId()==list.get(newItemPosition).getId();
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
HistoryEntry old = entries.get(oldItemPosition);
HistoryEntry neww = list.get(newItemPosition);
return old.getExpression().equals(neww.getExpression())
&& old.getResult().equals(neww.getResult());
}
}, false);//detectMoves=true потом позволит вызвать анимацию если строка просто была перемещена
entries = list;
diffResult.dispatchUpdatesTo(this);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
//some Views
final Button delBtn;
ViewHolder(View view){
super(view);
//some Views
delBtn = (Button) view.findViewById(R.id.delete_btn);
}
#Override
public void onClick(View v) {
switch (v.getId()){
//some cases
case R.id.delete_btn:
historyManager.delete(entries.get(this.getAdapterPosition()));
break;
}
}
}
#NonNull
#Override
public HistoryRecyclerAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.history_row, parent, false);
return new HistoryRecyclerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(HistoryRecyclerAdapter.ViewHolder holder, final int position) {
//some code
holder.delBtn.setOnClickListener(holder);
}
#Override
public int getItemCount() {
return entries.size();
}
}
Early I used final int position in onBindViewHolder and don't understand what's wrong...)))
Here is snapet of my project (How to view list of my providers):
public class ProviderAdapter extends ListAdapter<Provider, RecyclerView.ViewHolder> {
private final GeneralListener<Provider> listener;
public ProviderAdapter(GeneralListener<Provider> listener) {
super(DIFF_CALLBACK);
this.listener = listener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(ItemProviderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).bind(getItem(position));
}
class ViewHolder extends RecyclerView.ViewHolder {
ItemProviderBinding binding;
public ViewHolder(ItemProviderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(Provider data) {
binding.image.setImageURI(data.img_url);
binding.txtName.setText(data.name);
binding.txtMealTypes.setText(data.meal_types);
binding.txtAddress.setText(data.address);
binding.btnBack.setOnClickListener(view -> {
listener.onClick(Actions.VIEW, data);
});
}
}
private static final DiffUtil.ItemCallback<Provider> DIFF_CALLBACK = new DiffUtil.ItemCallback<Provider>() {
#Override
public boolean areItemsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.name.equals(newItem.name)
&& oldItem.meal_types.equals(newItem.meal_types)
&& oldItem.address.equals(newItem.address);
}
};
}
To use it:
ProviderAdapter adapter = new ProviderAdapter(new GeneralListener<Provider>() {
#Override
public void onClick(Actions action, Provider provider) {
if (action == Actions.VIEW) {
// TODO: View provider details
}
}
});
recyclerView.setAdapter(adapter);
adapter.submitList(providerList);
Well... i want to show data retrieved from firebase database in RecycleView and do further stuffs like editing,updating etc. So, i'm using observable pattern to retrieve data(which successfully did) and trying to pass data to the constructor and the adapter class. The data did show up in the constructor class but didn't load in adapter class and also in the RecycleView.
Fragment class or main class
public class overviewFragment extends Fragment {
View view;
public static RecyclerView overRecycleView;
public overviewFragment() {}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.overview_fragment, container, false);
overRecycleView = view.findViewById(R.id.overViewRecycle);
overviewRecyclerAdapter adapter = new overviewRecyclerAdapter(getContext(), observerData.overViewlist);
overRecycleView.setLayoutManager(new LinearLayoutManager(getActivity()));
// adapter.notifyDataSetChanged();
overRecycleView.setAdapter(adapter);
return view;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
observableData ob =new observableData();
observerData observerData = new observerData(ob)
ob.setMeasurement();
ob.setMeasurement(); }}
Observable class to retrieve firebase data and notify observer
public class observableData extends Observable {
private String data;
public observableData() { }
public void setMeasurement(){
final DatabaseReference n = ScrollingActivityforTutor.db();
n.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String a = dataSnapshot.child("name").getValue().toString();
Log.d("name from database ", a);
data =a;
measurementChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void measurementChanged(){
setChanged();
notifyObservers();
}
public String getData() {
return data;
}
}
Observer class from where constructor class will be called
public class observerData implements Observer {
Observable observable;
public String data;
public static List<itemOverview> overViewlist = new ArrayList<>();;
public observerData(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
observableData od = (observableData) o;
this.data = od.getData();
passData();
}
public void passData(){
overViewlist.add(new itemOverview(data));
Log.d("data in observer ", data);
}
}
Constructor class
public class itemOverview {
private String text;
public itemOverview(String text) {
Log.d("IN CONSTRUCTOR ", text); // It is perfectly showing the data
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}}
Adapter class
public class overviewRecyclerAdapter extends
RecyclerView.Adapter<overviewRecyclerAdapter.overviewViewHolder> {
Context mcontext;
List<itemOverview> mdata;
public overviewRecyclerAdapter(Context mcontext, List<itemOverview> mdata)
{
this.mcontext = mcontext;
this.mdata = mdata; }
public static class overviewViewHolder extends RecyclerView.ViewHolder{
private TextView t;
public overviewViewHolder(#NonNull View itemView) {
super(itemView);
t = itemView.findViewById(R.id.overViewtextView); }}
#NonNull
#Override
public overviewRecyclerAdapter.overviewViewHolder
onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v;
v = LayoutInflater.from(mcontext).inflate(R.layout.item_overview,
parent, false);
overviewViewHolder overview = new overviewViewHolder(v);
return overview; }
#Override
public void onBindViewHolder(#NonNull
overviewRecyclerAdapter.overviewViewHolder holder, int position) {
Log.d("IN ADAPTER ", mdata.get(position).getText()); // HERE, IT IS
NOT WORKING
holder.t.setText(mdata.get(position).getText());
}
#Override
public int getItemCount() {
return mdata.size() ;
}}
But when i use measurementChanged() (in the observable class) outside the onDatachange method and put data (variable) with some string value, everything worked and the given data showed up in the recycleView.
dont understand where the problem is and where to debug.
However, SORRY for such big miles of codes :)
Thanks.
I can see that you are trying to add elements to a RecyclerView programmatically. But the RecyclerView is not updated when you update the array containing the necessary data. As you have made the RecyclerView static, you can easily notify the changes to the adapter using the following code:
public void passData(){
overViewlist.add(new itemOverview(data));
Log.d("data in observer ", data);
overviewRecyclerAdapter adapter = (overviewRecyclerAdapter) overviewFragment.overRecycleView.getAdapter();
adapter.notifyDataSetChanged();
}
There are much better ways of doing this. adapter.notifyItemInserted(overViewlist.size()-1); is probably a better solution in your case (in terms of performance).
P.S. You should follow the naming conventions in java to make your code more readable.
I am implementing a Recycler View and its respective adapter using Android Data Binding. The problem is that in all the tutorials I have seen, they initialize the data of the adapter with a Collection of View model class, like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements View.OnClickListener {
private ArrayList<ViewModel> items;
public RecyclerViewAdapter(ArrayList<ViewModel> items) {
this.items = items;
}
.....
}
But the data I want to pass to the Recycler View is a Collection of records from my database:
private ArrayList<Record> items;
How can I do that??
Thanks in advance!
EDIT
li_item.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="report"
type="viewmodel.ReportVM"/>
</data>
....
<TextView
android:id="#+id/tv_report_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textSize="18sp"
android:paddingBottom="6dp"
android:text="#{report.name}"
tools:text="Report 1"
/>
....
ADAPTER
public class ReportRVAdapter extends RecyclerView.Adapter<ReportRVAdapter.ReportViewHolder> {
private List<ReportDb> data;
public ReportRVAdapter(final List<ReportDb> reportData) {
this.data = reportData;
}
#Override
public ReportRVAdapter.ReportViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.li_item, parent, false);
return new ReportViewHolder(v);
}
#Override
public void onBindViewHolder(final ReportViewHolder holder, final int position) {
ReportDb item = data.get(position);
holder.getBinding().setVariable(BR.name, item.getName());
holder.getBinding().setVariable(BR.contractor, item.getContractor());
//make binding happen immediately
holder.getBinding().executePendingBindings();
}
#Override
public int getItemCount() {
return data == null ? 0 : data.size();
}
public class ReportViewHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public ReportViewHolder(final View rowView) {
super(rowView);
binding = DataBindingUtil.bind(rowView);
}
public ViewDataBinding getBinding() {
return binding;
}
}
}
MODEL VIEW
public class ReportVM extends BaseObservable {
public String name;
public String contractor;
public ReportVM() {
}
#Bindable
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
#Bindable
public String getContractor() {
return contractor;
}
public void setContractor(final String contractor) {
this.contractor = contractor;
}
}
You can use any collection in RecyclerView, It depends on you how you bind value with the View(TextView, ImageView).
Example:-
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
holder.txtHeader.setText(mDataset.get(position));
}
in above example, it depends on you, if you use Map then you need to get value using key. For set same get method can be used.
Finally I did realized that instead of:
holder.getBinding().setVariable(BR.name, item.getName());
holder.getBinding().setVariable(BR.contractor, item.getContractor());
//make binding happen immediately
holder.getBinding().executePendingBindings();
I should write this:
holder.getBinding().setReport(new ReportVM(item));
and my POJO:
private ReportDb report;
public ReportVM(final ReportDb report) {
this.report = report;
}
public String getName() {
return report.getName();
}
public String getContractor() {
return report.getContractor();
}
I'm not familiar with rx java. Trying to use it with recycleview. For some reason my code is not working Here is my code.
Fragment with recycle view
public class CheeseListFragment extends Fragment {
private final CompositeSubscription subscriptions = new CompositeSubscription();
private PublishSubject<String> timespanSubject;
private final Func1<String, Observable<LiveInfo>> trendingSearch =
new Func1<String, Observable<LiveInfo>>() {
#Override
public Observable<LiveInfo> call(String s) {
RadioLiveInfoObservableService radioLiveInfoObservableService=ApiProvider.getInstance().getRadioObserverInfo();
return radioLiveInfoObservableService.radioInfo(Type.INTERVAL)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(trendingError)
.onErrorResumeNext(Observable.<LiveInfo>empty());
}
};
private final Action1<Throwable> trendingError = new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
Timber.e(throwable, "Failed to get trending repositories");
}
};
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
timespanSubject = PublishSubject.create();
final RecyclerView rv = (RecyclerView) inflater.inflate(
R.layout.fragment_cheese_list, container, false);
setupRecyclerView(rv);
subscriptions.add(timespanSubject
.flatMap(trendingSearch)
.map(SearchResultToRepositoryList.instance())
.subscribe(adapter));
return rv;
}
private SimpleStringRecyclerViewAdapter adapter;
private void setupRecyclerView(RecyclerView recyclerView) {
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));
adapter=new SimpleStringRecyclerViewAdapter(getActivity(), new SimpleStringRecyclerViewAdapter.CurrentShowClickListener() {
#Override
public void onCurrentShowClick(Current currentShow) {
Intent intent = new Intent(CApplication.getAppContext(), CheeseDetailActivity.class);
intent.putExtra(CheeseDetailActivity.EXTRA_NAME, currentShow.getName());
CApplication.getAppContext().startActivity(intent);
}
});
recyclerView.setAdapter(adapter);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onChanged() {
Toast.makeText(getActivity(),"data changed",Toast.LENGTH_SHORT).show();
}
});
}
private List<String> getRandomSublist(String[] array, int amount) {
ArrayList<String> list = new ArrayList<>(amount);
Random random = new Random();
while (list.size() < amount) {
list.add(array[random.nextInt(array.length)]);
}
return list;
}
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> implements Action1<List<Current>> {
private List<Current> currentShows = Collections.emptyList();
public interface CurrentShowClickListener {
void onCurrentShowClick(Current currentShow);
}
private final CurrentShowClickListener currentShowClickListener;
private final TypedValue mTypedValue = new TypedValue();
private int mBackground;
#Override
public void call(List<Current> currentShows) {
this.currentShows = currentShows;
notifyDataSetChanged();
}
public SimpleStringRecyclerViewAdapter(Context context,CurrentShowClickListener currentShowClickListener) {
context.getTheme().resolveAttribute(R.attr.selectableItemBackground, mTypedValue, true);
mBackground = mTypedValue.resourceId;
this.currentShowClickListener = currentShowClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ListItemView view = (ListItemView)LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override public void onBindViewHolder(ViewHolder viewHolder, int i) {
viewHolder.bindTo(currentShows.get(i));
}
#Override public long getItemId(int position) {
return position;
}
#Override public int getItemCount() {
return currentShows.size();
}
public final class ViewHolder extends RecyclerView.ViewHolder {
public final ListItemView itemView;
private Current currentShow;
public ViewHolder(ListItemView itemView) {
super(itemView);
this.itemView = itemView;
this.itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
currentShowClickListener.onCurrentShowClick(currentShow);
}
});
}
public void bindTo(Current currentShow) {
this.currentShow = currentShow;
itemView.bindTo(currentShow);
}
}
}
}
SearchToResultRepositoryList
public final class SearchResultToRepositoryList implements Func1<LiveInfo, List<Current>> {
private static volatile SearchResultToRepositoryList instance;
public static SearchResultToRepositoryList instance() {
if (instance == null) {
instance = new SearchResultToRepositoryList();
}
return instance;
}
#Override public List<Current> call(LiveInfo repositoriesResponse) {
List<Current> currents=new ArrayList<>();
currents.add(repositoriesResponse.getCurrent());
return currents;
}
}
REST
public interface RadioLiveInfoObservableService {
#GET("/api/live-info/")
Observable<LiveInfo> radioInfo(
#Query("type") Type type);
}
It's just doing nothing. I tried to debug it trendingSearch.call is not called at all.
I can make it work only this way. But still i want to know how to it with subscription
RadioLiveInfoObservableService radioLiveInfoObservableService=ApiProvider.getInstance().getRadioObserverInfo();
radioLiveInfoObservableService.commits(Type.INTERVAL)
.observeOn(AndroidSchedulers.mainThread())
.doOnError(trendingError)
.onErrorResumeNext(Observable.<LiveInfo>empty()).subscribe(new Action1<LiveInfo>() {
#Override
public void call(LiveInfo liveInfo) {
List<Current> currents=new ArrayList<Current>();
currents.add(liveInfo.getCurrent());
adapter.currentShows=currents;
adapter.notifyDataSetChanged();
rv.setAdapter(adapter);
}
});
That's a lot of code to digest looking for errors, but off the top of my head nothing will happen until timeSpanSubject.onNext() is called. I don't see this called anywhere but maybe there is some missing code you are not showing.
If there is no missing code that calls timeSpanSubject.onNext(), then you could use either a BehaviorSubject which will emit an item when first subscribed to or another Observable such as timer or interval depending on what you are trying to do. timer would subscribe to your trendingSearch Observable a single time whereas using an interval would subscribe multiple times.