Getting position of View in onCreateViewHolder - java

I am using a RecyclerView with a single row layout with an ImageView and a TextView.
I want to implement a OnClickListener for the View and not for seperate ViewHolder objects. How can i get the position of the view in the Adapter?
Right now i'm deleting comments on click, but i cannot select the clicked View. I added a TODO in the appropriate line.
public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.ViewHolder> {
/** List of Comment objects */
private List<Comment> mCommentList;
/** Database with Comment objects */
private CommentsDataSource mDataSource;
/**
* Construcutor for CommentAdapter
*
* #param commentList List of Comment objects
* #param dataSource Database with Comment objects
*/
public CommentAdapter(List<Comment> commentList, CommentsDataSource dataSource) {
this.mCommentList = commentList;
this.mDataSource = dataSource;
}
/**
* Add Comment objects to RecyclerView
*
* #param position The position where the Comment object is added
* #param comment Comment Object
*/
public void add(int position, Comment comment) {
mCommentList.add(position, comment);
notifyItemInserted(position);
}
/**
* Remove Comment objects from RecyclerView
*
* #param comment Comment Object
*/
public void remove(Comment comment) {
int position = mCommentList.indexOf(comment);
// Avoid double tap remove
if (position != -1) {
mCommentList.remove(position);
mDataSource.deleteComment(comment);
notifyItemRemoved(position);
}
}
#Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_line_row, parent, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO get position
remove(mCommentList.get(getItemCount() - 1));
}
});
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Comment comment = mCommentList.get(position);
holder.comment.setText(comment.getComment());
}
#Override
public int getItemCount() {
return mCommentList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
/** ImageView icon */
public ImageView icon;
/** TextView comment */
public TextView comment;
/**
* Constructor for ViewHolder
*
* #param itemView Layout for each row of RecyclerView
*/
public ViewHolder(final View itemView) {
super(itemView);
icon = (ImageView) itemView.findViewById(R.id.icon);
comment = (TextView) itemView.findViewById(R.id.comment);
}
}
}

You cannot use the position parameter of onBindViewHolder in a callback.
If a new item is added above, RecyclerView will not rebind your item so the position is obsolete.
Instead, RecyclerView provides a getAdapterPosition method on the ViewHolder.
#Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_line_row, parent, false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final int position = holder.getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
remove(mCommentList.get(position));
}
}
});
return holder;
}
I've added position != RecyclerView.NO_POSITION check because when item is removed, RecyclerView will fade out the View so it may still be clicked by the user but its adapter position will return NO_POSITION.

you can create a method to update position in your class.
in my case I need to attach watcher and get the position to update arraylist. here is the example:
class DodolWatcher bla bla {
private var position: Int = 0
fun updatePosition(pos:Int)
{
position = pos
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
Log.d("test", position.toString())
}
}
and in your onCreateViewHolder you can attach watcher to edittext
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVPaymentMethodAdapter.ViewHolder {
....
bla bla
....
theWatcher = DodolWatcher() <-- this is the trick
amount.addTextChangedListener(theWatcher)
}
and you will able to update position in your bindViewHolder like this:
override fun onBindViewHolder(viewHolder: RVPaymentMethodAdapter.ViewHolder, position: Int) {
theWatcher.updatePosition(viewHolder.adapterPosition) <-- this is the trick
}

override getItemViewType method in your Adapter:
#Override
public int getItemViewType(int position) {
//...
return position;
}
Now viewType is position
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int position=viewType; //position
//your code
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recursive, parent, false);
return new ViewHolder(view);
}
for example
public class BrandBtnAdapter extends RecyclerView.Adapter<BrandBtnAdapter.MyViewHolder>
{
//............
#Override
public int getItemViewType(int position)
{
//...
return position;
}
#Override
public BrandBtnAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
int position = viewType; //position
final View itemView = mInflater.inflate(mResource, parent, false);
return new BrandBtnAdapter.MyViewHolder(itemView);
}
}

Related

How to return string from interface to setonclicklistener

