Recycler Adapter callback nullpointer - java

i have recycler adapter like this
public class ShopUsersAdapter extends RecyclerView.Adapter<ShopUsersAdapter.MyViewHolder> implements View.OnClickListener {
private List<ShopUsersRecyclerModel> user_list;
private Context context;
private AdminCheckLocActivity activity;
private AdapterCallback mAdapterCallback;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView user;
public TextView type;
private ImageButton btn_loc;
public MyViewHolder(View view) {
super(view);
user = (TextView) view.findViewById(R.id.txt_text);
type =(TextView)view.findViewById(R.id.txt_count);
btn_loc=(ImageButton)view.findViewById(R.id.btn_loc);
}
}
#Override
public ShopUsersAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.map_pop_up_item, parent, false);
context=parent.getContext();
itemView.setOnClickListener(this);
return new ShopUsersAdapter.MyViewHolder(itemView);
}
#Override
public void onClick(View v) {
}
public ShopUsersAdapter(List<ShopUsersRecyclerModel> user_list, AdminCheckLocActivity activity) {
this.user_list = user_list;
this.activity=activity;
this.mAdapterCallback = ((AdapterCallback) context);
}
#Override
public void onBindViewHolder(final ShopUsersAdapter.MyViewHolder holder, int position) {
final ShopUsersRecyclerModel user = user_list.get(position);
holder.user.setText(user.getUser_info());
if (user.getUser_type()==3){holder.type.setText("TV Pro.");}
if (user.getUser_type()==4){holder.type.setText("B. Esya Pro.");}
holder.btn_loc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mAdapterCallback.onMethodCallback(user.getUser_info());
}
});
}
#Override
public int getItemCount() {
return user_list.size();
}
public interface AdapterCallback {
void onMethodCallback(String userName);
}
}
but i get error
NullPointer at
mAdapterCallback.onMethodCallback(user.getUser_info());
my activity code;
#Override
public void onMethodCallback(final String userName) {
Toast.makeText(this,userName,Toast.LENGTH_LONG).show();
}
and i implement this like ;
implements ShopUsersAdapter.AdapterCallback
userInfo data is not null, i check this. but i need this callback all of my recyclers please help me.

I suggest changing your adapter's constructor to directly receive a callback:
public ShopUsersAdapter(List<ShopUsersRecyclerModel> user_list, AdminCheckLocActivity activity, AdapterCallback callback) {
this.user_list = user_list;
this.activity=activity;
this.mAdapterCallback = callback;
}
You can also make sure your callback isn't null when you're utilizing it:
holder.btn_loc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This should be optional for your case
// but it can be useful if you're ever in a situation
// where the callback isn't always available
if(mAdapterCallback != null){
mAdapterCallback.onMethodCallback(user.getUser_info());
}
}
});
And within your Activity, make sure to pass in the callback as an argument when you create your Adapter instance:
// Use 'this' since your Activity implements the callback
ShopUsersAdapter adapter = new ShopUsersAdapter(myList, myActivity, this);

Related

How to call an Interstitial by clicking on an item in the list that goes through an Adapter?

I need to call an Interstitial in this Activity by clicking on an item in the list that comes from my Adapter.
public class SoundRecyclerAdapter extends RecyclerView.Adapter<SoundRecyclerAdapter.SoundViewHolder> {
private final ArrayList<SoundObject> soundObjects;
SoundRecyclerAdapter(ArrayList<SoundObject> soundObjects) {
this.soundObjects = soundObjects;
}
#NonNull
#Override
public SoundViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.sound_item, parent, false);
return new SoundViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull SoundViewHolder holder, int position) {
final SoundObject object = soundObjects.get(position);
final Integer soundID = object.getItemID();
holder.itemTextView.setText(object.getItemName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// I tried calling the interstitial here, but to no avail
EventHandlerClass.startMediaPlayer(v, soundID);
}
});
// Handle actions when the user presses a sound button
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
EventHandlerClass.popupManager(v, object);
return true;
}
});
}
#Override
public int getItemCount() {
return soundObjects.size();
}
class SoundViewHolder extends RecyclerView.ViewHolder {
final TextView itemTextView;
SoundViewHolder(View itemView) {
super(itemView);
itemTextView = itemView.findViewById(R.id.textViewItem);
}
}
}
Pass context as parameter of adapter's constructor:
private Context context;
SoundRecyclerAdapter(Context context, ArrayList<SoundObject> soundObjects) {
this.soundObjects = soundObjects;
this.context = context;
}
and use it in click handler
Use this code in the setOnClickListener() method:
mInterstitialAd = new InterstitialAd(SoundRecyclerAdapter.this);
Then add the adrequests and load the ad.

