I've implemented the recyclerView clicklistener using best practices yet it still isn't working. The recyclerview is loading data from the internet using retrofit and should take users to the site where the article was published below is my code:
NOTE: My RecyclerView is a RecyclerView of Cardviews
RecyclerView Adapter
public class NewsArticleAdapter extends RecyclerView.Adapter<NewsArticleAdapter.Viewholder> {
private ArrayList<Article> newsArticles = new ArrayList<>();
private Context context;
private OnArticleListener onArticleListener;
public NewsArticleAdapter(List<Article> newsArticles, Context context, OnArticleListener onArticleListener) {
this.newsArticles = (ArrayList<Article>) newsArticles;
this.context = context;
this.onArticleListener = onArticleListener;
}
#NonNull
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_list_item, parent, false);
Viewholder viewholder = new Viewholder(view, onArticleListener);
return viewholder;
}
#Override
public void onBindViewHolder(#NonNull Viewholder holder, int position) {
Article currentArticle = newsArticles.get(position);
holder.newsHeadline.setText(currentArticle.getTitle());
Glide.with(context)
.load(currentArticle.getUrlToImage())
.placeholder(R.drawable.ic_launcher_foreground)
.into(holder.newsImage);
holder.newsContent.setText(currentArticle.getContent());
// holder.publishedTime.setText(UTCtoLocalDateConverter(currentArticle.getPublishedAt()));
}
#Override
public int getItemCount() {
return newsArticles.size();
}
public class Viewholder extends RecyclerView.ViewHolder implements View.OnClickListener {
OnArticleListener onArticleClickedListner;
TextView newsHeadline;
TextView newsContent;
// TextView publishedTime;
ImageView newsImage;
ConstraintLayout parentLayout;
public Viewholder(#NonNull View itemView, OnArticleListener onArticleListener) {
super(itemView);
newsHeadline = itemView.findViewById(R.id.news_headline_textView);
newsContent = itemView.findViewById(R.id.news_content_textView);
// publishedTime = itemView.findViewById(R.id.published_time_textView);
newsImage = itemView.findViewById(R.id.news_image_view);
parentLayout = itemView.findViewById(R.id.list_item_parent_layout);
onArticleClickedListner = onArticleListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Log.d("RecyclerViewAdapter", "item position clicked: " + getAdapterPosition());
onArticleClickedListner.onArticleClicked(getAdapterPosition());
}
}
/*RecyclerView onItemClickListenerInterface*/
public interface OnArticleListener {
void onArticleClicked(int position);
}
}
MainActivity (Houses RecyclerView)
public class MainActivity extends AppCompatActivity implements NewsArticleAdapter.OnArticleListener {
private RecyclerView newsRecyclerView;
private NewsArticleAdapter newsAdapter;
private NewsAPI NewsAPI;
private ArrayList<Article> newsArticles;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
newsRecyclerView = findViewById(R.id.newsRecyclerView);
newsAdapter = new NewsArticleAdapter(new ArrayList<Article>(), this, this);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
newsRecyclerView.setLayoutManager(layoutManager);
newsRecyclerView.setAdapter(newsAdapter);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(RetrofitClient.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
NewsAPI = retrofit.create(NewsAPI.class);
Call<Example> call = NewsAPI.getRootJSONObject();
call.enqueue(new Callback<Example>() {
#Override
public void onResponse(Call<Example> call, Response<Example> response) {
if (response.isSuccessful()) {
newsArticles = (ArrayList<Article>) response.body().getArticles();
refreshAdapterWithNewsArticles(newsArticles);
} else {
Log.d("MainActivity", "Error in on Response " + String.valueOf(response.code()));
}
}
#Override
public void onFailure(Call<Example> call, Throwable t) {
Toast.makeText(MainActivity.this, "Error retrieving News Articles :(", Toast.LENGTH_SHORT).show();
}
});
}
/*
*Method used to generate list of data using recyclerView with costom adapter
* */
private void refreshAdapterWithNewsArticles(List<Article> body) {
newsAdapter = null;
newsAdapter = new NewsArticleAdapter(body, this, MainActivity.this);
newsRecyclerView.setAdapter(newsAdapter);
}
#Override
public void onArticleClicked(int position) {
Log.d("MainActivity", "onArticleClicked Was called in MainACtivity");
Article currentArticle = newsArticles.get(position);
Uri articleUri = Uri.parse(currentArticle.getUrl());
Intent intent = new Intent(Intent.ACTION_VIEW, articleUri);
startActivity(intent);
}
}
Inspired by the code provided in this answer from Jacob Tabak, in your mainActivity under onCreate method.
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(MainActivity.this, recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
//whatever you want to do with position
}
#Override
public void onLongItemClick(View view, int position) {
//whatever you want to do with position
}
}));
and create a new class name it RecyclerItemClickListener
and paste this
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
GestureDetector mGestureDetector;
private OnItemClickListener mListener;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = 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 && mListener != null) {
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
return true;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface OnItemClickListener {
public void onItemClick(View view, int position);
public void onLongItemClick(View view, int position);
}}
It is a custom RecyclerItemClickListener.
Why don't you try the listener from the onBindViewHolder() method?
holder.your_layout_item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onArticleClickedListner.onArticleClicked(position);
}
});
If you have any confusion I can explain it more clear. If you solved it you can accept the answer.
Related
I am new to Android and I have a question.
I have a recyclerview that can be clicked and will send a query. When clicked, it will get a number/ID of each position that is clicked,
like this:
fno = arrivals.get(position).flightno;
codeCity = arrivals.get(position).arr;
When clicked for the first time it succeeds, but when clicked for the second or third time, it gets an error. I think this because the variable fno and codeCity has been filled. So how to delete the value on the variable?
This my code when the recyclerview clicked:
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
GestureDetector gestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
public boolean onSingleTapUp(MotionEvent e){
return true;
}
});
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && gestureDetector.onTouchEvent(e)){
int position = rv.getChildAdapterPosition(child);
fno = arrivals.get(position).flightno;
codeCity = arrivals.get(position).arr;
final Bundle i = new Bundle();
i.putString("city", codeCity.toString()); // Key1
i.putString("flightno", fno.toString()); // Key1
open2(i);
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
This the query
public void open2(Bundle args) {
codeIata = args.getString("city");
String flightdate = args.getString("flightdate");
fno = args.getString("flightno");
ArrivalActivity.detail(codeIata, flightdate, fno);
}
public static void detail(String s, String date, String fno) {
DateFormat df = new SimpleDateFormat("yyyy-MM-d");
date = df.format(Calendar.getInstance().getTime());
Call<GetArrivals> arrCall = mApiInterface.getArr(s, "2019-03-04", "", fno);
arrCall.enqueue(new Callback<GetArrivals>() {
#Override
public void onResponse(Call<GetArrivals> call, Response<GetArrivals>
response) {
System.out.println("status code "+response.code());
arrivals = response.body().getResult();
mAdapter = new ArrivalAdapter(mContext, arrivals);
//mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onFailure(Call<GetArrivals> call, Throwable t) {
Log.e("Retrofit Get", t.toString());
}
});
}
Thank you!
There are two approaches you can capture a click event on item of a recyclerview:
First approach
override onClick method instead of onInterceptTouchEvent.
I am providing you with an example.
mRecyclerView.addOnItemTouchListener(new RecyclerTouchListener(this,
recyclerView, new ClickListener() {
#Override
public void onClick(View view, final int position) {
fno = arrivals.get(position).flightno;
codeCity = arrivals.get(position).arr;
}
}));
class RecyclerTouchListener implements RecyclerView.OnItemTouchListener{
private ClickListener clicklistener;
private GestureDetector gestureDetector;
public RecyclerTouchListener(Context context, final RecyclerView recycleView, 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=recycleView.findChildViewUnder(e.getX(),e.getY());
if(child!=null && clicklistener!=null){
clicklistener.onLongClick(child,recycleView.getChildAdapterPosition(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.getChildAdapterPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Second Approach(a more conventional one)
override the OnClickListener of the viewholder of your adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
LayoutInflater inflater;
Context context;
private MyAdapter.RecyclerViewClickListener mListener;
public MyAdapter(Context context, RecyclerViewClickListener mListener) {
this.context = context;
this.mListener = mListener;
inflater = LayoutInflater.from(this.context);
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public MyViewHolder(View item, RecyclerViewClickListener listener) {
super(item);
mListener = listener;
item.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mListener.onClick(view, getAdapterPosition());
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.mylayout, parent, false);
return new MyViewHolder(itemView,mListener);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return youylist.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public long getItemId(int position) {
return 0;
}
public interface RecyclerViewClickListener {
void onClick(View view, int position);
}
}
and in your Fragment/Activity:
myadapter = new MyAdapter(getContext(), new MyAdapter.RecyclerViewClickListener() {
#Override
public void onClick(View view, int position) {
fno = arrivals.get(position).flightno;
codeCity = arrivals.get(position).arr;
}
});
recyclerView.setAdapter(MyAdapter);
MyAdapter.notifyDataSetChanged();
Hope this helps you.
I want to take textview data from recycler view to another activity.I want to take
workshop_name TextView to Otheractivity.
Here is my code.
MechSearchActivity.java
public class MechSearchActivity extends AppCompatActivity {
List<SearchResponse.WorkshopDataBean> workshopList;
RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_mec);
recyclerView = (RecyclerView) findViewById(R.id.rv_workshoplist);
callSearchApi();
onItemClick();
}
public void callSearchApi() {
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<SearchResponse> call = apiService.getSearchData( "workshop_name","image", "street", "locality", "city"); // single search for all the values
call.enqueue(new Callback<SearchResponse>() {
#Override
public void onResponse(Call<SearchResponse> call, retrofit2.Response<SearchResponse> response) {
SearchResponse searchResponse = response.body();
workshopList = searchResponse.getWorkshopData();
MechanicRecyclerAdapter adapter = new MechanicRecyclerAdapter(MechSearchActivity.this, workshopList);
recyclerView.setAdapter(adapter);
LinearLayoutManager mGridLayoutManager = new LinearLayoutManager(MechSearchActivity.this); // (Context context, int spanCount)
recyclerView.setLayoutManager(mGridLayoutManager);
}
#Override
public void onFailure(Call<SearchResponse> call, Throwable t) {
}
});
}
private void onItemClick() {
final GestureDetector mGestureDetector = new GestureDetector(MechSearchActivity.this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
recyclerView_ws.addOnItemTouchListener(new
RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
View child = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
if (child != null && mGestureDetector.onTouchEvent(motionEvent)) {
int position = recyclerView.getChildPosition(child);
String workshop_name = workshopList.get(position).getWorkshop_name();
Intent intent = new Intent(MechSearchActivity.this, OtherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("workshop_name", workshop_name);
startActivity(intent);
return true;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
}
}
MechRecyclerAdapter.java
public class MechanicRecyclerAdapter extends RecyclerView.Adapter<MechanicRecyclerAdapter.MyViewHolder> {
private static final String TAG = MechanicRecyclerAdapter.class.getSimpleName();
Context mContext;
private LayoutInflater inflater;
String vehicle_type;
List<SearchResponse.WorkshopDataBean> workshopList;
public MechanicRecyclerAdapter(Context context, List<SearchResponse.WorkshopDataBean> workshopList) {
inflater = LayoutInflater.from(context);
this.workshopList = workshopList;
mContext = context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.single_workshop, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
SearchResponse.WorkshopDataBean current = workshopList.get(position);
String ws_name = workshopList.get(position).getWorkshop_name();
holder.tv_workshopname.setText(ws_name);
}
#Override
public int getItemCount() {
return workshopList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
SearchResponse.WorkshopDataBean current;
TextView tv_workshopname;
public MyViewHolder(View itemView) {
super(itemView);
tv_workshopname = (TextView) itemView.findViewById(workshop_name);
}
}
}
OtherActivity.java
public class OtherActivity extends AppCompatActivity {
TextView SingleWorkshopName;
String workshop_name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.otheractivity);
SingleWorkshopName=(TextView)findViewById(R.id.single_workshop_name);
Bundle extras = getIntent().getExtras();
workshop_name = extras.getString("workshop_name", null);
}
}
For that you need to create one interface in your MechanicRecyclerAdapter
public interface OnListItemClick {
void onItemClick(int position);
}
in onBindViewHolder() assign your click by using following code
viewHolder.main_layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onListItemClick.onItemClick(position);
}
});
Implements this interface in you MechSearchActivity you will get onItemClick() method in your activity using position you will get proper name of row and pass to other activity using intent.putExtra("workshop_name", workshop_name);
i am working on one project in which i have used open source code, but at one point i am stuck, in this code i am using RecyclerView in which onRecycleItemClickListener is already implemented but now i need to implement onItemLongClickListener in existing code
public abstract class BaseRecyclerViewAdapter<T, VH extends BaseClickListenerViewHolder> extends RecyclerView.Adapter<VH> {
private List<T> objectsList;
protected final BaseActivity baseActivity;
protected final LayoutInflater layoutInflater;
protected final Resources resources;
protected QBUser currentQbUser;
// Package private because we need access in BaseViewHolder but not in child classes
OnRecycleItemClickListener<T> onRecycleItemClickListener;
AdapterView.OnItemLongClickListener onItemLongClickListener;
public BaseRecyclerViewAdapter(BaseActivity baseActivity) {
this.baseActivity = baseActivity;
this.layoutInflater = LayoutInflater.from(baseActivity);
resources = baseActivity.getResources();
objectsList = new ArrayList<>();
}
public BaseRecyclerViewAdapter(BaseActivity baseActivity, List<T> objectsList) {
this(baseActivity);
this.objectsList = objectsList;
currentQbUser = AppSession.getSession().getUser();
}
public void setList(List<T> items) {
objectsList = items;
notifyDataSetChanged();
}
public void addItem(T item) {
objectsList.add(item);
notifyItemInserted(objectsList.size() - 1);
}
public void addItem(int position, T item) {
objectsList.add(position, item);
notifyItemInserted(position);
}
public void addAll(Collection<T> collection) {
objectsList.addAll(collection);
notifyItemRangeChanged(objectsList.size() - collection.size(), collection.size());
}
public void removeItem(int position) {
objectsList.remove(position);
notifyItemRemoved(position);
}
public void removeItem(T item) {
int position = objectsList.indexOf(item);
if (position != -1) {
objectsList.remove(item);
notifyItemRemoved(position);
}
}
public void clear() {
objectsList.clear();
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return objectsList.size();
}
public T getItem(int position) {
return objectsList.get(position);
}
public List<T> getAllItems() {
return objectsList;
}
public boolean isEmpty() {
return objectsList.size() == 0;
}
public void setOnRecycleItemClickListener(OnRecycleItemClickListener<T> onRecycleItemClickListener) {
this.onRecycleItemClickListener = onRecycleItemClickListener;
}
protected void displayAvatarImage(String uri, ImageView imageView) {
ImageLoader.getInstance().displayImage(uri, imageView, ImageLoaderUtils.UIL_USER_AVATAR_DISPLAY_OPTIONS);
}
}
You can use this class ItemTouchListenerAdapter in your code to easily add support for both item clicks and long-clicks.
Usage:
recyclerView.addOnItemTouchListener(new ItemTouchListenerAdapter(recyclerView, this));
Code:
public class ItemTouchListenerAdapter extends GestureDetector.SimpleOnGestureListener implements RecyclerView.OnItemTouchListener {
public interface RecyclerViewOnItemClickListener {
void onItemClick(RecyclerView parent, View clickedView, int position);
void onItemLongClick(RecyclerView parent, View clickedView, int position);
}
private RecyclerViewOnItemClickListener listener;
private RecyclerView recyclerView;
private GestureDetector gestureDetector;
public ItemTouchListenerAdapter(RecyclerView recyclerView, RecyclerViewOnItemClickListener listener) {
if (recyclerView == null || listener == null) {
throw new IllegalArgumentException("RecyclerView and Listener arguments can not be null");
}
this.recyclerView = recyclerView;
this.listener = listener;
this.gestureDetector = new GestureDetector(recyclerView.getContext(), this);
}
#Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
gestureDetector.onTouchEvent(motionEvent);
return false;
}
#Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
}
#Override
public void onShowPress(MotionEvent e) {
View view = getChildViewUnder(e);
if (view != null) {
view.setPressed(true);
}
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
View view = getChildViewUnder(e);
if (view == null) return false;
view.setPressed(false);
int position = recyclerView.getChildPosition(view);
listener.onItemClick(recyclerView, view, position);
return true;
}
public void onLongPress(MotionEvent e) {
View view = getChildViewUnder(e);
if (view == null) return;
int position = recyclerView.getChildPosition(view);
listener.onItemLongClick(recyclerView, view, position);
view.setPressed(false);
}
#Nullable
private View getChildViewUnder(MotionEvent e) {
return recyclerView.findChildViewUnder(e.getX(), e.getY());
}
}
I have this adapter class where I have an object with a list in it.
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
private Context context;
private Shop shop;
public ProductAdapter(Context context, Shop shop) {
this.context = context;
this.shop = shop;
}
#Override
public ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(R.layout.product_row, parent, false);
return new ProductViewHolder(v);
}
#Override
public void onBindViewHolder(ProductViewHolder holder, int position) {
holder.tvProductTitle.setText(shop.products.get(position).getTitle());
holder.tvProductDescription.setText(shop.products.get(position).getDescription());
Glide.with(context)
.load(shop.products.get(position).getImageUrl())
.placeholder(android.R.drawable.ic_menu_upload_you_tube)
.error(android.R.drawable.stat_notify_error)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.into(holder.ivProduct);
}
#Override
public int getItemCount() {
if (shop.products != null) {
return shop.products.size();
}
return 0;
}
public static class ProductViewHolder extends RecyclerView.ViewHolder {
private TextView tvProductTitle, tvProductDescription;
private ImageView ivProduct;
public ProductViewHolder(View itemView) {
super(itemView);
tvProductTitle = (TextView) itemView.findViewById(R.id.tvProductTitle);
tvProductDescription = (TextView) itemView.findViewById(R.id.tvProductDescription);
ivProduct = (ImageView) itemView.findViewById(R.id.ivProduct);
}
}
public interface ClickListener {
void onClick(View v, int position);
void onLongClick(View v, int position);
}
public static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ProductAdapter.ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ProductAdapter.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.getChildAdapterPosition(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.getChildAdapterPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
I use this adapter in my fragment where I can click an item from the recyclerview which will open another activity with the selected product and it's parametars. Instead of clicking the whole item, I would like to click on part of it (lets say imageview) which will do the same as it is doing now. But, I would also like to put a checkbox in the recyclerview item itself which will have different function. At the moment, I can't use an intent in the adapter class. The way it is set up now I can only click the whole item. How can I make it work like I explained above?
public class ProductsFragment extends android.support.v4.app.Fragment {
private RecyclerView rvProduct;
private RecyclerView.LayoutManager layoutManager;
private ProductAdapter productAdapter;
public static ProductsFragment newInstance(Bundle args) {
ProductsFragment fragment = new ProductsFragment();
fragment.setArguments(args);
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_products, container, false);
rvProduct = (RecyclerView) v.findViewById(R.id.rvProduct);
rvProduct.setHasFixedSize(true);
layoutManager = new GridLayoutManager(getContext(), 2);
rvProduct.setLayoutManager(layoutManager);
Bundle bundle = getArguments();
final Shop shop = (Shop) bundle.getSerializable("shop");
productAdapter = new ProductAdapter(getContext(), shop);
rvProduct.setAdapter(productAdapter);
productAdapter.notifyDataSetChanged();
rvProduct.addOnItemTouchListener(new ProductAdapter.RecyclerTouchListener(getContext(), rvProduct, new ProductAdapter.ClickListener() {
#Override
public void onClick(View v, int position) {
Intent details = new Intent(getContext(), ProductDetailsActivity.class);
details.putExtra("product", shop.products.get(position));
startActivity(details);
}
#Override
public void onLongClick(View v, int position) {
}
}));
return v;
}
}
EDIT:
I've managed to do this, please tell me if this is right and efficient way:
holder.tvProductTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(context, ProductDetailsActivity.class);
i.putExtra("product", shop.products.get(position));
context.startActivity(i);
}
});
The way you doing it is not correct.
As your onBindViewHolder will be called repeatedly as you scroll through the list, every time a new listener will be set.
Instead you should set the listeners in the ViewHolder class of Adapter. And use getAdapterPosition() method to get the position of item clicked.
For example:
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
private Context context; private Shop shop;
public ProductAdapter(Context context, Shop shop) {
this.context = context;
this.shop = shop;
}
#Override public ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(R.layout.product_row, parent, false);
return new ProductViewHolder(v);
}
#Override public void onBindViewHolder(ProductViewHolder holder, int position) {
holder.tvProductTitle.setText(shop.products.get(position).getTitle());
holder.tvProductDescription.setText(shop.products.get(position).getDescription());
Glide.with(context) .load(shop.products.get(position).getImageUrl()) .placeholder(android.R.drawable.ic_menu_upload_you_tube) .error(android.R.drawable.stat_notify_error) .diskCacheStrategy(DiskCacheStrategy.RESULT) .into(holder.ivProduct);
}
#Override public int getItemCount()
{
if (shop.products != null) {
return shop.products.size();
} return 0;
}
public static class ProductViewHolder extends RecyclerView.ViewHolder {
private TextView tvProductTitle, tvProductDescription;
private ImageView ivProduct;
public ProductViewHolder(View itemView)
{
super(itemView);
tvProductTitle = (TextView) itemView.findViewById(R.id.tvProductTitle);
tvProductDescription = (TextView) itemView.findViewById(R.id.tvProductDescription);
ivProduct = (ImageView) itemView.findViewById(R.id.ivProduct);
// Set onclickListener on image
ivProduct.setOnCliCkListener(new onClickListener(){
#Override
public void onClick(View v){
Intent i = new Intent(context, ProductDetailsActivity.class);
i.putExtra("product",
shop.products.get(getAdapterPosition()));
context.startActivity(i);
}
}
}
}
public interface ClickListener {
void onClick(View v, int position);
void onLongClick(View v, int position);
}
}
You can add click listeners to each respective view of the inflated item in public onBindViewHolder. Did you consider that?
Edit: Ok it's what you did. I don't know about the efficiency though, but this is the way I would do it.
I have been trying to work on RecycleView item click listener. I have come up with following solution. I would like to know if this solution is correct or just a "hack".
public class CityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private int lastPosition = -1;
private int focusedItem = 0;
private static Interfaces.OnCityItemClickListener cityItemClickListner;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView cityNameTextView, tempTextView, humidityTextView, timeTextView;
private ImageView weatherStatusImageView;
private CardView cardView;
public ViewHolder(View itemView) {
super(itemView);
cityNameTextView = (TextView) itemView.findViewById(R.id.city_name_tv);
tempTextView = (TextView) itemView.findViewById(R.id.temp_tv);
humidityTextView = (TextView) itemView.findViewById(R.id.humidity_tv);
timeTextView = (TextView) itemView.findViewById(R.id.time_tv);
cardView = (CardView) itemView.findViewById(R.id.card_view);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
cityItemClickListner.onCityItemClick(getAdapterPosition(), v);
}
}
public CityListAdapter(Context context){
this.context = context;
}
public void setOnCityItemClickListener(Interfaces.OnCityItemClickListener clickListener){
cityItemClickListner = clickListener;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.row_city, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewHolder myViewHolder = (ViewHolder) holder;
myViewHolder.itemView.setSelected(focusedItem == position);
}
#Override
public int getItemCount() {
return 15;
}
}
Then in my Activity
adapter.setOnCityItemClickListener(new Interfaces.OnCityItemClickListener() {
#Override
public void onCityItemClick(int position, View view) {
Toast.makeText(BaseActivity.this, "Clicked position " + position, Toast.LENGTH_SHORT).show();
}
});
This blog post by Hugo shows an alternative way without listeners: http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
It comes down to adding these lines:
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
When using this class:
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}
And this value in ids.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
This is my implementation of RecyclerAdapter.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
ViewHolder.IOnClickListener clickListener;
ViewHolder.IOnLongClickListener longClickListener;
#Override
public void setClickListener(ViewHolder.IOnClickListener clickList, ViewHolder.IOnLongClickListener longClickListener) {
clickListener = clickList;
longClickListener = longClickListener;
}
#Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_event, viewGroup, false);
return new ViewHolder(v, new ViewHolder.IOnClickListener() {
#Override
public void onLogo(View view, ViewHolder holder, int position) {
Log.d(TAG, "on logo click");
clickListener.onLogo();
}
#Override
public void onItem(View view, ViewHolder holder, final int position) {
Log.d(TAG, "on item click");
clickListener.onItem();
}
}, new ViewHolder.IOnLongClickListener() {
#Override
public void onItem(View view, ViewHolder holder, int position) {
Log.d(TAG, "on item long click");
longClickListener.onItem();
}
});
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, OnLongClickListener {
View mView;
ImageView mIcon;
IOnClickListener mClickListener;
IOnLongClickListener mLonglickListener;
public ViewHolder(View itemView,
IOnClickListener listener,
IOnLongClickListener longClickListener) {
super(itemView);
mView = itemView;
mIcon = (ImageView)itemView.findViewById(R.id.icon);
mClickListener = listener;
mLonglickListener = longClickListener;
mIcon.setOnClickListener(this);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#Override
public void onClick(View v){
if(v instanceof ImageView){
mClickListener.onLogo(v, this, getAdapterPosition());
} else {
mClickListener.onItem(v, this, getAdapterPosition());
}
}
#Override
public boolean onLongClick(View v) {
mLonglickListener.onItem(v, this, getAdapterPosition());
return true;
}
public static interface IOnClickListener {
void onLogo(View view, ViewHolder v,int position);
void onItem(View view, ViewHolder v, int position);
}
public static interface IOnLongClickListener {
void onItem(View view, ViewHolder v,int position);
}
}
Then you can transfer your onClickListener to this.
RecyclerAdapter adapter = new RecyclerAdapter();
adapter.setClickListener(new ViewHolder.IOnClickListener(){...}, new ViewHolder.IOnLongClickListener(){...})
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onLongItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = 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 && mListener != null) {
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
return true;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
#Override
public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
}
and using:
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(parentView.getContext(),
recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
//here your logic
}
#Override
public void onLongItemClick(View view, int position) {
}
}));
Try this:
mRecyclerView.addOnItemTouchListener(new RecyclerTouchListener(mActivity, mRecyclerView, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
Toast.makeText(mActivity, mProductList.get(position).getPname(), Toast.LENGTH_SHORT).show();
}
#Override
public void onLongClick(View view, int position) {
Toast.makeText(mActivity, mProductList.get(position).getPname(), Toast.LENGTH_SHORT).show();
}
}));
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.getChildAdapterPosition(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.getChildAdapterPosition(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);
}
}