I'm trying to learn android development and i was learning about the recyclerView. I want to send a string via an interface when a list item is clicked but I dont know how to do that.Can anyone please tell me what to do to achieve this.
This is my java code of the custom recyclerview adapter that I've created.
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] localDataSet;
private newsOnCkicked k;
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder).
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
textView = (TextView) view.findViewById(R.id.textView2);
}
public TextView getTextView() {
return textView;
}
}
/**
* Initialize the dataset of the Adapter.
*
* #param dataSet String[] containing the data to populate views to be used
* by RecyclerView.
*/
public CustomAdapter(String[] dataSet,newsOnCkicked k) {
localDataSet = dataSet;
this.k=k;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list item
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.mylayout, viewGroup, false);
RecyclerView.ViewHolder v=new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
k.onItemClicked(localDataSet[v.getAdapterPosition()]);
}
});
return new ViewHolder(view);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.getTextView().setText(localDataSet[viewHolder.getAdapterPosition()]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return localDataSet.length;
}
}
interface newsOnCkicked extends View.OnClickListener {
String onItemClicked(String s);
}
Kindly help me to figure this out!!
This code seems fine except for these parts i noted you had to do
1)Extending onclick listener
2)The function and position of your interface.
First place your interface in a separate file or inside the adapter class,remove the extends and replace the string to return void:
interface newsOnCkicked {
void onItemClicked(String s);
}
Then call it like this from your activity
new RecyclerView(this)/* <- your recyclerview*/.setAdapter(new CustomAdapter(new String[20]/*your array*/, new CustomAdapter.newsOnCkicked() {
#Override
public void onItemClicked(String s) {
//here s is your clicked string
}
}));
The code should look more or less like below:
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] localDataSet;
private newsOnCkicked k;
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder).
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
textView = (TextView) view.findViewById(R.id.textView2);
}
public TextView getTextView() {
return textView;
}
}
/**
* Initialize the dataset of the Adapter.
*
* #param dataSet String[] containing the data to populate views to be used
* by RecyclerView.
*/
public CustomAdapter(String[] dataSet,newsOnCkicked k) {
localDataSet = dataSet;
this.k=k;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list item
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.mylayout, viewGroup, false);
RecyclerView.ViewHolder v=new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
k.onItemClicked(localDataSet[v.getAdapterPosition()]);
}
});
return new ViewHolder(view);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.getTextView().setText(localDataSet[viewHolder.getAdapterPosition()]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return localDataSet.length;
}
interface newsOnCkicked {
void onItemClicked(String s);
}
}

How to build a custom horizontal recycler view like Play Store in Android?

I already know how to make horizontal RecyclerView, but I want to make a custom recycler view having a blank space at the starting, and when we start to scroll the background image gets faded.
just make your adapter pretend to be having more data than it actually has.
when it gives you position <n (n : number of items you want empty at the beginning) don't load any image into and leave it empty.
when it asks about the data size in getItemCount() return your data size + n.
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
...
private final int N =2 ;
#Override
public PageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {...}
#Override
public void onBindViewHolder(#NonNull PageViewHolder holder, int position) {
if(position <N)return ;
else {
// bind your holder with data
}
}
#Override
public int getItemCount() {
return pageEntities.size() +N;
}
}
you might also make use of the view Type value by overriding method getItemViewType and return value that indicates if you want this view holder to be empty or to have another behavior than the regular view holder.
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
...
private final int N =2 ;
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v ;
if(viewType ==0){
v = LayoutInflater.from(context).inflate(R.layout.view_holder_item_1, parent, false);
}else {
v = LayoutInflater.from(context).inflate(R.layout.view_holder_item_2, parent, false);
}
return new new MyViewHolder(v.getRootView()) ;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
if(position <N){
// bind your holder view of type 1 with data you want
}
else {
// bind your holder view of type 2 with data you want
}
}
#Override
public int getItemCount() {
return pageEntities.size() +N;
}
#Override
public int getItemViewType(int position) {
return position <N ?0:1 ;
}
}
where your type 1 in this case would be the empty view holder.

Add static default item in RecyclerView