Updating TextView from another Activity

I have an Adapter (A ViewHolder) Class, and when a Button is pressed I'd like to update a TextView of the Home() activity, preferrably without launching the activity immediatly.
The code in the Adapter:
public class GameViewAdapter extends RecyclerView.Adapter<GameViewAdapter.GameViewHolder> {
public static Object OnItemClickListener;
private ArrayList<GameItem> mGameList;
private OnItemClickListener mListener;
public static MyCallback callback;
public GameViewAdapter(ArrayList<GameItem> mGameList, GameViewAdapter.OnItemClickListener mListener, MyCallback callback) {
this.mGameList = mGameList;
this.mListener = mListener;
this.callback = callback;
}
#Override
public GameViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
View v = LayoutInflater.from(context).inflate(R.layout.game_entry, viewGroup, false);
GameViewHolder GVH = new GameViewHolder(v, mListener);
return GVH;
}
#Override
public void onBindViewHolder(#NonNull GameViewHolder gameViewHolder, int position) {
gameViewHolder.bind(mGameList.get(position));
}
#Override
public int getItemCount() {
return mGameList.size();
}
class GameViewHolder extends RecyclerView.ViewHolder {
private ImageView itemCover;
private TextView itemTitle;
private TextView itemDescription;
private PopupWindow popupWindow;
private ImageView popUpImage;
private TextView PopUpTitle;
private EditText customAmount;
private Button add;
private Button addcustom;
private Button exit;
public GameViewHolder(View itemView, GameViewAdapter.OnItemClickListener mListener) {
super(itemView);
setupViews(itemView);
}
public void bind(final GameItem gameItem) {
Glide.with(this.itemCover).load(gameItem.getCover()).into(this.itemCover);
this.itemTitle.setText(gameItem.getTitle());
this.itemDescription.setText(gameItem.getDescription());
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showPopUp(itemView, gameItem);
}
});
}
private void setupViews(View itemView) {
addcustom = itemView.findViewById(R.id.addcustom);
popUpImage = itemView.findViewById(R.id.popupimg);
PopUpTitle = itemView.findViewById(R.id.popuptitle);
customAmount = itemView.findViewById(R.id.gameamount);
itemCover = itemView.findViewById(R.id.GameCover);
itemTitle = itemView.findViewById(R.id.GameTitle);
itemDescription = itemView.findViewById(R.id.GameAmount);
exit = itemView.findViewById(R.id.exit);
}
private void showPopUp(final View itemView, final GameItem gameItem) {
//Declaration
final View popupView = LayoutInflater.from(itemView.getContext()).inflate(R.layout.popup, null);
final PopupWindow popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
final ImageView popupItemCover = popupView.findViewById(R.id.popupimg);
final TextView popupItemTitle = popupView.findViewById(R.id.popuptitle);
//Set Data
Glide.with(popupItemCover).load(gameItem.getCover()).into(popupItemCover);
popupItemTitle.setText(gameItem.getTitle());
popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
//ExitButton
exit = popupView.findViewById(R.id.exit);
exit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
add = popupView.findViewById(R.id.addaverage);
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.onDataChanged(Integer.valueOf(gameItem.getTime()));
Toast.makeText(popupView.getContext(), String.valueOf(gameItem.getTime()), Toast.LENGTH_SHORT).show();
}
});
}
}
public interface MyCallback {
void onDataChanged(Integer gameTime);
}
public interface OnItemClickListener {
void onGameClick(int position);
}
}
And I have implemented this in the Main Activity:
public void onDataChanged(int gameTime) {
// it will fire when you call your callback.onDataChanged(); in your adapter
// here can change your textview
GameTime.setText(Integer.valueOf(gameTime));
}
}
I've tried to use BroadcastReceiver as well, with no success.
Thank you!
EDIT:
When adding the callback my App crashes with following traceback:
java.lang.NullPointerException: Attempt to invoke interface method 'void com.reogen.gametime.Adapters.GameViewAdapter$MyCallback.onDataChanged(java.lang.String)' on a null object reference
at com.reogen.gametime.Adapters.GameViewAdapter$GameViewHolder$3.onClick(GameViewAdapter.java:127)
Line 127 is: callback.onDataChanged(Integer.valueOf(gameItem.getTime()));
You can create observable like:
in your Adapter create:
public interface IMyCallback {
void onDataChanged(int gameTime);
}
Global field in Adapter:
private IMyCallback callback
Inside constructor of Adapter
public MyAdapter(... , IMyCallback callback) {
...
this.callback = callback;
}
Now your activity make implements IMyCallback and override method onDataChanged.
And when you create your adapter send as callback YourActivity.this.
finnaly you need to make like:
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.onDataChanged(Integer.valueOf(gameItem.getTime()));
}
after this call you will get update in your override method onDataChanged in your activity class. enjoy
------------ Update -----------
in your activity, when you will implements interface you will get method like
#Override
public void onDataChanged(int gameTime) {
// it will fire when you call your callback.onDataChanged(); in your adapter
// here can change your textview
textview.setText(String.valueOf(gameTime));
}

