I have implemented an Recycler view with its item click inside the Holder class. Each item click opens a new activity. The problem is when user clicks an item twice in a fast tap (very quickly very less span of time), it opens the activity twice.
I can't try below solution:
android:launchMode="singleInstance" as I am using
startActivityForResult() to call the new activity.
android:launchMode="singleTask" is also not working.
How can it be stopped from happening? Please suggest.
Below is my adapter class:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
case MY_VIEW_TYPE:
View v1 = inflater.inflate(R.layout.created_by_me_item, parent, false);
viewHolder = new ViewHolderCreatedByMe(v1);
break;
}
return viewHolder;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolderCreatedByMe) {
// The data setting method is inside the Holder class
((ViewHolderCreatedByMe) holder).setDataCreatedByMe(mFeedRecordListData.get(position));
} else {
((ViewHolderFooter) holder).setFooterView();
}
}
And below is my holder class:
public class ViewHolderCreatedByMe extends RecyclerView.ViewHolder implements View.OnClickListener {
// ... some class members are declared here
public ViewHolderCreatedByMe(View itemView) {
super(itemView);
// a few more UI components are initialized here
rl_reaction_item_main_layout = (RelativeLayout) itemView.findViewById(R.id.rl_reaction_item_main_layout);
rl_reaction_item_main_layout.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.rl_reaction_item_main_layout:
Intent intent = new Intent(mActivity, NewActivity.class);
intent.putExtra(NewActivity.KEY_EVENT_ID, UIModel.getEventID());
intent.putExtra(NewActivity.KEY_EVENT_TIME_STAMP, UIModel.getEventLocalTimeStamp());
intent.putExtra(NewActivity.KEY_EVENT_POSITION, getAdapterPosition());
mFragment.startActivityForResult(intent, Constants.REQUEST_CODE);
break;
}
}
you should use a boolean flag to check weather row item is clicked or not like this.
boolean isItemClicked = false;
private void onRecyclerViewItemClick()
{
if(!isItemClicked)
{
isItemClicked = true;
startActivity(new Intent(this, MyActivity.class));
}
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
isItemClicked = false;
}
*Note- I used name onRecyclerViewItemClick() for example here, it will be replaced with your listView's onItemClick
Create an interface to intercept click on the view of your recyclerview item.
For example :
public interface CustomClickListener {
void onClick(UIModel uiModel, int position);
}
Create a method in your adapter class to set the interface defined :
private CustomClickListener mCustomClickListener;
public void setClickListener( CustomClickListener customClickListener ) {
this.mCustomClickListener = customClickListener;
}
Modify your view holder class to this :
public class ViewHolderCreatedByMe extends RecyclerView.ViewHolder implements View.OnClickListener {
// ... some class members are declared here
public ViewHolderCreatedByMe(View itemView) {
super(itemView);
// a few more UI components are initialized here
rl_reaction_item_main_layout = (RelativeLayout) itemView.findViewById(R.id.rl_reaction_item_main_layout);
rl_reaction_item_main_layout.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if( position != RecyclerView.NO_POSITION && mCustomClickListener != null ) {
mCustomClickListener.onClick(UIModel, position);
}
}
In your fragment class, you would have your adapter defined, set the click listener here :
CustomClickListener customClickListener = new CustomClickListener() {
#Override
void onClick(UIModel uiModel, int position) {
Intent intent = new Intent(getActivity(), NewActivity.class);
intent.putExtra(NewActivity.KEY_EVENT_ID, uiModel.getEventID());
intent.putExtra(NewActivity.KEY_EVENT_TIME_STAMP, uiModel.getEventLocalTimeStamp());
intent.putExtra(NewActivity.KEY_EVENT_POSITION, position);
getActivity().startActivityForResult(intent, Constants.REQUEST_CODE);
adapter.setCustomClickListener(null); //To prevent the event being triggered twice
}
}
adapter.setCustomClickListener( customClickListener );
In your fragment where you have overriden onActivityResult, do this :
#Override
public void onActivityResult( int requestCode, int resultCode, Intent intent ) {
if( requestCode == Constants.REQUEST_CODE ) {
//Do something with the result
adapter.setCustomClickListener(customClickListener); // Make customClickListener a global variable
}
}
Hope this helps. Let me know if you've found a better way to tackle the problem if this doesn't suit your needs. The answer based out of boolean will work, but its a mess to track the boolean when you open and close the app.
Add these flag Intent.FLAG_ACTIVITY_SINGLE_TOP FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_RESET_TASK_IF_NEEDED FLAG_ACTIVITY_CLEAR_TOP
Your click event should look like this
case R.id.rl_reaction_item_main_layout:
Intent intent = new Intent(mActivity, NewActivity.class);
intent.putExtra(NewActivity.KEY_EVENT_ID, UIModel.getEventID());
intent.putExtra(NewActivity.KEY_EVENT_TIME_STAMP, UIModel.getEventLocalTimeStamp());
intent.putExtra(NewActivity.KEY_EVENT_POSITION, getAdapterPosition());
//add these following line
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mFragment.startActivityForResult(intent, Constants.REQUEST_CODE);
break;
Related
I created six of CardView and linked them to RecyclerView , how when press on cardview postion[2]
I want to make every card view and guest a move to another activity
this my code.
public class MyMovieAdapter extends RecyclerView.Adapter<MyMovieAdapter.ViewHolder> {
MyMovieData[] myMovieData;
Context context;
public MyMovieAdapter(MyMovieData[] myMovieData,MainActivity activity) {
this.myMovieData = myMovieData;
this.context = activity;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.movie_item_list,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final MyMovieData myMovieDataList = myMovieData[position];
holder.textViewName.setText(myMovieDataList.getMovieName());
holder.textViewDate.setText(myMovieDataList.getMovieDate());
holder.movieImage.setImageResource(myMovieDataList.getMovieImage());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, myMovieDataList.getMovieName(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(), MainActivity2.class);//////////////// //this line //////////////////////// I want to position id
v.getContext().startActivity(intent);
}
});
}
you already have int position, just make it final and then you may use it inside onClick
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, final int position) {
...
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, myMovieDataList.getMovieName(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(), MainActivity2.class);
intent.putExtra("position", position);
// would be better to pass item id or name, much better approach
intent.putExtra("itemId", myMovieDataList.getMovieId());
v.getContext().startActivity(intent);
}
});
}
Answer by #snachman is perfectly right. But, it is not preferred by google. Use the method given below.
Create a new interface with name RecyclerViewItemClickListener.java
Add method void onClick(); to it;
Add that to the constructor of the adapter.
After adding the constructor, add it to your activity by implementing the interface
Add it in the activity by new RecyclerViewItemClickListener
Now in the activity, add the code to start the activity.
Hope it helps 😀
How to get the position for clicked button inside the RecyclerView items
Here's my onBindViewHolder :
public void onBindViewHolder(MyViewHolder holder, int position) {
Masar masar=masrList.get(position);
holder.masarName.setText(masar.getMasarTitle());
holder.masarDesc.setText(masar.getMasarDescreption());
//How to get the Position
holder.masarImg.setImageResource(masar.getMasarImg());
holder.mapBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v ) {
//if you need position, just use recycleViewHolder.getAdapterPosition();
Intent intent = new Intent(v.getContext(), MapsActivity.class);
mContext.startActivity(intent);
}
});
}
If you need in onBindViewHolder only then you can use
holder.getAdapterPosition();
and if you need this position clicked in activity and fragment then you have to use callbacks from holder to activity and fragment and have to pass the same getAdapterPosition();
Edit: Added sample code for listening position click in fragment/activity
step 1: make an interface or callback
public interface RecyclerViewClickListener {
void onClick(View view, int position);
}
step 2: While initializing adapter class in fragment or activity pass the above-created reference as a parameter
public YourAdapter(List<SomeModel> modelList, RecyclerViewClickListener listener){
this.clickListener = listener;
}
step 3: In your ViewHolder or similar Class for view initialization do something like this
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private Button mapBtn;
ViewHolder(View v, RecyclerViewClickListener listener) {
super(v);
mapBtn = findViewById(R.id.mapBtn);
mListener = listener;
mapBtn.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mListener.onClick(view, getAdapterPosition());
}
}
you will get the position in your fragment or activity where you have passed the callback reference while initializing the adapter.
Use holder.getAdapterPosition();
public void onBindViewHolder(final MyViewHolder holder, int position) {
Masar masar=masrList.get(position);
holder.masarName.setText(masar.getMasarTitle());
holder.masarDesc.setText(masar.getMasarDescreption());
//How to get the Position
holder.masarImg.setImageResource(masar.getMasarImg());
holder.mapBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v ) {
Toast.makeText(getContext(), "The position is: "+holder.getAdapterPosition(), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(), MapsActivity.class);
mContext.startActivity(intent);
}
});}
My RecyclerView(rvPredictionIndex) item includes ImageView, EditText and Button, when Button is clicked the Text inside EditText will be uploaded on server and when ImageView is clicked, the New Activity will be called and the position of that Item will be passed to new activity using Intent().
I am using DataBinding so View() is not used here (or any other way of using View() than i don't know).
Here is RecyclerView Adapter code....
public class PredictionItemAdapter extends RecyclerView.Adapter<PredictionItemAdapter.MyViewHolder> {
private List<PredictionItems> mPredictionItemsList;
private Context mContext;
private int predictionId;
private String etAnswer;
public class MyViewHolder extends RecyclerView.ViewHolder {
// ImageView ivPredictionImage;
// CustomTextView txtPredictionQuestion;
PredictionItemBinding predictionItemBinding;
public MyViewHolder(PredictionItemBinding predictionItemBinding) {
super(predictionItemBinding.getRoot());
this.predictionItemBinding = predictionItemBinding;
// ivPredictionImage = (ImageView) itemView.findViewById(R.id.ivPredictionImage);
// txtPredictionQuestion = (CustomTextView) itemView.findViewById(R.id.txtPredictionQuestion);
}
}
public PredictionItemAdapter(Context context, List<PredictionItems> mPredictionItemsList) {
this.mPredictionItemsList = mPredictionItemsList;
this.mContext = context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.prediction_item, parent, false);
// return new MyViewHolder(view);
PredictionItemBinding predictionItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.prediction_item, parent, false);
return new MyViewHolder(predictionItemBinding);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
// PredictionItems predictionItems = mPredictionItemsList.get(position);
// Picasso.with(mContext).load(predictionItems.getImage()).into(holder.ivPredictionImage);
// holder.txtPredictionQuestion.setText(predictionItems.getQuestion());
final PredictionItems predictionItems = mPredictionItemsList.get(position);
PredictionViewModel predictionViewModel = new PredictionViewModel(predictionItems);
predictionViewModel.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
#Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == 1) {
etAnswer = holder.predictionItemBinding.editText.getText().toString();
predictionId = mPredictionItemsList.get(position).getId();
if (etAnswer.equals("")) {
Utility.showToastShort(mContext, "Please input answer");
} else {
callUpdatePredictionAnswerApi(predictionId, etAnswer);
// Toast.makeText(mContext, "submjit of " + holder.getAdapterPosition), Toast.LENGTH_SHORT).show();
}
}
else if (propertyId == 2){
//Here i want to start activity and pass data with intent
}
}
});
holder.predictionItemBinding.setPredictionVM(predictionViewModel);
}
#Override
public int getItemCount() {
return mPredictionItemsList.size();
}
}
I have already opened new activity on ItemClick of recyclerView but when i tried to click the EditText for input something, it will redirect me to the new activity, so that's why i want to start new activity on ImageView click..
here is my RecyclerViewItem Click...
private void rvPredictionIndexClick() {
rvPredictionIndex.addOnItemTouchListener(new RecyclerTouchListener(mContext, rvPredictionIndex, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
String preId = mPredictionItemsList.get(position).getId().toString();
Intent i = new Intent(mContext, PredictionInfoActivity.class);
String posi = ((String.valueOf(position)));
i.putExtra("predictionId", posi);
startActivity(i);
}
#Override
public void onLongClick(View view, int position) {
}
}));
}
Create Class DataBindingAdapter
and paste
#BindingAdapter("android:onClick")
public static void setOnClickListener(View view, final Runnable runnable) {
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runnable.run();
}
});
}
than go to your ViewModel class
and paste
public void onSubmitClicked() {
Log.e("onButtonSubmit", "onButtonSubmit");
notifyPropertyChanged(1);
}
public void onImageClicked() {
Log.e("onImageClicked", "onImageClicked");
notifyPropertyChanged(2);
}
than go to your item.xml file and call
android:onClick="#{predictionVM::onSubmitClicked}"
in your Button,
and
android:onClick="#{predictionVM.onImageClicked}
in your imaggView,
than go to your ItemAdapterClass
and inside onCreateViewHolder
predictionViewModel.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
#Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == 1) {
//do your stuff
}
else if (propertyId == 2) {
// do your stuff
}
The most elegant and frankly adequate solution would be not to do anything inside adapter!
I wrote an article about adapters, have a look RecyclerView Adapters
Straightforward: add a callback to your adapter and perform everything inside calling sight (fragment etc)
I have TextView and Button in RecyclerView I put text on Button as number when I clicked the button I want that number increase by 1 when I try recursive setonclicklistener the application doesn't work
I have two activities each of them call the same adapter
the code of adapter
public class AzkaarAdapter extends RecyclerView.Adapter<AzkaarAdapter.AzkaarViewHolder> {
ArrayList<AzkarForm> azkarForms=new ArrayList<AzkarForm>();
Context ctx;
private static ItemClickCallback itemClickCallback;
public interface ItemClickCallback {
void onItemClick(int p);
void onSecondaryIconClick(int p);
}
public void setItemClickCallback(final ItemClickCallback itemClickCallback) {
this.itemClickCallback = itemClickCallback;
}
public AzkaarAdapter (ArrayList<AzkarForm> azkarForms,Context ctx){
this.azkarForms=azkarForms;
this.ctx=ctx;
}
#Override
public AzkaarViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
AzkaarViewHolder azkaarViewHolder;
if(ctx instanceof MainActivity){
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_layout,parent,false);
azkaarViewHolder=new AzkaarViewHolder(view,ctx,azkarForms);
}
else {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_details,parent,false);
azkaarViewHolder=new AzkaarViewHolder(view,ctx,azkarForms);
}
return azkaarViewHolder;
}
#Override
public void onBindViewHolder(AzkaarViewHolder holder, int position) {
AzkarForm azkarForm=azkarForms.get(position);
if(ctx instanceof MainActivity) {
holder.sample.setText(azkarForm.getComplete_morning());
holder.number.setText(String.valueOf(azkarForm.getNumber()));
}else {
holder.sample.setText(azkarForm.getComplete_morning());
holder.tally_count.setText(getTextNumber(azkarForm.getNumber()));
}
}
public String getTextNumber(int number){
String numberText="";
switch (number){
case 1:
numberText="مرة واØدة";
break;
case 3:
numberText="ثلاث مرات";
break;
case 4:
numberText="أربع مرات";
break;
case 7:
numberText="سبع مرات";
break;
case 10:
numberText="عشر مرات";
break;
case 100:
numberText="مائة مرة";
break;
}
return numberText;
}
#Override
public int getItemCount() {
return azkarForms.size();
}
public static class AzkaarViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView sample, number;
View container;
Context ctx;
ArrayList<AzkarForm> azkarForms = new ArrayList<AzkarForm>();
Button tally_count,press,next;
public AzkaarViewHolder(View itemView, Context ctx, ArrayList<AzkarForm> azkarForms) {
super(itemView);
this.ctx = ctx;
//itemView.setOnClickListener(this);
this.azkarForms = azkarForms;
if(this.ctx instanceof MainActivity) {
sample = (TextView) itemView.findViewById(R.id.sample);
number = (TextView) itemView.findViewById(R.id.number);
container = itemView.findViewById(R.id.container);
container.setOnClickListener(this);
}else {
sample = (TextView) itemView.findViewById(R.id.sample);
tally_count=(Button)itemView.findViewById(R.id.count);
press=(Button)itemView.findViewById(R.id.press);
press.setOnClickListener(this);
next=(Button)itemView.findViewById(R.id.next);
}
//container.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (v.getId() == R.id.container){
itemClickCallback.onItemClick(getAdapterPosition());
} else {
itemClickCallback.onSecondaryIconClick(getAdapterPosition());
}
/*if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP){
Pair<View, String> p1 = Pair.create((View)sample, "qoute");
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, p1);
}*/
}
}
}
First Activity
recyclerView =(RecyclerView) findViewById(R.id.recyclerviewcard);
layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
adapter=new AzkaarAdapter(AzkaarData.getArrayListData(),this);
recyclerView.setAdapter(adapter);
adapter.setItemClickCallback(this);
toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
#Override
public void onItemClick(int p) {
//int position=getAdapterPosition();
AzkarForm azkarForm= list.get(p);
Intent intent=new Intent(this,DetailsActivity.class);
intent.putExtra("numbers",azkarForm.getNumber());
intent.putExtra("sample",azkarForm.getSample());
intent.putExtra("complete",azkarForm.getComplete_morning());
intent.putExtra("position",p);
this.startActivity(intent);
}
#Override
public void onSecondaryIconClick(int p) {
}
Second Activity
recyclerView =(RecyclerView) findViewById(R.id.recyclerviewcard);
layoutManager=new LinearLayoutManager(DetailsActivity.this,LinearLayoutManager.HORIZONTAL,false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.scrollToPosition(pos);
adapter=new AzkaarAdapter(AzkaarData.getArrayListData(),this);
recyclerView.setAdapter(adapter);
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
#Override
public void onItemClick(int p) {
}
#Override
public void onSecondaryIconClick(int p) {
AzkarForm azkarForm= list.get(p);
press=(Button) findViewById(R.id.press);
int currentNum=Integer.parseInt(press.getText().toString());
int num=azkarForm.getNumber();
if(currentNum<num){
currentNum++;
onSecondaryIconClick(p);
}
press.setText(String.valueOf(currentNum));
}
Okay so, first off there is no need for your SecondaryIconClick callback, all because you want it to do something else doesn't mean you need two of them, you can just set the same ItemCallback interface for both activities and then call a switch statement to know which widget was clicked from the layouts. It's easier for me to show you with the code you have provided, I haven't tested any of this code.
From what I can see the First Activity, works fine (I assume this isn't causing the issues in question). Except for readability I would suggest initializing a global instance of the Context (private Context context = this) for that activity and then instead of new Intent(this, DetailsActivity.class) you should have new Intent(context, DetailsActivity.class) because sometimes 'this' can point to the ClickListener rather than the Activity.
For the Second Activity the logic for the counter and setText for the button seems fine except you have already initialized your Button in the ViewHolder, so you don't need to initialize it again in the click listener. All you have to do is pass the view to the listener. (see my edit for the Adapter and ViewHolder code) also, I don't know why you're calling a new instance of the click event if the currentNum < num.
Do you want the clicks to be automatic up until a certain number or just everytime they click up until a certain number?
Anyway, let's solve this problem for you. I'm writing this code with the assumption you want the counter to go up everytime the user clicks R.id.press.
FirstActivity
So as I suggest above change the context reference.
#Override
public void onItemClick(View v, int position) {
switch (v.getId()) {
case R.id.container:
AzkarForm azkarForm= list.get(position);
Intent intent=new Intent(context, DetailsActivity.class);
intent.putExtra("numbers",azkarForm.getNumber());
intent.putExtra("sample",azkarForm.getSample());
intent.putExtra("complete",azkarForm.getComplete_morning());
intent.putExtra("position", position);
context.startActivity(intent);
break;
}
SecondActivity
recyclerView =(RecyclerView) findViewById(R.id.recyclerviewcard);
layoutManager=new LinearLayoutManager(DetailsActivity.this,LinearLayoutManager.HORIZONTAL,false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.scrollToPosition(pos);
adapter=new AzkaarAdapter(AzkaarData.getArrayListData(),this);
recyclerView.setAdapter(adapter);
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
#Override
public void onItemClick(View v, int position) {
switch(v.getId()) {
case R.id.press:
AzkarForm azkarForm= list.get(position);
int currentNum=Integer.parseInt(press.getText().toString());
int num = azkarForm.getNumber();
if(currentNum < num){
currentNum++;
}
v.setText(String.valueOf(currentNum));
break;
}
RecyclerViewAdapter
Your new interface callbacks should be:
public interface ItemClickCallback {
void onItemClick(View v, int position);
}
public void setItemClickCallback(final ItemClickCallback itemClickCallback) {
this.itemClickCallback = itemClickCallback;
}
ViewHolder
Your new click listener should look like:
#Override
public void onClick(View v) {
if (itemClickCallback != null) {
itemClickCallback.onItemClick(v, getAdapterPosition());
/*if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP){
Pair<View, String> p1 = Pair.create((View)sample, "qoute");
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, p1);
}*/
}
}
}
Now you have a clean framework for your listeners and you're passing the correct views and calling the correct context, you're able to apply the logic accordingly without confusion between callbacks.
Hope this helps.
final Button counter = (Button) findViewById(R.id.counter_button);
counter.setText("1");
counter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int currNum = Integer.parseInt(counter.getText().toString());
counter.setText("" + currNum + 1);
}
});
Replace button id as per your layout.
I have a RecyclerView which contains multiple item types (4 or 5 at least).
Some items can be clicked, sometimes they have two differents clickListener (for example two imageView in one item).
For now, the item manages the clicks himself like this :
item.imageView1.setOnClickListener(....){
startActivity(Activity2);
}
item.imageView2.setOnClickListener(....){
startActivity(Activity1);
}
But I have a problem : I need to put some variables in the activity which will be started, so what is the best way to do this :
1) My item need to know these variables and continues to manage his own click ?
2) My item has a listener which call startActivity with the variables (like the fragment or parent activity or an object dedicated to this) ?
If you need more precisions, ask me.
Thx.
Make an interface for passing those values.
public interface MyRecyclerCallback {
void onItemClicked(Integer o); //insert whatever you want to pass further, possibly translated to form packable to intents
}
Then add it to your adapter from the activity with recycler like you would any listener. Either constructor or by separate method.
Pass it further down to every children upon their creation.
Call it when onClick gets detected with appropriate argument.
The actual argument may be some abstract thing
depending on your logic. It's more of a general idea. That's the way I do it with my recyclers.
In your activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = (RecyclerView) findViewById(R.id.recycler);
MyAdapter adapter = new MyAdapter(this,new MyRecyclerCallback() {
#Override
public void onItemClicked(Integer o) { //any argument you like, might be an abstract
Intent i = new Intent(this,ActivityTwo.class);
i.putExtra(EXTRA_VALUE,o);
startActivity(i);
}
});
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(adapter);
}
Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Child>{
private final Context mContext;
private final MyRecyclerCallback mCallback;
private List<Integer> mChildren;
public MyAdapter(Context ctx, MyRecyclerCallback myRecyclerCallback) {
mContext = ctx;
mCallback = myRecyclerCallback;
mChildren = new ArrayList<>();
}
public void populateList(List<Integer> list ) { //this can be a network call or whatever you like
mChildren.addAll(list);
}
#Override
public Child onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
return new Child(v,mCallback);
}
#Override
public void onBindViewHolder(Child holder, int position) {
holder.setValue1(mChildren.get(position)*3);
holder.setValue2(mChildren.get(position));
}
#Override
public int getItemCount() {
return mChildren.size();
}
public class Child extends RecyclerView.ViewHolder{
View mView1;
View mView2;
private int mValue1;
private int mValue2;
public Child(View itemView, final MyRecyclerCallback mCallback) {
super(itemView);
mView1 = itemView.findViewById(R.id.view1);
mView2 = itemView.findViewById(R.id.view2);
mView1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCallback.onItemClicked(mValue1);
}
});
mView2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCallback.onItemClicked(mValue2);
}
});
}
public void setValue1(int position) {
mValue1 = position;
}
public void setValue2(int position) {
mValue2=position;
}
}
}
this a good alternative to handle onclick and onlongclick
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
You can have a single OnClickListener on your ViewHolder class and use it with an interface to determine the type of item which has been clicked.
For example, say you have a model class called Item :
public class Item {
private int itemType;
private String itemDescription;
private String optionalExtra;
public static final int ITEM_TYPE_1 = 1;
public static final int ITEM_TYPE_2 = 2;
public Item(int itemType, String itemDescription) {
this.itemType = itemType;
this.itemDescription = itemDescription;
}
public Item(int itemType, String itemDescription, String optionalExtra) {
this.itemType = itemType;
this.itemDescription = itemDescription;
this.optionalExtra = optionalExtra;
}
}
You have a custom interface defined to intercept click on items in recyclerview :
public interface CustomClickListener {
void onClickOfItemType1( int position );
void onClickOfItemType2( int position );
}
Inside your adapter for recyclerview, in the viewholder class :
//Similar implementation for other ViewHolders too.
public class ViewHolderType1 extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView imageView;
TextView textView;
View itemView;
public ViewHolderType1(View view) {
super(view);
this.itemView = view;
//initialize other views here
//Set item click listener on your view
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int itemPosition = getAdapterPosition();
if( itemPosition != RecyclerView.NO_POSITION ) {
customClickListener.onClickOfItemType1(itemPosition);
//You can call onClickOfItemType2(itemPosition) in your other type of view holder class
}
}
}
In your Activity or Fragment :
Pass the customClickListener as a parameter to your adapeter :
CustomAdapter customAdapter = new CustomAdapter(List<Item> itemList, new CustomClickListener {
#Override
void onClickOfItemType1(int position) {
Item item = itemList.get(position);
//This item is of type 1
//You can implement serializable / parcelable in your item class and use it to directly pass across item to your activity
Intent intent = new Intent(Activity.this, CustomActivity1.class);
intent.putExtra("item", item);
startActivity(intent);
}
#Override
void onClickOfItemType2(int position) {
Item item = itemList.get(position);
//This item is of type 2
//You can implement serializable / parcelable in your item class and use it to directly pass across item to your activity
Intent intent = new Intent(Activity.this, CustomActivity2.class);
intent.putExtra("item", item);
startActivity(intent);
}
});
In case you are trying to trigger different activities on click of different views; inside your viewclicklistener implementation check the view id and trigger the corresponding activity.
#Override
public void onClick(View v) {
int itemPosition = getAdapterPosition();
if( itemPosition != RecyclerView.NO_POSITION ) {
switch(v.getId()) {
case R.id.imageView :
customClickListener.onClickOfItemType1(itemPosition);
break;
case R.id.textView :
customClickListener.onClickOfItemType2(itemPosition);
break;
}
}
}
This is a guide for usage of RecyclerView :
https://guides.codepath.com/android/using-the-recyclerview