I created a custom class named Food that contained all the elements from my layout which I want to populate.
I set the setters and getter method in that class
// Recycler View
private RecyclerView mFrooderList;
private DatabaseReference mDatabase;
//Setting Parameters for RecyclerView
mFrooderList =(RecyclerView) findViewById(R.id.frooder_list);
mFrooderList.setHasFixedSize(true);
mFrooderList.setLayoutManager(new LinearLayoutManager(this));
mDatabase = FirebaseDatabase.getInstance().getReference().child("Food");
Then I use Firebase Recycler adapter to populate my layout from Firebase Database
FirebaseRecyclerOptions<Food> options =
new FirebaseRecyclerOptions.Builder<Food>()
.setQuery(mDatabase, Food.class)
.build();
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter<Food, FoodViewHolder>(options) {
#Override
public FoodViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Create a new instance of the ViewHolder, in this case we are using a custom
// layout called R.layout.message for each item
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_frooder_post, parent, false);
return new FoodViewHolder(view);
}
#Override
protected void onBindViewHolder(FoodViewHolder holder, int position, Food model) {
mFrooderList.setAdapter(adapter);
}
};
But when I run the app it runs but I dont see my layout in the main Activity and in the run time I get this error
I don't know what I am doing wrong Please help
onBindViewHolder is where you take values from Food model and set them to the FoodViewHolder holder.
It is not where you set the adapter.
In other words, it should look like this
#Override
protected void onBindViewHolder(FoodViewHolder holder, int position, Food model) {
// TODO: Bind the ViewHolder
}
}; // end of adapter definition
mFrooderList.setAdapter(adapter);
} // end onCreate
Related
Context:
I've implemented a RecyclerView in my to-do list app.
I wanted to be able to use various onClick methods for items within the RecyclerView so I created an interface called onTaskListener.
This interface has two method stubs, one for onClick and one for onLongClick. In my ViewHolder, I implement both the onClick() and onLongClick() methods which simply pass off control to my onTaskClickListener().
In my adapter, I create an onTaskClickListener().
Then in my main activity, I implement the methods within onTaskClickListener().
My issue is that while my onTaskClick() works perfectly, my onTaskLongClick doesn't seem to function at all. Is there something wrong with the way I set up my RecyclerView/Adapter/ViewHolder/ViewModel pattern?
Question: If the way I have implemented my interface is wrong, how do I include multiple types of click events within a single interface?
Here are the relevant contents of each file (I know it's a lot, I'm very sorry for the wall of code):
onTaskClickListener.java:
public interface OnTaskListener {
void onTaskClick(int position); // Interfaces are implicitly abstract
void onTaskLongClick(int position);
}
itemViewHolder.java:
public class itemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
View itmView; // This is the general view
TextView txtView; // This is the specific text view that shows up as a singular task in the list of to-do tasks
OnTaskListener onTaskListener; // Create an OnTaskListener inside our view holder which allows the view holder to realize it's been clicked
public itemViewHolder(#NonNull View itemView, OnTaskListener inputOnTaskListener) {
super(itemView);
itmView = itemView;
txtView = itemView.findViewById(R.id.txtTask);
this.onTaskListener = inputOnTaskListener; // Take an onTaskListener that is passed into the object and store it internally
itemView.setOnClickListener(this); // passes the View.OnClickListener context to the itemView via "this"
}
#Override
public void onClick(View view) {
onTaskListener.onTaskClick(getAdapterPosition()); // This says that whenever we register a click event, we pass the logic onto the taskClick event
}
#Override
public boolean onLongClick(View view) {
onTaskListener.onTaskLongClick(getAdapterPosition()); // This says that whenever we register a longClick event, we pass the logic onto the taskClick event
return true; // This means that we have successfully consumed the long click event. No other click events will be notified
}
}
dataAdapter.java
public class dataAdapter extends RecyclerView.Adapter<itemViewHolder> {
List<taskItem> taskItemList;
private OnTaskListener onTaskListener;
public dataAdapter(List<taskItem> inputTaskItemList, OnTaskListener inputOnTaskListener){
this.taskItemList = inputTaskItemList;
this.onTaskListener = inputOnTaskListener;
}
#NonNull
#Override
public itemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View localView = LayoutInflater.from(parent.getContext()).inflate(R.layout.taskholder, parent, false); //Don't even know what this line does, it's all so over my head
return new itemViewHolder(localView, onTaskListener); // Return an instance of whatever we made directly above this line
}
#Override
public void onBindViewHolder(#NonNull itemViewHolder holder, final int position) {
holder.txtView.setText(taskItemList.get(position).taskTitle);
// Look inside our ViewModel and get the text for this specific instance of the ViewModel, which corresponds to the current position
}
#Override
public int getItemCount() {
return taskItemList.size();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements OnTaskListener{
private RecyclerView taskList; // Creates a RecyclerView to hook up to our RecyclerView widget in the UI
private dataAdapter localAdapter; // Instantiates our custom adapter class
List<taskItem> myItems; // Stores the items in a list of taskItem's
private RecyclerView.LayoutManager localLayoutManager; // God knows what this does :(
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
taskList = findViewById(R.id.taskList); // Connects our list from UI to recycler view code
localLayoutManager = new LinearLayoutManager(this); // assigns our localLayoutManager to an actual Layout Manager
taskList.setLayoutManager(localLayoutManager); // connecting our layout manager to our recycler view
taskList.setHasFixedSize(true);
myItems = new ArrayList<>(); // Now we FINALLY make our to-do list and populate it with actual tasks
myItems.add(new taskItem("groceries"));
myItems.add(new taskItem("practice bjj"));
localAdapter = new dataAdapter(myItems, this); // Pass the to do list to the adapter so it can feed it to the recycler view
taskList.setAdapter(localAdapter); // Lastly set the recycler view's adapter to the one we made above
}
#Override
public void onTaskClick(int position) {
taskItem currentTask = myItems.get(position);
if(!(currentTask.taskTitle.startsWith("Done: "))){ // Logic that marks a task as done on tap
currentTask.taskTitle = "Done: " + currentTask.taskTitle;
//logic that moves the tapped item to bottom of list
myItems.remove(position);
myItems.add(myItems.size(), currentTask);
localAdapter.notifyItemMoved(position, myItems.size());
}
else if(myItems.get(position).taskTitle.startsWith("Done: ")){ // Logic for if user taps a task already marked "done"
currentTask.taskTitle = currentTask.taskTitle.replaceFirst("Done: ", "");
myItems.set(position, currentTask); // Remove prefix
localAdapter.notifyItemChanged(position);
myItems.remove(position);
myItems.add(0, currentTask);
}
localAdapter.notifyDataSetChanged(); // Let the activity know that the data has changed
}
#Override
public void onTaskLongClick(int position) { // This branch deals with deleting tasks on long click
myItems.remove(position);
localAdapter.notifyItemRemoved(position); // Item has been deleted
}
}
You never call setOnLongClickListener():
public itemViewHolder(#NonNull View itemView, OnTaskListener inputOnTaskListener) {
super(itemView);
itmView = itemView;
txtView = itemView.findViewById(R.id.txtTask);
this.onTaskListener = inputOnTaskListener; // Take an onTaskListener that is passed into the object and store it internally
itemView.setOnClickListener(this); // passes the View.OnClickListener context to the itemView via "this"
// Add this line
itemView.setOnLongClickListener(this); // passes the View.OnLongClickListener context to the itemView via "this"
}
Alternatively, you can avoid going through this entirely by inlining the entire OnLongClickListener (and similarly for the OnClickListener):
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
onTaskListener.onTaskLongClick(getAdapterPosition()); // This says that whenever we register a longClick event, we pass the logic onto the taskClick event
return true; // This means that we have successfully consumed the long click event. No other click events will be notified
}
});
Thus avoiding having your itemViewHolder class implement the OnLongClickListener interface and making it impossible to forget to call setOnLongClickListener().
I'm new to android, but have a good JavaFX experience. I'm trying to create a custom view that i can reuse, but having a hard time figuring out the correct way to do it.
In javafx i could achieve this by: Creating a separate fxml file defining the layout of the custom view, then create a controller class linked to the fxml file, in that class, i'd have a method to retrieve the data model of the controller and use it to fill in the labels, etc.
The custom view i want would be
Constrained Layout
TextView (constrained to right anchor)
Round TextView (constrained to left anchor)
What is the best way to do this in android? Also, Is it possible to achieve this with a RecyclerView? If yes, how can i use a custom view for each item and set its data?
The question is broad. You may need additional research on creating views
Create a recyclerview in the main.xml,
a separate file with an item view.
You have 3 views in your item view - white background with margins (linearlayout?), right textView, and left textview.
The left textview should have android:background="drawable/round_shape" and round_shape.xml defined in your drawables folder. Everything is done in 3 xml files, main.xml for recyclerview, item.xml, round_background.xml. Then, the recyclerview adapter to bind the textviews with your array, and recyclerview initialization
A typical RV adaptor
public class MyRV extends RecyclerView.Adapter<MyRV.ViewHolder> {
private List<MyModelItemWith2Strings> mDataSet; // You may need to setup an array,
// with 2 String objects - for the right and left textviews
// Use an array of class with 2 elements rather than <String>, e.g. List<MyModelItemWith2Strings>
// pass your model here
// this setData will be used to provide the contents for the textviews
void setData(List< /* set your 2 string class here*/ > dataSet) {
mDataSet = dataSet;
}
static class ViewHolder extends RecyclerView.ViewHolder {
// Here you bind item TV's
// first you declare textviews that you will use to fill with data
// Add any other item views you will need to fill in
public TextView tv;
public TextView tv2;
public ViewHolder(LinearLayout v) {
super(v);
// Bind itemview views here. Put R.id.tv from your itemview.xml
tv = v.findViewById(R.id.....);
tv2 = v...
}
}
// Add your itemview layout here
#Override
public MyRV.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(/***R.layout.item_view***/, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder( MyRV.ViewHolder h, int position) {
// get content from your model (the above list) and fill in the the itemview textviews
String a= mDataSet.get(position).getItem1();
String b = mDataSet.get(position). getItem2();
...
h.tv.setText(a);
// set clickers if you want to. The clicker class is below.
h.tv.setOnClickListener(new Click(position));
h.tv2.setText(...)
}
// This is obligatory to pass for your RV to initialize. It won't work if you don' t tell Android how to count your array soze
#Override
public int getItemCount() {
return mDataSet.size();
}
// These are my implementation of clickers. I prefer to put them in the nested class of the adapter.
private class Click implements OnClickListener {
private int pos;
Click(int position) {
pos = position;
}
#Override
public void onClick(View p1) {
// get data from your array on click
mDataSet.get(pos);
// Use pos as position on the array, mData.get(pos)
}
}
}
Then, in your main class set a recyclerview
RecyclerView rv = (RecyclerView) findViewById(R.id.rv_In_Main_Xml);
// just additional tunings.
rv.setHasFixedSize(true);
rv.setLayoutManager(new LinearLayoutManager(context)); // <- context = this, if you are in the Main activity
Then set the adapter
MyRV rva = new MyRV();
rva.setData(myArray_with_2_string_objects_to_fill_tvs);
rv.setAdaptor(rva);
And your recycler view gets filled with data
I have a RecyclerView which gets populated by a RealmRecyclerViewAdapter but somehow there are no animations playing when the data changes.
The adapter class uses multiple ViewHolders for different layouts but that should not affect animations right?
public class DiaryPageEntryAdapter extends RealmRecyclerViewAdapter<Entry, RecyclerView.ViewHolder> {
static class MealEntryViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.item_diary_entry_drink_title) TextView tvMealEntryTitle;
#BindView(R.id.item_diary_entry_meal_time) TextView tvMealEntryTime;
BindView(R.id.item_diary_entry_meal_bullet_list) RecyclerView rvBulletList;
MealEntryBulletAdapter bulletAdapter;
public MealEntryViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
this.bulletAdapter = new MealEntryBulletAdapter();
rvBulletList.setLayoutManager(new LinearLayoutManager(itemView.getContext()));
rvBulletList.setAdapter(this.bulletAdapter);
}
void bindData(MealEntry mealEntry) {
tvMealEntryTitle.setText(mealEntry.getTitle());
tvMealEntryTime.setText(DateTimeUtils.timeValueToText(itemView.getContext(), mealEntry.getTime()));
this.bulletAdapter.updateData(mealEntry.getConsumedMeals() ,mealEntry.getConsumedDrinks());
}
}
// Other ViewHolders
public DiaryPageEntryAdapter(#Nullable OrderedRealmCollection<Entry> data, boolean autoUpdate) {
super(data, autoUpdate, true);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case MEAL_ENTRY:
// Inflate meal entry layout and then create a new meal view holder with it
View rowMeal = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_diary_entry_meal, parent, false);
return new MealEntryViewHolder(rowMeal);
// Other case options.
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Entry entry = getItem(position);
if(Entry.isMealEntry(entry)) {
MealEntryViewHolder mealHolder = (MealEntryViewHolder) holder;
mealHolder.bindData(Entry.getMealEntryFromEntry(entry));
}
// Other if branches.
}
#Override
public int getItemViewType(int position) {
Entry entry = getItem(position);
if(Entry.isMealEntry(entry)) return MEAL_ENTRY;
// Other if branches.
}
The code for setting up the RecyclerView and adapter looks as following:
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.frag_diary_page_rv_entries);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
entryAdapter = new DiaryPageEntryAdapter(null, true);
recyclerView.setAdapter(entryAdapter);
The code that binds the data to the adapter is stated below:
RealmResults<Entry> sortedEntries = diaryEntry.getEntries()
.where()
.findAllSortedAsync("time");
entryAdapter.updateData(sortedEntries);
The auto-updates work fine but somehow no there is no animation when the data changes. A new entry simply appears but without animations. Furthermore I want a short animation to play once the RecyclerView is populated for the first time - an entrance animation similar to [http://anthony-skr.com/article/recyclerview-items-animation-with-rebound-effect][1].
Note: In my appĀ“s build.gradle file
dependencies {
// Other dependencies
compile 'io.realm:android-adapters:2.1.0'
}
In the Project build.gradle file:
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
classpath "io.realm:realm-gradle-plugin:3.1.4"
}
From your code sample, it isn't easy to see where you call entryAdapter.updateDate() from, but calling this method continuously on every update will disable all animations as it just calls notifyDataSetChanged().
A more standard pattern would look like this:
RealmResults<Entry> sortedEntries = diaryEntry.getEntries()
.where()
.findAllSortedAsync("time");
entryAdapter = new DiaryPageEntryAdapter(sortedEntries, true);
recyclerView.setAdapter(entryAdapter);
Animations should work if you do the above.
Good day all,
I have a Recyclerview, where on first load achieves the result I want which is grouping all the Trades a specific seller is selling, by that seller.
Example:
Bob selling a watch.
Bob selling a car.
Bob selling a horse.
Button to remove all Bobs trades
Jim selling a house.
Jim selling a monkey.
Button to remove all Jims trades
ect
Example:
The issue is the minute I start scrolling the recyclerview, the rows of items get mixed up.
The way I did this was, I have a single layout that holds a Vertical Linear Layout with a green button below the LinearLayout.
Now I was programatically inflating the view for each Row, then setting the data for that row.
This is my onBindViewHolder:
#Override
public void onBindViewHolder(final ParentCartResultsViewHolder holder, int position) {
final LinkedHashMap<Long, List<Trade>> mapTradesBySeller = CartUtils.getUserToFixedPriceTradeMap();
mTradesBySeller = (new ArrayList<>(mapTradesBySeller.values())).get(holder.getAdapterPosition());
if (mTradesBySeller != null) {
for (Trade trade : mTradesBySeller) {
View singleTrade = LayoutInflater.from(MyApplication.getAppContext()).inflate(R.layout.item_trade_details_include_row, holder.mLinearLayout, false);
TextView tradeTitle = (TextView) singleTrade.findViewById(R.id.trade_details_include_trade_title);
tradeTitle.setText(trade.getTitle());
TextView endDate = (TextView) singleTrade.findViewById(R.id.trade_details_include_trade_ending_time);
endDate.setText(trade.getUserAlias() + " : " + trade.getUserId());
holder.mLinearLayout.addView(singleTrade);
}
}
}
My onCreateViewHolder:
#Override
public ParentCartResultsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.cart_grouped_by_seller, parent, false);
return new ParentCartResultsViewHolder(v);
}
Now I think I know what the issue is, its just I cant figure out how I can fix it.
The problem is the creating of the views and adding them to the LinearLayout is in the onBindViewHolder, as this runs numerous times
You can use the library SectionedRecyclerViewAdapter to easily group your data into sections and add a footer to each section.
First create a Section class:
class TradeSection extends StatelessSection {
List<String> list;
public TradeSection(List<String> list) {
// call constructor with layout resources for this Section header, footer and items
super(-1, R.layout.section_item, R.layout.section_footer);
// remove header
this.setHasHeader(false);
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));
}
#Override
public RecyclerView.ViewHolder getFooterViewHolder(View view) {
return new MyFooterViewHolder(view);
}
#Override
public void onBindFooterViewHolder(RecyclerView.ViewHolder holder) {
MyFooterViewHolder footerHolder = (MyFooterViewHolder) holder;
// bind your footer view here
footerHolder.tvItem.setText(title);
}
}
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 for each year
TradeSection section1 = new TradeSection(bobDataList);
TradeSection section2 = new TradeSection(jimDataList);
// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
I am using RecyclerView (and GridLayout) to place dynamic buttons in a grid. What is the best way to set up a DIFFERENT onClickListener for each dynamic button as it is created using RecyclerView and placed in the Gridlayout?
My buttons are created randomly depending on a user action by passing a drawable to my RecyclerView in the method "createButton" below. Only one drawable gets passed to my gridLayout at a time, and each time a new onClickListener must be created. What is the best way to go about this?
private GridLayoutManager lLayout;
RecyclerViewAdapter rcAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<ItemObject> myList = new ArrayList<>();
rcAdapter = new RecyclerViewAdapter(getActivity(),myList);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.home_fragment, container, false);
lLayout = new GridLayoutManager(getActivity(), 3,
GridLayoutManager.HORIZONTAL, false);
RecyclerView rView = (RecyclerView)view.findViewById(R.id.recycler_view);
rView.setHasFixedSize(true);
rView.setLayoutManager(lLayout);
rView.setAdapter(rcAdapter);
return view;
}
public void createButton (Drawable d, String appName){
rcAdapter.addItem(new ItemObject(appName, d));
}
I don't think you need a new click listener every time you just need a click listener that is aware of the ItemObject. For that, I'll give you my usual approach for such a pattern:
Somewhere in your code you have an RecyclerView.ViewHolder, you should make that view holder implement OnClickListener and give pass the reference of ItemObject to the holder during OnBind, like following:
class MyHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ItemObject itemObject;
public MyHolder(View itemView) {
super(itemView);
itemView.findViewById(your button Id).setOnClickListener(this); // make this holder receives the clicks
}
#Override
public void onClick(View view) {
// here you add logic that depending on the data from itemObject
}
}
and then during onBind you must properly set the ItemObject
#Override
public void onBindViewHolder (MyHolder holder, int position) {
ItemObject itemObject = list.get(position);
holder.itemObject = itemObject;
// the rest of your bind code....
}