RecyclerView Item Click Listener the Right Way

I use RecyclerView adapter to display data inside an activity, I want to implement onClickListener inside the activity, currently, I am setting onClickListener inside adapter as usual which works fine.
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
});
}
However I want to implement it inside activity so I have greater control. This doesn't serve my purpose. I think it'll be useful for a lot of us.
You need to check this tutorial here for better understanding on how you can achieve the behaviour that you want.
In case of handling the onClickListener from your activity you need to work based on a callback implementation with an interface. Pass the interface from the activity to your adapter and then call the callback function from your adapter when some items are clicked.
Here's a sample implementation from the tutorial.
Let us first have the interface.
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
You need to modify your adapter to take the listener as the parameter like the one stated below.
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
Now in your onBindViewHolder method, set the click listener.
#Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
...
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
Now setting the adapter in your RecyclerView.
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
#Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
So the whole adapter code looks like the following.
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
#Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
#Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
#Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
Registering clickListener inside onCreateViewHolder instead of onBindViewHolder is more performant since you only add listener when a view is created not ever time recyclerView is scrolled.
And i use ListAdapter with DiffUtil callback instead of RecyclerViewAdapter
abstract class BaseListAdapter<ItemType>(
callBack: DiffUtil.ItemCallback<ItemType> = DefaultItemDiffCallback(),
private inline val onItemClicked: ((ItemType, Int) -> Unit)? = null
) : ListAdapter<ItemType, BaseItemViewHolder>(
AsyncDifferConfig.Builder<ItemType>(callBack)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseItemViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
).apply {
onViewHolderCreated(this, viewType, binding)
}
}
fun createCustomViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
)
}
override fun onBindViewHolder(
holder: BaseItemViewHolder,
position: Int,
payloads: MutableList<Any>
) {
val item: ItemType? = currentList.getOrNull(position)
item?.let {
holder.binding.setVariable(BR.item, item)
onViewHolderBound(holder.binding, item, position, payloads)
holder.binding.executePendingBindings()
}
}
override fun onBindViewHolder(holder: BaseItemViewHolder, position: Int) {
}
/**
* get layout res based on view type
*/
protected abstract fun getLayoutRes(viewType: Int): Int
/**
* Called when a ViewHolder is created. ViewHolder is either created first time or
* when data is refreshed.
*
* This method is not called when RecyclerView is being scrolled
*/
open fun onViewHolderCreated(
viewHolder: RecyclerView.ViewHolder,
viewType: Int,
binding: ViewDataBinding
) {
binding.root.setOnClickListener {
onItemClicked?.invoke(getItem(viewHolder.bindingAdapterPosition), viewHolder.bindingAdapterPosition)
}
}
/**
* bind view while RecyclerView is being scrolled and new items are bound
*/
open fun onViewHolderBound(
binding: ViewDataBinding,
item: ItemType,
position: Int,
payloads: MutableList<Any>
) {
}
}
open class BaseItemViewHolder(
val binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root)
class DefaultItemDiffCallback<ItemType> : DiffUtil.ItemCallback<ItemType>() {
override fun areItemsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}
Another better user experience is using onBindViewHolder with payLoad which lets you only update some part of the rows instead of whole row. For instance you have image, title and body in rows, and only body changes frequently, without payload image flashes and provides bad user experience. But with payload you can decide which part of the row should be updated allowing you not to reload parts that were not updated.
very simple and clean solution is:
create a class with the name of RecyclerTouchListener:
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
in your recyclerview activity:
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
speech(countries_list_code[position]);
}
#Override
public void onLongClick(View view, int position) {
}
}));
I found super duper easy method! I recommend this one
Example Code:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
#Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
#Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
#Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
And Use RecyclerView Adapter using below code:
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
#Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
i found this from here
Hope it helped you.
In my way, I just created a single instance of ClickListener, And it dispatches click event to both RecyclerView and Activity or Fragment:
class LeagueAdapter(
onLeagueSelected: (League, Int, View) -> Unit
) : RecyclerView.Adapter<LeagueHolder>() {
private val dataSet = arrayListOf<League>()
private val clickListener = View.OnClickListener { view ->
val adapterPosition = view.tag as Int
onLeagueSelected(dataSet[adapterPosition], adapterPosition, view)
// perform adapter related action here ...
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: LeagueHolder, position: Int) {
// put item position in tag field
holder.itemView.tag = position
holder.itemView.setOnClickListener(clickListener)
}
}
And inside Activity, we have something like this:
private val headerAdapter = LeagueAdapter { league, i, view ->
Log.e(TAG, "item clicked $i")
}
Create an interface for the adapter class
private OnItemClickListener mListener;
public CustomAdapter(List<Listdata> listdata, OnItemClickListener listener) {
mListener = listener;
...
...
}
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View view) {
...
...
view.setOnClickLister(this);
}
#override
public void onClick(View v) {
mListener.onAdapterItemClick(getAdapterPosition())
}
}
interface OnItemClickListener {
void onAdapterItemClick(int position);
}
Let the activity implement the interface
public class CustomListActivity extends AppCompatActivity implements OnItemClickListener {
...
...
#override
public void onAdapterItemClick(int position) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
There is another way of doing this, check out this implementation
You can let your Activity implements View.OnClickListener and pass it to adapter. Below is an example.
class RAdapter extends RecyclerView.Adapter<>{
View.OnClickListener listner;
public RAdapter(View.OnClickListener listner) {
this.listner = listner;
}
public void onBindViewHolder(MyHolder holder, final int position) {
holder.vname.setOnClickListener(listner);
}
}
But to handle click in Activity you will going to need clicked position. You can have it with adapter.getAdapterPosition() to validate which item is clicked.
Apart from that To pass click event to the Fragment/Activity you can use a Custom callback listener this way your Adapter will be reusable .
A better way to handle clicks in ViewHolder. See the below example.
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
Button button;
public Holder(View itemView) {
super(itemView);
button=itemView.findViewById(R.id.b1);
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.b1){
int position=getAdapterPosition();
// Call the call method here
// with position or data Object itself
}
}
}
If I understood correctly you want to set the on click logic in the Activity.
You can do this by setting the OnClickListener in the Activity and passing it in the Adapter constructor.
MyAdapter myAdapter = new MyAdapter(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
}));
And your MyAdapter Constructor would be:
final private OnClickListener onClickListener;
public MyAdapter(OnClickListener onClickListener) {
this.OnClickListener = OnClickListener;
}
So your new code would be something like this
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(onClickListener);
}
RecyclerView widget only has 2 useful listeners for this scenario:
RecyclerView.OnChildAttachStateChangeListener - covered here
RecyclerView.OnItemTouchListener - the one that I will be covering
the code is inspired by TouchEvents sample related to Accessibility, and works in Activity/Fragment without setting any listeners in the Adapter
recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
var downTouch = false
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> downTouch = true
MotionEvent.ACTION_UP -> if (downTouch) {
downTouch = false
recyclerView.findChildViewUnder(e.x, e.y)?.let {
val position = rv.getChildAdapterPosition(it)
Toast.makeText(rv.context, "clicked on $position", Toast.LENGTH_SHORT)
.show()
}
}
else -> downTouch = false
}
return super.onInterceptTouchEvent(rv, e)
}
})
There's another very simple way documented in CodePath.
ItemClickSupport.addTo(recyclerView).setOnItemClickListener(
new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do stuff
}
}
);
The implementation of ItemClickSupport.
Personally, I like to handle this via RxJava subjects:
A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by re-emitting them, and it can also emit new items.
For more info read Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject.
in Adapter:
public static PublishSubject<MyData> onClickSubject = PublishSubject.create();
ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
.
.
.
#Override
public void onClick(View view) {
onClickSubject.onNext(getItem(getAdapterPosition()));
}
}
Add your disposables to a CompositeDisposable and dispose them in onDestroy():
private CompositeDisposable compositeDisposable = new CompositeDisposable();
in onCreate():
compositeDisposable.add(MyAdapter.onClickSubject.subscribe(myData -> {
//do something here
}));
in onDestroy():
compositeDisposable.dispose();
Note:
1. getItem() is a method of androidx.recyclerview.widget.ListAdapter and androidx.paging.PagedListAdapter if you are extending RecyclerView.Adapter you can get item from your data list by position.
2. to use Disposables you need RxJava2 or above
Kotlin
I'd better to add item click in onCreateViewHolder like this
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int):
ProductViewHolder {
val view: View = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.layout_product_item, viewGroup, false)
return ProductViewHolder(view).also { viewHolder ->
viewHolder.itemView.setOnClickListener {
val position = viewHolder.layoutPosition
if (position != RecyclerView.NO_POSITION) {
// do what you want with data[position]
}
}
}
}
You can implement the View.OnClickListener interface in your RecyclerView.ViewHolder class and call it from there.
In your Adapter class create a public interface.
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(int position);
}
private OnItemClickListener onItemClickListener;
On your ViewHolder class, you can implement the View.OnClickListener interface and set an onclick listener to the itemView.
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView myText;
public WalletViewHolder(#NonNull View itemView) {
super(itemView);
myText= itemView.findViewById(R.id.my_text_view);
// Set click listener for each item view
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(getAdapterPosition());
}
}
}
Then your OnItemClickListener will be created only once.
I always have one Generic Adapter in my project to avoid make a Adapter class every I use a Recyclerview. Here some example
public class AdapterRecyclerviewTextOnly extends RecyclerView.Adapter<AdapterRecyclerviewTextOnly.ViewHolder> {
private RecyclerView recyclerView;
private OnRecyclerviewListener onRecyclerviewListener;
public interface OnRecyclerviewListener {
void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position);
void onRecyclerviewClick(RecyclerView recyclerView, int position);
int onItemCount(RecyclerView recyclerView);
}
public void setOnRecyclerviewListener(OnRecyclerviewListener listener) { this.onRecyclerviewListener = listener; }
public AdapterRecyclerviewTextOnly(RecyclerView recyclerView) {
super();
this.recyclerView = recyclerView;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
RecyclerView recyclerView;
public TextView textView;
ViewHolder(RecyclerView recyclerView, View itemView) {
super(itemView);
this.recyclerView = recyclerView;
this.itemView.setOnClickListener(this);
this.textView = itemView.findViewById(R.id.textview_title);
}
void onBind(int position) { onRecyclerviewListener.onRecyclerviewBind(this.recyclerView, this, position); }
#Override
public void onClick(View v) {
onRecyclerviewListener.onRecyclerviewClick(this.recyclerView, getAdapterPosition());
}
}
#Override
public AdapterRecyclerviewTextOnly.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflatedView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_text_only, parent, false);
return new ViewHolder(this.recyclerView, inflatedView);
}
#Override
public void onBindViewHolder(AdapterRecyclerviewTextOnly.ViewHolder holder, int position) {
holder.onBind(position);
}
#Override
public int getItemCount() {
return onRecyclerviewListener.onItemCount(this.recyclerView);
}
}
And then in your Activity Class, you can use this adapter with :
this.recyclerView = findViewById(R.id.recyclerview);
this.recyclerView.setHasFixedSize(true);
this.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
AdapterRecyclerviewTextOnly recyclerViewAdapter = new AdapterRecyclerviewTextOnly(this.recyclerView);
this.recyclerView.setAdapter(this.recyclerViewAdapter);
this.recyclerViewAdapter.setOnRecyclerviewListener(new AdapterRecyclerviewTextOnly.OnRecyclerviewListener() {
#Override
public void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position) {
}
#Override
public void onRecyclerviewClick(RecyclerView recyclerView, int position) {
}
#Override
public int onItemCount(RecyclerView recyclerView) {
}
});
You can reuse this with 2 or 3 recyclerview too.
First, declare a globar listener private AdapterRecyclerviewTextOnly.OnRecyclerviewListener listener;.
Then init the listener with new object then set the your every recyclerview with the listener. Use specific identifier:
if (recyclerView == recyclerViewA){ } else if (recyclerView == recyclerViewB) { } to manage your recyclerview inside the adapter.

