How to call bindViewHolder when RecyclerView data changes [duplicate] - java

I have a RecyclerView with an TextView text box and a cross button ImageView. I have a button outside of the recyclerview that makes the cross button ImageView visible / gone.
I'm looking to remove an item from the recylerview, when that items cross button ImageView is pressed.
My adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private ArrayList<String> mDataset;
private static Context sContext;
public MyAdapter(Context context, ArrayList<String> myDataset) {
mDataset = myDataset;
sContext = context;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);
ViewHolder holder = new ViewHolder(v);
holder.mNameTextView.setOnClickListener(MyAdapter.this);
holder.mNameTextView.setOnLongClickListener(MyAdapter.this);
holder.mNameTextView.setTag(holder);
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mNameTextView.setText(mDataset.get(position));
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public void onClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onLongClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
mDataset.remove(holder.getPosition());
notifyDataSetChanged();
Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
Toast.LENGTH_SHORT).show();
}
return false;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mNumberRowTextView;
public TextView mNameTextView;
public ViewHolder(View v) {
super(v);
mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
}
}
}
My layout is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:id="#+id/layout">
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
android:background="#drawable/greyline"/>
<ImageView
android:id="#+id/crossButton"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="gone"
android:layout_marginLeft="50dp"
android:src="#drawable/cross" />
</LinearLayout>
How can I get something like an onClick working for my crossButton ImageView? Is there a better way? Maybe changing the whole item onclick into a remove the item? The recyclerview shows a list of locations that need to be edited. Any technical advice or comments / suggestions on best implementation would be hugely appreciated.

I have done something similar.
In your MyAdapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public CardView mCardView;
public TextView mTextViewTitle;
public TextView mTextViewContent;
public ImageView mImageViewContentPic;
public ImageView imgViewRemoveIcon;
public ViewHolder(View v) {
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
mTextViewContent = (TextView) v.findViewById(R.id.item_content);
mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
//......
imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);
mTextViewContent.setOnClickListener(this);
imgViewRemoveIcon.setOnClickListener(this);
v.setOnClickListener(this);
mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(view, getPosition());
}
return false;
}
});
}
#Override
public void onClick(View v) {
//Log.d("View: ", v.toString());
//Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
if(v.equals(imgViewRemoveIcon)){
removeAt(getPosition());
}else if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getPosition());
}
}
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
mDataset.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
}
Edit:
getPosition() is deprecated now, use getAdapterPosition() instead.

first of all, item should be removed from the list!
mDataSet.remove(getAdapterPosition());
then:
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(), mDataSet.size()-getAdapterPosition());

if still item not removed use this magic method :)
private void deleteItem(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
holder.itemView.setVisibility(View.GONE);
}
Kotlin version
private fun deleteItem(position: Int) {
mDataSet.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, mDataSet.size)
holder.itemView.visibility = View.GONE
}

The Problem
RecyclerView was built to display data in an efficient and responsive manner.
Usually you have a dataset which is passed to your adapter and is looped through to display your data.
Here your dataset is:
private ArrayList<String> mDataset;
The point is that RecyclerView is not connected to your dataset, and therefore is unaware of your dataset changes.
It just reads data once and displays it through your ViewHolder, but a change to your dataset will not propagate to your UI.
This means that whenever you make a deletion/addition on your data list, those changes won't be reflected to your RecyclerView directly. (i.e. you remove the item at index 5, but the 6th element remains in your recycler view).
A (old school) solution
RecyclerView exposes some methods for you to communicate your dataset changes, reflecting those changes directly on your list items.
The standard Android APIs allow you to bind the process of data removal (for the purpose of the question) with the process of View removal.
The methods we are talking about are:
notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)
A Complete (old school) Solution
If you don't properly specify what happens on each addition, change or removal of items, RecyclerView list items are animated unresponsively because of a lack of information about how to move the different views around the list.
The following code will allow RecyclerView to precisely play the animation with regards to the view that is being removed (And as a side note, it fixes any IndexOutOfBoundExceptions, marked by the stacktrace as "data inconsistency").
void remove(position: Int) {
dataset.removeAt(position)
notifyItemChanged(position)
notifyItemRangeRemoved(position, 1)
}
Under the hood, if we look into RecyclerView we can find documentation explaining that the second parameter we pass to notifyItemRangeRemoved is the number of items that are removed from the dataset, not the total number of items (As wrongly reported in some others information sources).
/**
* Notify any registered observers that the <code>itemCount</code> items previously
* located at <code>positionStart</code> have been removed from the data set. The items
* previously located at and after <code>positionStart + itemCount</code> may now be found
* at <code>oldPosition - itemCount</code>.
*
* <p>This is a structural change event. Representations of other existing items in the data
* set are still considered up to date and will not be rebound, though their positions
* may be altered.</p>
*
* #param positionStart Previous position of the first item that was removed
* #param itemCount Number of items removed from the data set
*/
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
Open source solutions
You can let a library like FastAdapter, Epoxy or Groupie take care of the business, and even use an observable recycler view with data binding.
New ListAdapter
Google recently introduced a new way of writing the recycler view adapter, which works really well and supports reactive data.
It is a new approach and requires a bit of refactoring, but it is 100% worth switching to it, as it makes everything smoother.
here is the documentation, and here a medium article explaining it