I have a horizontal RecyclerView that displays 7 images from the users device (changing as user keeps taking pictures). I would like to add a default icon at the end of the 7 images, with an onclick capability. How can I go about doing this?
Something like this:
In the image above the plus icon should be the default icon, however it should be at the end of the images.
UPDATED:
RecentPhotosAdapter.java
public class RecentPhotosAdapter extends RecyclerView.Adapter<RecentPhotosAdapter.ViewHolder> {
// Variables
private Context mContext;
private ArrayList<String> mImage;
private int VIEW_TYPE_DEFAULT = 0;
private int VIEW_TYPE_IMAGE = 1;
// Limit the recent photo selection
int RECENT_PHOTO_LIMIT = 7;
public RecentPhotosAdapter(Context mContext, ArrayList<String> mImage) {
this.mContext = mContext;
this.mImage = mImage;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_IMAGE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recent_photos_item, parent, false);
return new ViewHolder(view);
}
else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.default_select_photos_item, parent, false);
return new ViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if(position < Math.min(mImage.size(), RECENT_PHOTO_LIMIT)){
String images = mImage.get(position);
// Recent Photos
Glide.with(mContext)
.load(images)
.placeholder(R.drawable.background_gallery_placeholder)
.transform(new CenterCrop(), new RoundedCorners(30))
.into(holder.recent_photos_iv);
// Default select photos
holder.recent_photos_iv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do something here
// Take user to image select slider
}
});
}
else {
//code for default view
}
}
#Override
public int getItemViewType(int position) {
return (position == mImage.size()) ? VIEW_TYPE_DEFAULT : VIEW_TYPE_IMAGE;
}
#Override
public int getItemCount() {
return Math.min(mImage.size(), RECENT_PHOTO_LIMIT) + 1;
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView recent_photos_iv;
ImageView default_select_photo_iv;
public ViewHolder(View itemView) {
super(itemView);
recent_photos_iv = itemView.findViewById(R.id.recent_photos_iv);
default_select_photo_iv = itemView.findViewById(R.id.default_select_photo_iv);
}
}
}
You can achieve this by making the default icon as a "ViewType" of your adapter.
Override getItemViewType to return a different value for your last item.
#Override
public int getItemViewType(int position) {
return (position == mData.size()) ? VIEW_TYPE_DEFAULT : VIEW_TYPE_IMAGE;
}
Now in the onCreateViewHolder, handle both the view Types.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_IMAGE) {
//Create viewholder for your images
}
else {
//Create viewholder for default view
}
}
Add 1 to your ItemList size to accomodate this extra view:
#Override
public int getItemCount() {
return mData.size() + 1;
}
Your Updated code:
public class RecentPhotosAdapter extends RecyclerView.Adapter<RecentPhotosAdapter.ViewHolder> {
// Variables
private Context mContext;
private ArrayList<String> mImage;
private int VIEW_TYPE_DEFAULT=0;
private int VIEW_TYPE_IMAGE=1;
public RecentPhotosAdapter(Context mContext, ArrayList<String> mImage) {
this.mContext = mContext;
this.mImage = mImage;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_IMAGE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recent_photos_item, parent, false);
return new ViewHolder(view);
}
else {
//Create viewholder for default view
}
}
#Override
public int getItemViewType(int position) {
return (position == Math.min(mImage.size(), RECENT_PHOTO_LIMIT)) ?VIEW_TYPE_DEFAULT : VIEW_TYPE_IMAGE;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if(position < Math.min(mImage.size(), RECENT_PHOTO_LIMIT)){
String images = mImage.get(position);
Glide.with(mContext)
.load(images)
.placeholder(R.drawable.background_gallery_placeholder)
.transform(new CenterCrop(), new RoundedCorners(30))
.into(holder.recent_photos_iv);
holder.recent_photos_iv.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do something here
// Take user to image select slider
}
});
}
else {
//code for default view
}
}
#Override
public int getItemCount() {
// Limit the recent photo selection
int RECENT_PHOTO_LIMIT = 7;
return Math.min(mImage.size(), RECENT_PHOTO_LIMIT)+1;
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView recent_photos_iv;
public ViewHolder(View itemView) {
super(itemView);
recent_photos_iv = itemView.findViewById(R.id.recent_photos_iv);
}
}
}

setAdapter in recyclerView cannot be applied