How pass data from RecyclerView to activity

I need to pass data from recyclerView adapter to main activity on click on image of recyclerview. Can someone help?
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder> {
private ArrayList<Video> mData;
private ArrayList<Video> mData2;
private Activity mACtivity;
public VideoAdapter(ArrayList<Video> data, ArrayList<Video> data2, Activity activity) {
this.mData = data;
this.mData2 = data2;
this.mACtivity = activity;
}
#Override
public VideoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.anteprima_list_item, parent, false);
//view.setOnClickListener(mOnClickListener);
return new VideoHolder(view);
}
#Override
public void onBindViewHolder(VideoHolder holder, int position) {
Video video = mData.get(position);
final Video video2 = mData2.get(position);
holder.setTitolo(video.getTitolo());
holder.setSottoTitolo(video.getSottotitolo());
holder.setData(video.getData());
holder.setData(video.getData());
/* holder.setAddress(restaurant.getAddress());
holder.setCost("Average cost for 2: " + restaurant.getCurrency() + restaurant.getCost());
holder.setRating(restaurant.getRating());*/
Glide.with(mACtivity)
.load(video2.getPic())
.into(holder.restaurantImageView);
holder.restaurantImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// handle click event here
System.out.println("PIC"+video2.getPic());
}
});
}
#Override
public int getItemCount() {
if (mData == null)
return 0;
return mData.size();
}
public class VideoHolder extends RecyclerView.ViewHolder {
ImageView restaurantImageView;
TextView restaurantNameTextView;
TextView restaurantAddressTextView;
TextView restaurantRatingTextView;
TextView costTextView;
TextView distanceTextView;
LinearLayout linearlayout;
public VideoHolder(View itemView) {
super(itemView);
linearlayout=(LinearLayout) itemView.findViewById((R.id.linearlayout));
restaurantImageView = (ImageView) itemView.findViewById(R.id.imageview_restaurant);
restaurantNameTextView = (TextView) itemView.findViewById(R.id.textview_restaurant_name);
restaurantAddressTextView = (TextView) itemView.findViewById(R.id.restaurant_address_textview);
distanceTextView = (TextView) itemView.findViewById(R.id.restaurant_distance_textview);
/* costTextView = (TextView) itemView.findViewById(R.id.cost_for_two_textview);
restaurantRatingTextView = (TextView) itemView.findViewById(R.id.rating);*/
}
public void setTitolo(String titolo) {
restaurantNameTextView.setText(titolo);
}
public void setSottoTitolo(String sottotitolo) {
restaurantAddressTextView.setText(sottotitolo);
}
public void setData(String data) {
distanceTextView.setText(data);
}
/* public void setPic(String pic) {
distanceTextView.setText(pic);
}
public void setCost(String cost) {
costTextView.setText(cost);
}
public void setDistance(String distance) {
distanceTextView.setText(distance);
}*/
}
}
Create a listener Interface and let your MainActivity implement it. That way you can call your callback method in your onClick method.
Interface:
public interface OnImageClickListener {
void onImageClick(String imageData);
}
MainActivity:
public class MainActivity implements OnImageClickListener {
#Override
public void onImageClick(String imageData) {
// handle image data
}
//...
}
Your VideoAdapter:
//...
private OnImageClickListener onImageClickListener;
public VideoAdapter(ArrayList<Video> data, ArrayList<Video> data2, Activity activity, OnImageClickListener onImageClickListener) {
this.mData = data;
this.mData2 = data2;
this.mACtivity = activity;
this.onImageClickListener = onImageClickListener;
}
//...
#Override
public void onBindViewHolder(VideoHolder holder, int position) {
//...
holder.restaurantImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onImageClickListener.onImageClick(video2.getPic());
}
});
//...
}
//...
If you want to pass value from onclick to your Parent activity, use onMethodCallback in your MainActivity:
public class MainActivity extends Activity implements AdapterCallback {
private MyAdapter mMyAdapter;
#Override
public void onMethodCallback(String yourValue) {
// get your value here
}
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mMyAdapter = new MyAdapter(this);
}
}
In your Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(Context context) {
try {
this.mAdapterCallback = ((AdapterCallback) context);
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement AdapterCallback.");
}
}
#Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, final int i) {
// simple example, call interface here
// not complete
viewHolder.itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mAdapterCallback.onMethodCallback();
}
});
}
public static interface AdapterCallback {
void onMethodCallback(String yourValue);
}
}
If you are working with kotlin, the solution is much simpler.
Original Answer here
pass the lamda val itemClick: (Int) -> Unit to your adapter.
class MyRecyclerViewAdapter(params , val itemClick: (Int) -> Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
internal inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
...
itemView.setOnClickListener( {itemClick(layoutPosition)} )
}
}
In your Activity use the returned value position
val myAdapter = MyRecyclerViewAdapter(params) { position ->
// do something
}
Write interface in your adapter like bellow :
interface clickItemListener {
void onItemClick(String order);
}
And your constructor should be :
privat clickItemListener clickItemListeners;
public VideoAdapter(ArrayList<Video> data, ArrayList<Video> data2, Activity activity, clickItemListener clickItemListeners) {
this.mData = data;
this.mData2 = data2;
this.mACtivity = activity;
this.clickItemListeners = clickItemListeners;
}
Then you can pass to Activity :
yourLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
clickItemListeners.onItemClick(yourValue);
}
});
And implement your interface in your activity like bellow :
public class yourActivity extends AppCompatActivity implements
YourAdapter.clickItemListener{
#Override
public void onItemClick(String order) {
//You can get your value here
}
}
Remember that you should pass activity to adapter like bellow :
YourAdapter adapter = new YourAdapter (... , ... , ... , activity.this);