Here are some visual supplemental examples. See my fuller answer for examples of adding and removing a range.
Add single item
Add "Pig" at index 2.
String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);
Remove single item
Remove "Pig" from the list.
int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);

Possibly a duplicate answer but quite useful for me. You can implement the method given below in RecyclerView.Adapter<RecyclerView.ViewHolder>
and can use this method as per your requirements, I hope it will work for you
public void removeItem(#NonNull Object object) {
mDataSetList.remove(object);
notifyDataSetChanged();
}

I tried all the above answers, but inserting or removing items to recyclerview causes problem with the position in the dataSet. Ended up using delete(getAdapterPosition()); inside the viewHolder which worked great at finding the position of items.

The problem I had was I was removing an item from the list that was no longer associated with the adapter to make sure you are modifying the correct adapter you can implement a method like this in your adapter:
public void removeItemAtPosition(int position) {
items.remove(position);
}
And call it in your fragment or activity like this:
adapter.removeItemAtPosition(position);

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;
public MyAdapter(Context context, List<cardview_widgets> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
viewGroup, false);
return new MyViewHolder(view);
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView txtValue;
TextView txtCategory;
ImageView imgInorEx;
ImageView imgCategory;
TextView txtDate;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txtValue= itemView.findViewById(R.id.id_values);
txtCategory= itemView.findViewById(R.id.id_category);
imgInorEx= itemView.findViewById(R.id.id_inorex);
imgCategory= itemView.findViewById(R.id.id_imgcategory);
txtDate= itemView.findViewById(R.id.id_date);
}
}
#NonNull
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, int i) {
myViewHolder.txtValue.setText(String.valueOf(list.get(i).getValuee()));
myViewHolder.txtCategory.setText(list.get(i).getCategory());
myViewHolder.imgInorEx.setBackgroundColor(list.get(i).getImg_inorex());
myViewHolder.imgCategory.setImageResource(list.get(i).getImg_category());
myViewHolder.txtDate.setText(list.get(i).getDate());
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
list.remove(myViewHolder.getAdapterPosition());
notifyDataSetChanged();
return false;
}
});
}
#Override
public int getItemCount() {
return list.size();
}}
i hope this help you.

if you want to remove item you should do this:
first remove item:
phones.remove(position);
in next step you should notify your recycler adapter that you remove an item by this code:
notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());
but if you change an item do this:
first change a parameter of your object like this:
Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);
or new it like this :
Service s = new Service();
services.set(position,s);
then notify your recycler adapter that you modify an item by this code:
notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());
hope helps you.

String str = arrayList.get(position);
arrayList.remove(str);
MyAdapter.this.notifyDataSetChanged();

To Method onBindViewHolder Write This Code
holder.remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
values.remove(position);
notifyDataSetChanged();
}
});

Incase Anyone wants to implement something like this in Main class instead of Adapter class, you can use:
public void removeAt(int position) {
peopleListUser.remove(position);
friendsListRecycler.getAdapter().notifyItemRemoved(position);
friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}
where friendsListRecycler is the Adapter name

you must to remove this item from arrayList of data
myDataset.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());

//////// set the position
holder.cancel.setTag(position);
///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
mDataset.remove(positionToRemove);
notifyDataSetChanged();
}
});

make interface into custom adapter class and handling click event on recycler view..
onItemClickListner onItemClickListner;
public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public interface onItemClickListner {
void onClick(Contact contact);//pass your object types.
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
// below code handle click event on recycler view item.
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onClick(mContectList.get(position));
}
});
}
after define adapter and bind into recycler view called below code..
adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
#Override
public void onClick(Contact contact) {
contectList.remove(contectList.get(contectList.indexOf(contact)));
adapter.notifyDataSetChanged();
}
});
}

In case you are wondering like I did where can we get the adapter position in the method getadapterposition(); its in viewholder object.so you have to put your code like this
mdataset.remove(holder.getadapterposition());