So I followed an online tutorial to have a sort of gallery app. The pictures are shown inside a GridView but I needed to change it into a recyclerview. When I do that though I get that error and my adapter doesn't seem to work with the recyclerview. Here is my adapter code:
class AlbumAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<HashMap< String, String >> data;
public AlbumAdapter(Activity a, ArrayList < HashMap < String, String >> d) {
activity = a;
data = d;
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
AlbumViewHolder holder = null;
if (convertView == null) {
holder = new AlbumViewHolder();
convertView = LayoutInflater.from(activity).inflate(
R.layout.album_row, parent, false);
holder.galleryImage = (ImageView) convertView.findViewById(R.id.galleryImage);
holder.gallery_count = (TextView) convertView.findViewById(R.id.gallery_count);
holder.gallery_title = (TextView) convertView.findViewById(R.id.gallery_title);
convertView.setTag(holder);
} else {
holder = (AlbumViewHolder) convertView.getTag();
}
holder.galleryImage.setId(position);
holder.gallery_count.setId(position);
holder.gallery_title.setId(position);
HashMap < String, String > song = new HashMap < String, String > ();
song = data.get(position);
try {
holder.gallery_title.setText(song.get(Function.KEY_ALBUM));
holder.gallery_count.setText(song.get(Function.KEY_COUNT));
Glide.with(activity)
.load(new File(song.get(Function.KEY_PATH))) // Uri of the picture
.into(holder.galleryImage);
} catch (Exception e) {}
return convertView;
}
}
class AlbumViewHolder {
ImageView galleryImage;
TextView gallery_count, gallery_title;
}
And this is some of the code from the activity in which I set my adapter:
#Override
protected void onPostExecute(String xml) {
AlbumAdapter adapter = new AlbumAdapter(ProfileActivity.this, albumList);
galleryGridView.setAdapter(adapter);
galleryGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Intent intent = new Intent(ProfileActivity.this, AlbumActivity.class);
intent.putExtra("name", albumList.get(+position).get(Function.KEY_ALBUM));
startActivity(intent);
}
});
}
This is the error I'm currently getting:
Error message
I basically want to fix the adapter so that it works inside a recyclerview.
You have to create RecyclerView.Adapter to work with RecyclerView. It's bit different that BaseAdapter. Let me give you example of how to implement this.
public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder> {
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.album_row, viewGroup, false);
return new AlbumAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
holder.galleryImage.setId(position);
holder.gallery_count.setId(position);
holder.gallery_title.setId(position);
}
#Override
public int getItemCount() {
return data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView galleryImage;
TextView gallery_count, gallery_title;
public ViewHolder(View v) {
galleryImage = v.findViewById(R.id.galleryImage);
gallery_count =v.findViewById(R.id.gallery_count);
gallery_title = v.findViewById(R.id.gallery_title);
}
}
}

RecyclerView ClickListener Not Recognizing Touch

I have created a RecyclerView, and in the Adapter I am hoping to create a ClickListener. I want each item in the RecyclerView to send a unique response after click. In other words, a click is loading a different fragment for each specific RecyclerView item. Right now, the RecyclerView is not responding, as I have logged it and even added a Toast without any luck:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<LatestPoll> mDataSet;
int lastPosition = -1;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// each data item is just a string in this case
protected TextView pollQuestion;
protected ImageView pollImage;
protected RelativeLayout individualItem;
public ViewHolder(View v) {
super(v);
pollQuestion = (TextView) v.findViewById(R.id.latest_item_question);
pollImage = (ImageView) v.findViewById(R.id.pollThumbNailImage);
individualItem = (RelativeLayout) v.findViewById(R.id.individual_item);
individualItem.setOnClickListener(this);
}
#Override
public void onClick(View v) {
v.getId();
int itemPosition = getLayoutPosition();
String item = String.valueOf(mDataSet.get(itemPosition));
Toast.makeText(getActivity().getApplicationContext(), item, Toast.LENGTH_LONG).show();
Log.v("TESTING", "ITEM CLICKED!");
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<LatestPoll> myDataset) {
mDataSet = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.latest_item, parent, false);
// set the view's size, margins, paddings and layout parameters
return new ViewHolder(v);
}
// Replace the contents of a view (invoked by the layout manager)
//The OutOfBoundsException is pointing here
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.v("ON_BIND", "ON_BINDVIEWHOLDER CALLED");
LatestPoll latestPoll = mDataSet.get(position);
holder.pollQuestion.setText(latestPoll.getQuestion());
Picasso.with(getActivity())
.load(latestPoll.getPollImage())
.fit()
.placeholder(R.drawable.loading_spinnter_white)
.into(holder.pollImage);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataSet.size();
}
}
private void onLoad() {
if (mFragmentListener != null) {
mFragmentListener.onFragmentLoaded();
}
}
public interface FragmentListener {
void onFragmentLoaded();
}
}
You need to set onclick listener to the parent layout inside the onBindViewHolder .
holder.parentID.setOnClickListener(mRecyclerViewListener);
In your View holder
parentID = iteView.findViewById(R.id.parent);

Categories