I have this error in my code:
Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later.
I even used holder.getAdapterPosition() in the position place, but it still keeps crashing the app when I click the items.
MusicAdapter(Context mcontext, ArrayList<MusicFile> mfiles) {
this.mfiles = mfiles;
this.mcontext = mcontext;
}
#Override // androidx.recyclerview.widget.RecyclerView.Adapter
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(this.mcontext).inflate(R.layout.music_items, parent, false));
}
public void onBindViewHolder( final MyViewHolder holder, int position) {
holder.file_name.setText(this.mfiles.get(position).getTitle());
byte[] image = getAlbumArt(this.mfiles.get(position).getPath());
if (image != null) {
Glide.with(this.mcontext).asBitmap().load(image).into(holder.album_art);
} else {
Glide.with(this.mcontext).load((int) R.drawable.ic_launcher_background).into(holder.album_art);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent(MusicAdapter.this.mcontext, PlayerActivity.class);
intent.putExtra("position", holder.getAdapterPosition());
MusicAdapter.this.mcontext.startActivity(intent);
}
});
}
#Override // androidx.recyclerview.widget.RecyclerView.Adapter
public int getItemCount() {
return this.mfiles.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
ImageView album_art;
TextView file_name;
LinearLayout linearLayout;
public MyViewHolder(View itemView) {
super(itemView);
this.file_name = (TextView) itemView.findViewById(R.id.music_file_name);
this.album_art = (ImageView) itemView.findViewById(R.id.music_img);
linearLayout = itemView.findViewById(R.id.linear_layout);
}
}
private byte[] getAlbumArt(String uri) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(uri);
byte[] art = retriever.getEmbeddedPicture();
retriever.release();
return art;
}
}
Imagine that we have a RecyclerView that will display 10 items so it will create 10 items and call onBindView for those 10 items and pass the positions from 0 to 9, so if you fixed the position by using it to handle user clicks and later you added an item at position 0 and notified the data set that you inserted a new item by notifyItemInserted() the RecyclerView will create a new item with position 0 and pass it to the layout but the pre created ones still have the old positions and if you logged those positions you will have 00123…9 which is not true it should be 0123…10. Here come the power of holder.getAdapterPosition().
#Override
public void onBindViewHolder(#NonNull GalleryViewAdapter holder,final int position) {
Glide.with(context).load(images.get(position)).into(holder.imageView);
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, FullImageview.class);
intent.putExtra("image", images.get(holder.getAdapterPosition()));
context.startActivity(intent);
}
});
}
now its fine to use holder.getAdapterPosition for user click event
notice that i used getAdapterPosition to bind data and used getLayoutPosition to tell the user the position of the pressed item.
Related
I am trying to Change the Background of certain CardViews when the user click and Opens the particular items.
This is achieved, but as I scroll to bottom or top, certain other cards also change their colors. Why is that ? Can u guys please provide a Fix.
Here is my Adapter Code :
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.resources_layout, parent, false);
return new ResourcesAdapter.ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
final ResourcesModel resourcesModel = resourcesModelList.get(position);
holder.book_title.setText(resourcesModel.getBook_title());
holder.book_description.setText(resourcesModel.getBook_description());
holder.book_price.setText(resourcesModel.getBook_price());
Glide.with(context).load(resourcesModel.getBook_image()).placeholder(R.drawable.placeholder).into(holder.book_image);
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, ResourcesDetails.class);
intent.putExtra("Title", resourcesModel.getBook_title());
intent.putExtra("Description", resourcesModel.getBook_description());
intent.putExtra("Price", String.valueOf(resourcesModel.getBook_price()));
intent.putExtra("BookImage", resourcesModel.getBook_image());
intent.putExtra("PDFS", resourcesModel.getPdf());
context.startActivity(intent);
}
});
String k = holder.book_title.getText().toString();
SharedPreferences myPreferences;
SharedPreferences.Editor myEditor;
myPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String name = myPreferences.getString(k, "notWatched");
Log.e("Name Value ADAPTER :", String.valueOf(name));
if (myPreferences.contains(k)) {
holder.cardView.setBackgroundColor(context.getResources().getColor(R.color.color_bought));
}
#Override
public int getItemCount() {
return resourcesModelList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
ImageView book_image;
TextView book_title, book_description, book_price, pdfLinks;
Button buy_btn;
MaterialCardView cardView;
ImageView tick;
public ViewHolder(#NonNull View itemView) {
super(itemView);
book_image = itemView.findViewById(R.id.book_img);
book_title = itemView.findViewById(R.id.book_title);
book_description = itemView.findViewById(R.id.book_description);
book_price = itemView.findViewById(R.id.book_price);
cardView = itemView.findViewById(R.id.cardView);
}
if (myPreferences.contains(k)) {
holder.cardView.setBackgroundColor(context.getResources().getColor(R.color.color_bought));
}
You're not setting the background color back to the default if it's not in myPreferences. Because of ViewHolder recycling, you need an } else { case to handle this.
I have ActivityCourses and ActivityNewCourse. ActivityCourses include a list (recyclerview) of custom courses that user make, and ActivityNewCourse obviously is the activity where the user makes those custom courses.
ActivityNewCourse contains primitive data (course name and number of holes)
those are the data, which I pass to ActivityCourses, and make new item there in its recyclerview (so the new items name is that course name which is passed from ActivityNewCourse).
The problem I have is that ActivityNewCourse also contains recyclerview, and that contains obviously unique items. I need to get all those items, from ActivityNewCourse, and get them STORED (not shown) in that same item, where I send primitive data in ActivityCourses.
I've tried to use interface in my NewCourseAdapter, to pass those items from ActivityNewCourse recyclerview, to my ActivityCourses item, but the problem is that I need all of them items, not just 1. and also that button "Save Course" which user clicks to obviously save all the data from ActivityNewCourse, is outside of the recyclerview, where those items are located.
HERE IS MY NEW COURSE ADAPTER
public class NewCourseAdapter extends RecyclerView.Adapter<NewCourseAdapter.NewCourseViewHolder> {
private ArrayList<NewCourseItem> mNewCourseList;
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onMinusClick(int position);
void onPlusClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
public static class NewCourseViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView1, mTextView2;
public ImageView mImageView1, mImageView2;
public NewCourseViewHolder(#NonNull View itemView, final OnItemClickListener listener) {
super(itemView);
mTextView1 = itemView.findViewById(R.id.hole_number);
mTextView2 = itemView.findViewById(R.id.par_number);
mImageView1 = itemView.findViewById(R.id.item_minus_btn);
mImageView2 = itemView.findViewById(R.id.item_plus_btn);
mImageView1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onMinusClick(position);
}
}
}
});
mImageView2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onPlusClick(position);
}
}
}
});
}
}
public NewCourseAdapter(ArrayList<NewCourseItem> newCourseList) {
mNewCourseList = newCourseList;
}
#NonNull
#Override
public NewCourseViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_course_item, parent, false);
NewCourseViewHolder evh = new NewCourseViewHolder(v, mListener);
return evh;
}
#Override
public void onBindViewHolder(#NonNull NewCourseViewHolder holder, int position) {
NewCourseItem currentItem = mNewCourseList.get(position);
holder.mTextView1.setText(currentItem.getText1());
holder.mTextView2.setText(currentItem.getText2());
holder.mImageView1.setImageResource(currentItem.getImageMinus());
holder.mImageView2.setImageResource(currentItem.getImagePlus());
}
#Override
public int getItemCount() {
return mNewCourseList.size();
}
}
HERES HOW I PASS PRIMITIVE DATA FROM ACTIVITYNEWCOURSE TO ACTIVITYCOURSES
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
courseName = findViewById(R.id.course_name_input);
number = findViewById(R.id.number_of_holes_number);
String intentCourseName = courseName.getText().toString().trim();
String holeNumber = number.getText().toString().trim();
Intent intent = new Intent(ActivityNewCourse.this, ActivityCourses.class);
intent.putExtra("COURSENAME", intentCourseName);
intent.putExtra("HOLENUMBER", holeNumber);
startActivity(intent);
}
});
HERES HOW ACTIVITYCOURSES RESIVE THE DATA
public void addItem() {
if (getIntent().getStringExtra("COURSENAME") != null) {
mCourselist.add(new CoursesItem(getIntent().getStringExtra("COURSENAME"), "Holes:", getIntent().getStringExtra("HOLENUMBER"), R.drawable.ic_delete));
}
}
I'm not sure if this code helped or not...
Am I somehow be able to send the whole arraylist from ActivityNewCourse, when save button been clicked? I don't know, I'm kinda dead end and I have no clue what to do, so any suggestions on what to do in this situation would help...
Have you tried using intent.putStringArrayListExtra("ARRAYNAME", arrayName);
try putting your data in an ArrayList and send it like this:
Intent intent = new Intent(ActivityNewCourse.this, ActivityCourses.class);
intent.putStringArrayListExtra("ARRAYNAME", arrayName);
startActivity(intent);
and the retrieve it like this:
ArrayList<String> listOfData = getIntent().getStringArrayListExtra("ARRAYNAME");
Hope it helps.
updated
Using Serializable:
first extend NewCourseItem to Serializable then:
Bundle bundle = new Bundle();
bundle.putSerializable("KEYNAME", new NewCourseItem("str1", "str2", "imglink", "imglink"));
intent.putExtras(bundle);
startActivity(intent);
retrieve it like this:
Intent intent = this.getIntent();
Bundle bundle = intent.getExtras();
NewCourseItem item = (NewCourseItem)bundle.getSerializable("KEYNAME");
I have a RecyclerView.Adapter which has some Arrays there.
ArrayList with Strings and ArrayList with Integer. Strings are like url and Integer is the photo.
When the app is open for first time the first item is selected.
I have another method for click which makes another item as selected and this works, but the problem is that the first item stays as selected and so for every image click makes as selected, I want only one item to be selected and take a color.
This is my code.
Adapter of RecyclerView
public class ListViewAdapter extends RecyclerView.Adapter<ListViewAdapter.ViewHolder>{
private int selectedItem;
private ArrayList<Integer> mImages = new ArrayList<>();
private ArrayList<String> mSearchUrl = new ArrayList<>();
private Context mContext;
public ListViewAdapter(ArrayList<Integer> images, ArrayList<String> SearchUrl, Context context) {
mImages = images;
mContext = context;
mSearchUrl = SearchUrl;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.s_engine_item, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, final int i) {
selectedItem = 0;
if (selectedItem == i) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}
Glide.with(mContext).load(mImages.get(i))
.into(viewHolder.image);
viewHolder.searchUrl.setText(mSearchUrl.get(i));
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
selectedItem = i;
}
});
}
#Override
public int getItemCount() {
return mImages.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView image;
TextView searchUrl;
public ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.ivEngine);
searchUrl = itemView.findViewById(R.id.ivEngineText);
}
}
}
And this is the MainActivity.class
public void intSearch() {
mImages.add(R.drawable.s_bing);
mSearchUrl.add("https://www.bing.com/search?q=");
mImages.add(R.drawable.s_google);
mSearchUrl.add("https://www.google.com/search?q=");
mImages.add(R.drawable.s_yahoo);
mSearchUrl.add("www.yahoo.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
mImages.add(R.drawable.amazon_white256);
mSearchUrl.add("www.amazon.com");
initRecyclerView();
}
private void initRecyclerView() {
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
RecyclerView recyclerView = findViewById(R.id.lvEngines);
recyclerView.setLayoutManager(layoutManager);
ListViewAdapter adapter = new ListViewAdapter(mImages, mSearchUrl, this);
recyclerView.setAdapter(adapter);
}
Initialize your selected item globally
public class ListViewAdapter extends RecyclerView.Adapter<ListViewAdapter.ViewHolder>{
private int selectedItem = 0;
.....
Then inside your onBindViewHolder whenever you click a new Image notify your adapter for changes in the last selected item cell.
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int previousSelectedItem = selectedItem;
selectedItem = i;
notifyItemChanged(previousSelectedItem);
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}
});
Just remove this line from onBindViewHolder
selectedItem = 0;
and add an else to the background condition, like:
if (selectedItem == i) {
viewHolder.image.setBackgroundColor(Color.parseColor("#30000000"));
}else{
viewHolder.image.setBackgroundColor(“YOUR_DEFAULT_COLOR”);
}
and update the onClick:
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedItem = i;
notifyDataSetChanged();
}
});
I'm using this CarouselView library.
I have used Recycler View and Card View to create the following UI.
In each card view, I have added this carousel view and contains images inside carousel.
This library has the setImageClickListener that gives the index of each clicked image in each card view.
customCarouselView.setImageClickListener(new ImageClickListener() {
#Override
public void onClick(int position) {
Toast.makeText(SampleCarouselViewActivity.this, "Clicked item: "+ position, Toast.LENGTH_SHORT).show();
}
});
I want to know which image is clicked and from which card that image belongs since there are multiple card-views.
I tried to register the click event in the ImageView and get the adapterPosition of clicked cardview using getAdapterPosition().
fruitImageView.setOnClickListener(new View.OnClickListener() { /*Hits only when another click event (setImageClickListner) is not registered*/
#Override
public void onClick(View view) {
int a = getAdapterPosition(); //gives the index of clicked cardview position.
}
});
When both of the events are registered then only click on ImageView (fruitImageView.setOnClickListener) gets hit, which gives the position of the adapter. I also want to know which image is clicked.
When I remove this ImageView Click listener, then carousel view click customCarouselView.setImageClickListener gets hit which gives the postion of image clicked but not the adapter position.
Are there any work around for this? Please help.
My Adapter Class complete code:
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private ArrayList<HomeSalesModel> salesLists;
int[] sampleImages = {R.drawable.album1, R.drawable.album2, R.drawable.album3, R.drawable.album4, R.drawable.album5};
String[] sampleTitles = {"A", "B", "C", "D", "E"};
Context myContext = null;
public GridAdapter(ArrayList<HomeSalesModel> salesLists) {
this.salesLists = salesLists;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
myContext = parent.getContext();
return new ViewHolder(LayoutInflater.from(parent.getContext()), parent);
}
#Override
public void onBindViewHolder(GridAdapter.ViewHolder viewHolder, int i) {
viewHolder.tv_country.setText(salesLists.get(i).getTitle());
}
#Override
public int getItemCount() {
return salesLists.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private CarouselView customCarouselView;
private TextView tv_country;
private CardView cardView;
public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.grid_card_row, parent, false));
tv_country = (TextView) itemView.findViewById(R.id.tv_country);
customCarouselView = (CarouselView) itemView.findViewById(R.id.customCarouselView);
cardView = (CardView) itemView.findViewById(R.id.card);
customCarouselView.setPageCount(sampleImages.length);
//customCarouselView.setSlideInterval(4000);
customCarouselView.setViewListener(viewListener);
customCarouselView.setImageClickListener(imageClickListner);
}
ViewListener viewListener = new ViewListener() {
#Override
public View setViewForPosition(int position) {
LayoutInflater li = (LayoutInflater) myContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View customView = li.inflate(R.layout.view_custom, null);
TextView labelTextView = (TextView) customView.findViewById(R.id.labelTextView);
ImageView fruitImageView = (ImageView) customView.findViewById(R.id.fruitImageView);
fruitImageView.setImageResource(sampleImages[position]);
labelTextView.setText(sampleTitles[position]);
fruitImageView.setOnClickListener(new View.OnClickListener() { /*Works when another click event (imageClickListner) is not registered*/
#Override
public void onClick(View view) {
int a = getAdapterPosition(); //gives the index cardview position.
}
});
//labelTextView.setText(salesLists.get(position).getProducts().get(position).getSlug());
return customView;
}
};
ImageClickListener imageClickListner = new ImageClickListener() {
#Override
public void onClick(int position) {
Toast.makeText(myContext, "Clicked item: " + position, Toast.LENGTH_SHORT).show(); //gives index the clicked image.
}
};
}
}
Simply you can get the adapter position inside ImageClickListener.
int postion = getAdapterPosition();
to begin I would thanks those who help noobi dev like me :p! Let's say that I have a Recyclerview in which every unit element has two images. Actually I am able to find the indice of the unit element clicked to fire the good evenment thanks to this little trick in MyAdapter:
#Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.model, parent, false);
final MyHolder holder = new MyHolder(v);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onItemClick(v, holder.getAdapterPosition());
}
});
return holder;
}
Then I start the event this way in the activity:
MyAdapter adapter = new MyAdapter(this, countryCollection.getCountryArray(), new CustomItemClickListener() {
#Override
public void onItemClick(View v, int position) {
//Launch the events
ArrayList<String> languageCodeArray = countryCollection.getLanguageCodeArray();
ArrayList<String> countryIdArray = countryCollection.getCountryIdArray();
if(position >0){
String languageCode = languageCodeArray.get(position-1);
String countryId = countryIdArray.get(position-1);
Intent intent = new Intent(context, LetsSpeakActivity.class);
intent.putExtra("country_id", countryId);
intent.putExtra("language_code", languageCode);
startActivity(intent);
}
}
});
rv.setAdapter(adapter);
But how can I différentiate the two images of my RecyclerView in order to fire the good event? I've been searching for a while how to differentiate a click on the left or right side of the screen but it is obviously not the right way to do ! Any help would be lovely, I am really stuck...
Supposing you have defined both ImageView in your MyHolder class, you could set OnClickListener on both of them in your holder constructor :
public class MyHolder extends RecyclerView.ViewHolder {
public ImageView imageLeft;
public ImageView imageRight;
public CustomItemClickListener listenerLeft;
public CustomItemClickListener listenerRight;
public MyHolder(View v, CustomItemClickListener listenerLeft, CustomItemClickListener listenerRight) {
super(v);
this.listenerLeft = listenerLeft;
this.listenerRight = listenerRight;
imageLeft = v.findViewById(R.id.image_left);
imageRight = v.findViewById(R.id.image_right);
imageLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listenerLeft.onItemClick(v, getPosition());
}
});
imageRight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listenerRight.onItemClick(v, getPosition());
}
});
}
}
while injecting both listener in onCreateViewHolder :
#Override
public MyAdapter.MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.model, parent, false);
return new MyHolder(v, mImageLeftListener, mImageRightListener);
}
You can introduce both of these listener in your Adapter constructor like you did with listener
Or if you want to pass only one listener, you can define your CustomItemClickListener interface with 2 methods (or just 1 with an additional param to differentiate the image clicked) :
public interface CustomItemClickListener {
void onLeftImageClick(View view, int position);
void onRightImageClick(View view, int position);
}