In the activity:
mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);
In the your adapter:
void removeAt(int position) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
void updateAt(int position, String text, Boolean completed) {
TodoEntity todoEntity = list.get(position);
todoEntity.setText(text);
todoEntity.setCompleted(completed);
notifyItemChanged(position);
}

in 2022, after trying everything the whole internet given below is the answer
In MyViewHolder class
private myAdapter adapter;
inside MyViewHolder function initalise adapter
adapter = myAdapter.this
inside onclick
int position = getAdapterPosition()
list.remove(position);
adapter.notifyItemRemoved(position);

Related

Remove certain items which are empty from RecyclerView list?

I'm making an app using TMDB API and have gotten stuck at a small issue.
TMDB API shows seasons and episodes which are empty, basically, those are yet to air but since those are empty, the app shows a blank item that I'm trying to get rid of.
Here's my adapter:
public class SeasonAdapter extends RecyclerView.Adapter<SeasonAdapter.ViewHolder> {
private final List<Season> seasons;
private final Context context;
private final RequestOptions requestOptions;
public SeasonAdapter(List<Season> seasons, Context context) {
this.seasons = seasons;
this.context = context;
requestOptions = new RequestOptions().centerCrop().placeholder(R.drawable.poster_placeholder).error(R.drawable.poster_placeholder);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_season_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final Season season = seasons.get(position);
holder.tvTitle.setText(season.getSeasonTitle());
if (season.getSeasonDate() != null && !season.getSeasonDate().isEmpty()) {
holder.tvDate.setText(context.getResources().getString(R.string.aired_on) + season.getSeasonDate());
} else {
holder.tvDate.setVisibility(View.GONE);
}
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
holder.tvEpisodes.setText(String.valueOf(season.getSeasonEpisodes()) + context.getResources().getString(R.string.total_episodes));
Glide.with(context).load(season.getSeasonImageURL()).apply(requestOptions).into(holder.ivPoster);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, EpisodeActivity.class);
intent.putExtra("title", season.getShowTitle());
intent.putExtra("seasonTitle", season.getSeasonTitle());
intent.putExtra("seasonNo", season.getSeasonNo());
intent.putExtra("tvId", season.getTvId());
v.getContext().startActivity(intent);
});
}
#Override
public int getItemCount() {
return seasons.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView ivPoster;
public TextView tvTitle, tvDate, tvEpisodes;
public ViewHolder(#NonNull View itemView) {
super(itemView);
ivPoster = itemView.findViewById(R.id.ivSeasonPoster);
tvTitle = itemView.findViewById(R.id.tvSeasonTitle);
tvDate = itemView.findViewById(R.id.tvSeasonAired);
tvEpisodes = itemView.findViewById(R.id.tvSeasonEpisodes);
//Poster Corners
ivPoster.setClipToOutline(true);
}
}
}
I tried doing this:
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
It does seem to hide the season which has no episodes but if a show has multiple seasons without episodes, my app crashes so I figured this isn't the right solution so any help is appreciated.
I suggest performing that removal logic in the constructor of the adapter rather than in onBind. onBind happens as the recycler view is finalising the details of each view holder immediately before it's shown to the user. You want to do as little as possible logic in here to keep the recycler view performant.
Inside the constructor (or even before the list is passed in) you should perform a loop and remove those items that don't meet the criteria before setting the instance variable.
It's been a long time since I wrote code in java and so I'd end up with unhelpful incorrect syntax if I tried to do it here.

Identify a button click on list view android

I have created a ListView using a custom adapter that extends ArrayAdapter<String>. I need to add the item name to an ArrayList when it clicks on the + button.
You can do it exactly as usual (like in MainActivity), just link it in GetView(...)
Inside your ArrayAdapter.java , add this:
private OnItemClickListener mListener;
public interface OnItemClickListener
{
void onAddClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener)
{
mListener = listener;
}
Inside your Holder Function on same file, make this:
AddButtonVariable.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null)
{
int position = getAdapterPosition();
if(position != ListView.NO_POSITION)
{
listener.onAddClick(position);
}
}
}
});
Inside in your onResponse function of Activity.java file add this, after setAdapter:
listAdapter.setOnItemClickListener(new ArrayAdapter.OnItemClickListener() {
#Override
public void onAddClick(int position){
functiontoAdd(String.valueOf(position));
}
});
Now, at last, inside your Activity.Java file, at the end (before closing bracket of class), add this:
private void functiontoAdd(String position)
{
// rest of the code to add item in cart
}
Look, it is simpler with using Recycler view instead of list view. But it is ok if you already created.
listview.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position,
long id)
{
//I am assuming the ArrayAdapter is having list of dish name as string if the list is of Custom class please change the below code as per it.
String dish = (String)adapter.getItemAtPosition(position);
}
});
adapter AdapterView: The AdapterView where the click happened.
view View: The view within the AdapterView that was clicked (this will be a view provided by the adapter)
position int: The position of the view in the adapter.
id long: The row id of the item that was clicked.