RecyclerView adapter custom listener interface

I have a RecyclerView with a custom listener that I want to implement in my main activity so that it gets notified when the RecyclerView item gets clicked.
However, I'm not sure where to put setMyAdapterListener in the adapter. Where would I put it?
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<Data> data;
private MyAdapterListener myAdapterListener;
public MyAdapter(Context context, List<Object> data) {
this.context = context;
this.data = data;
}
public interface MyAdapterListener {
void onContainerClick();
}
public void setMyAdapterListener(MyAdapterListener myAdapterListener) {
this.myAdapterListener = myAdapterListener;
}
public class ViewHolderItem extends RecyclerView.ViewHolder implements View.OnClickListener {
public LinearLayout container;
public ImageView poster;
public ViewHolderItem(View v) {
super(v);
container = (LinearLayout) v.findViewById(R.id.container);
poster = (ImageView) v.findViewById(R.id.poster);
}
#Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.container) {
if (myAdapterListener != null) {
myAdapterListener.onContainerClick();
}
}
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v2 = inflater.inflate(R.layout.item_layout, parent, false);
viewHolder = new ViewHolderItem(v2);
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Data item = (Data) data.get(position);
holder.poster.setImageDrawable(someDrawable);
}
#Override
public int getItemCount() {
return data.size();
}
}
Remove the setMyAdapter() just pass the arguments you currently have in your setMyAdapterListener as part of your recyclerview's adapter contructor, see below:
public MyAdapter(Context context, List<Object> data, MyAdapterListener myAdapterListener) {
this.context = context;
this.data = data;
this.myAdapterListener = myAdapterListener;
}
//On your activity you have to implement the methods of your interface.
As #ScottS Said you dont need setMyAdapter() method, just pass the onclick listener Interface class as a argument to your adapter constructor then implement the interface methods in activity Class .. code is shown below.
public MyAdapter(Context context, List<Object> data) {
this.context = context;
this.data = data;
this.myAdapterListener = myAdapterListener;
}
public interface MyAdapterListener {
void onContainerClick(); // please provide 2 parameter that will help you more for manipulation like shown below
/// void onContainerClick(View view, int position);
}
}
public class ViewHolderItem extends RecyclerView.ViewHolder implements View.OnClickListener {
public LinearLayout container;
public ImageView poster;
public ViewHolderItem(View v) {
super(v);
container = (LinearLayout) v.findViewById(R.id.container);
poster = (ImageView) v.findViewById(R.id.poster);
container =setOnClickListener(this); /// this line must include
}
#Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.container) {
if (myAdapterListener != null) {
myAdapterListener.onContainerClick();
// myAdapterListener.onContainerClick(v,getAdapterPosition());
}
}
}
}
Then you can handle the events in your Activity or wherever your RecyclerView is being used:
mAdapter = new MyAdapter(getApplicationContext() , data, new MyAdapter.MyAdapterListener() {
#Override
public void onContainerClick(View v, int position) {
Log.d(TAG, "iconTextViewOnClick at position "+position);
}
});
mRecycler.setAdapter(mAdapter);

Categories