Using multiple onClick methods in a single interface for RecyclerView

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().

Can single Activity be used for multiple actions?

The cheat code app will have a list of games. And when each game is tapped, the cheat codes for that game will be displayed. My question is, do I have to create an activity for each game listed or is there a way to make it work in one activity.
No you don’t need to make individual activity for each game listed rather you can go for a RecyclerView and fragments combination to make it work in one activity.
here i have added a recyclerView for the game list and the when items of the game list will be clicked then the recylerview will be hidden and the frameLayout id(content) will be populated with the fragment and when the back btn is pressed while inside the fragment reverse will happen( hiding the fragment and showing the recyclerView).
though this whole approach can be far more simplified if all the game have similar type of data to show then you can,
in that case you can pass data to the fragment and make it work with only one fragment.
hope this answer the question.
**
XML code
**
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
**
Adapter code
**
public abstract class SettingsAdapter extends RecyclerView.Adapter<SettingsAdapter.MyViewHolder> {
private static final String TAG = "SettingsAdapter";
private LayoutInflater inflater;
private Context context;
private List<SettingData> data;
public SettingsAdapter(Context context , List<SettingData> data){
inflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_settings_row, parent, false);
SettingsAdapter.MyViewHolder holder = new SettingsAdapter.MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
SettingData current = data.get(position);
holder.settinsName.setText(current.settingName);
holder.hostRelativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OnButtonClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return data.size();
}
protected abstract void OnButtonClicked(View v ,int position);
class MyViewHolder extends RecyclerView.ViewHolder {
private final TextView settinsName;
private final RelativeLayout hostRelativeLayout;
public MyViewHolder(View itemView) {
super(itemView);
settinsName = itemView.findViewById(R.id.settings_name);
hostRelativeLayout = itemView.findViewById(R.id.hostRelativeLayout);
}
}
}
**
android activity code inside onCreate
**
SettingsAdapter settingsAdapter = new SettingsAdapter(this, getFinalData()) {
#SuppressLint("RestrictedApi")
#Override
protected void OnButtonClicked(View v, int position) {
switch (position) {
case 0:
settingsContent.setVisibility(View.GONE);
fab.show();
configAppbarTittle(StudentSettingsActivity.this, settingNameArr[position]);
appBarLayout.setExpanded(false);
getSupportFragmentManager().beginTransaction().replace(R.id.content, new SavedPlacesFragment()).commit();
break;
case 1:
//similar
break;
case 2:
//similar
break;
case 3:
//similar
break;
default:
break;
}
}
};
settingsRecycleView.setAdapter(settingsAdapter);
settingsRecycleView.setLayoutManager(new LinearLayoutManager(this));
**
fragment code inside activity
**
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class SavedPlacesFragment extends Fragment {
private StudentSettingsActivity myMainActivity;
private RecyclerView savedPlacesRecycler;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
myMainActivity = (StudentSettingsActivity) getActivity();
}
#SuppressWarnings("unchecked")
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//myMainActivity = (StudentSettingsActivity) getActivity();
View rootView = inflater.inflate(R.layout.stu_setting_saved_places_fragment, container, false);
return rootView;
}
private void flush(String msg) {
Toast.makeText(myMainActivity, msg, Toast.LENGTH_SHORT).show();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
myMainActivity.fab.hide();
startActivity(new Intent(getActivity(), StudentSettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
Please understand the fact that i have copy pasted from my production
code here. Just to demonstrate the way. If you copy paste this code it
won't work. You have to read through the code to understand the way
it's been implemented and then write it in your own way
...You say that the cheat code app have list of games
You create one activity to show the list
....And if u tap on list to get new game
So you have to create fragment or activity to show the game by click on list
You can create a single Activity and have it hold multiple fragments.
Your Activity ( Lets say MainActivity ) will be responsible for displaying fragments.
At a high level, you need 2 fragments:
GameListFragment
for displaying Game List and game item click. You will need a recycler view to display list of games on this fragment.
GameCheatCodeDetailsFragment
for displaying Cheat Code.
Refer to SmartShows to get some code reference.

RecyclerView items in ArrayList

I am working on an android project and I am putting some TextViews inside a RecyclerView and at the same time I am trying to put those things in an array list as ViewHolder type. After some tests on the program i understood that the items that are inserted in the ArrayList are only the items that are shown in the screen. For example if my screen fits 15 textviews and i put 30 textviews inside the recycler view and arraylist, the size of the arraylist will be only 15 so i can't make any changes to the rest of the items.
Also when i scroll down the recycler view the arraylist get a size of the items that has been shown while scrolling but when i scroll back to the top and try to change the number of the TextViews and make them less the program crashes.
What i want is to have all of the items that have been added to the recycler view also in the arraylist in order to can use them.
Recycler View class code:
public class Tab1Child1Numbers extends RecyclerView.Adapter<Tab1Child1Numbers.ViewHolder> {
ArrayList<Integer> textFront;
ArrayList<Integer> textBack;
ArrayList<Integer> colors;
Context context;
ArrayList<ViewHolder> texts = new ArrayList<>();
public Tab1Child1Numbers(Context context, ArrayList<Integer> textFront, ArrayList<Integer> textBack, ArrayList<Integer> colors) {
super();
this.context = context;
this.textFront = textFront;
this.textBack = textBack;
this.colors = colors;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.tab1_child1_numbers, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
viewHolder.textFront.setText(textFront.get(i)+"");
viewHolder.textBack.setText(textBack.get(i)+"");
viewHolder.textFront.setBackgroundColor(colors.get(i));
viewHolder.textBack.setBackgroundColor(colors.get(i));
texts.add(viewHolder);
// cardsFront.add(viewHolder.imageFront);
// cardsBack.add(viewHolder.imgThumbnail);
viewHolder.setClickListener(new ItemClickListener() {
#Override
public void onClick(View view, int position, boolean isLongClick) {
if (isLongClick) {
} else {
Deck deck = new Deck();
deck.flipCard(texts.get(position).frame, texts.get(position).textFront, texts.get(position).textBack);
}
}
});
}
#Override
public int getItemCount() {
return textFront.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public TextView textFront;
public TextView textBack;
public FrameLayout frame;
public LinearLayout layout;
private ItemClickListener clickListener;
public ViewHolder(View itemView) {
super(itemView);
textFront = (TextView) itemView.findViewById(R.id.txt1);
textBack = (TextView) itemView.findViewById(R.id.txt2);
frame = (FrameLayout) itemView.findViewById(R.id.list_frame);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public void setClickListener(ItemClickListener itemClickListener) {
this.clickListener = itemClickListener;
}
#Override
public void onClick(View view) {
clickListener.onClick(view, getPosition(), false);
}
#Override
public boolean onLongClick(View view) {
clickListener.onClick(view, getPosition(), true);
return true;
}
}
class Deck {
private void flipCard(View rootLayout, View cardFace, View cardBack) {
FlipAnimation flipAnimation = new FlipAnimation(cardFace, cardBack);
if (cardFace.getVisibility() == View.GONE) {
flipAnimation.reverse();
}
rootLayout.startAnimation(flipAnimation);
}
public void flipAll(){
System.out.println(texts.size()+"--------");
randomize();
for (int i = 0; i < texts.size(); i++) {
flipCard(texts.get(i).frame, texts.get(i).textFront, texts.get(i).textBack);
}
}
private void randomize(){
for (int i=0; i<texts.size(); i++){
if (texts.get(i).textFront.getVisibility() == View.GONE) {
texts.get(i).textFront.setText(textFront.get(i) + "");
texts.get(i).textFront.setBackgroundColor(colors.get(i));
}
else {
texts.get(i).textBack.setText(textBack.get(i) + "");
texts.get(i).textBack.setBackgroundColor(colors.get(i));
}
}
}
}
It looks like you're trying to do something that you shouldn't.
onBindViewHolder() is called by the system whenever it is about to display a ViewHolder to the user. So it will initially be called once for every view on the screen, plus once for a small number of views just off-screen (so that they can scroll on screen nicely).
However, this means it will also be called repeatedly as the user scrolls through the data in your adapter. In these cases, the ViewHolder being passed to onBindViewHolder() might have been recycled, meaning it might previously have been used to display different data and is now being re-used to display new data.
Let's say you have one million items in your data set, but your layout is set up in such a way that only 15 items are visible on screen at any one time. Chances are good that the system will wind up creating about 20 ViewHolder instances (15 on the screen, 5 available for re-use just off-screen). These same 20 ViewHolders will be used to display all one million items if the user scrolls enough. They'll just keep being re-used over and over.
That's why this line is problematic:
texts.add(viewHolder);
You're going to wind up building a list with potentially millions of objects in it, with the same 20 ViewHolders appearing multiple times in your list.
It would be much better to think about storing your data in a different way, rather than trying to store ViewHolders passed to onBindViewHolder().

Categories