I am currently creating an application which consists of a Fragment, an Adapter for a RecyclerView and a RecyclerView which is accessed through the fragment. I need data to be passed from the RecyclerView back to the fragment however am unable to do so as the Fragment isn't identified through the Intent. When the user selects an item from the RecyclerView this item should be then passed through to the fragment. I have the RecyclerView using onBackPressed() in order to navigate back to the fragment which works fine, however no data seems to pass. Please see below what I currently have:
CustomAdapter.java
private OnItemClickListener mOnItemClickListener;
public interface OnItemClickListener{
public void onItemClick(View view, int position);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, int position) {
holder.text1.setText(String.valueOf(text1.get(position)));
holder.text2.setText(String.valueOf(text2.get(position)));
holder.text3.setText(String.valueOf(text3.get(position)));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, holder.getAdapterPosition());
}
});
}
InputToFragment.java
#Override
public void onItemClick(View view, int position) {
// Need to pass data through to Fragment, however unable to do so as it's not identified
// within the Intent
onBackPressed();
}
});
Fragment.java
// Nothing related to Passing data within the Fragment
Its for your fragment
YourFragment : Fragment() {
OnItemClickListener listener = new OnItemClickListener(){
#Override
public void onItemClick(View view, int position){
//this called if item at adapter clicked
}
}
....
//whatever lifecycle you choose for initialize adapter
onCreateView(){
adapter = new YourAdapter(listener);
}
public interface OnItemClickListener{
public void onItemClick(View view, int position);
}
}
and this for your adapter
private OnItemClickListener listener;
YourConstructorAdapter(OnItemClickListener listener){
this.listener = listener;
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, int position) {
holder.text1.setText(String.valueOf(text1.get(position)));
holder.text2.setText(String.valueOf(text2.get(position)));
holder.text3.setText(String.valueOf(text3.get(position)));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// this code calling method from fragment
listener.onItemClick(v, position);
}
});
I'm highly recommend migrate to kotlin and use higher-order function, its very simple.
You can use interface, here is how to do it
create interface to handle click:-
public interface OnCustomItemSelectListener {
void onCustomItemSelected(String data);
}
not extend it in your fragment or activity:-
public class MyActivity implements OnCustomItemSelectListener {
#Override
public void onCustomItemSelected(String data) {
/*store it if neede */
/* additional code */
}
}
In recycler view pass this activity to recycler view constructor and inside your custom adapter you can assign this activity to listener interface and call the onCustomItemSelected method from overridden onClick(), which will be in your previous activity or fragment.
OnCustomItemSelectListener itemSelectListener;
public CustomAdapter(Activity listenerAct, .....){
this.itemSelectListener = listenerAct;
}
#Override
public void onClick(View view)
{
itemSelectListener.onCustomItemSelected(data);
}
this will pass data to onCustomItemSelected() method in your fragment/activity
Related
Good day, I want to pass position from RecyclerView's adapter to MainActivity, so I design a interface:
in RecyclerViewAdapter:
publuc interface recyclerViewAdapterListener{
void onDeviceConnect(data, position)
}
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
//here I call onDeviceConnect
holder.button.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
listener.onDeviceConnect(data, position);
}
in MainActicity:
private int pos;
public void onDeviceConnect(data,position){
pos = position;
}
I know the problem is pos = position, that will be always get the last position.
but I need data and position from RecyclerView, because when my device connected,
It will change RecyclerView's item.
ex : (in MainActivity)
case BleService.ACTION_NOTIFY_ON: //here my device is connected.
updateStatus(mac,status); //update status
private void updateStatus(mac,status){
Adapter.updateItem(data, pos); //call adapter's item
}
ex : (in RecyclerViewAdapter)
public void updateItem(data,pos){
if (dataList.size() != 0){
dataList.set(pos, data);
notifyItemChanged(pos);
}
is any good idea can fix it , Thanks.
in MainActicity:
`
public class MainActicity extends AppCompatActivity implements YourRecyleAdaptor.recyclerViewAdapterListener{
private int pos;
#Override
public void onDeviceConnect(data,position){
pos = position;
}
`
Try to replace the position with holder.getAdapterPosition()
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
//here I call onDeviceConnect
holder.button.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
listener.onDeviceConnect(data, holder.getAdapterPosition());
}
Or you can also setOnClickListener in the constructor of the ViewHolder class
I'm new in android and I have just screwed something.
I've used recycleView template and it worked fine but I wanted to change type of my object from string to "TagsManagerObject" which contains
private String tagName;
private String gender;
private String mAgeMin;
private String mAgeMax;
private String mDistance;
since then I'm stuck with this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.tinderapp, PID: 10921
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.tinderapp.Tags.TagsManagerAdapter$ItemClickListener.onDeleteClick(int)' on a null object reference
at com.example.tinderapp.Tags.TagsManagerAdapter$ViewHolder$1.onClick(TagsManagerAdapter.java:78)
at android.view.View.performClick(View.java:6614)
at android.view.View.performClickInternal(View.java:6587)
at android.view.View.access$3100(View.java:787)
at android.view.View$PerformClick.run(View.java:26122)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6831)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:927)
My adapter looks like:
public class TagsManagerAdapter extends RecyclerView.Adapter<TagsManagerAdapter.ViewHolder>{
private LayoutInflater mInflater;
private ImageView mDeleteImage;
private List<TagsManagerObject> mTagsManagerObject;
private ItemClickListener mItemClickListener;
// data is passed into the constructor
public TagsManagerAdapter(Context context,List<TagsManagerObject> TagsManagerObject) {
this.mInflater = LayoutInflater.from(context);
this.mTagsManagerObject = TagsManagerObject;
}
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_tags_manager, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
// String tag = TagsManagerObject.get(position);
holder.tagName.setText("#"+mTagsManagerObject.get(position).getTagName());
holder.gender.setText(mTagsManagerObject.get(position).getGender());
holder.distance.setText(mTagsManagerObject.get(position).getmDistance());
holder.tagAge.setText(mTagsManagerObject.get(position).getmAgeMin() + "-" + mTagsManagerObject.get(position).getmAgeMax());
}
// binds the data to the TextView in each row
// total number of rows
#Override
public int getItemCount() {
return mTagsManagerObject.size();
}
public void setClickListener(ItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView tagName,gender,tagAge,distance;
ViewHolder(View itemView) {
super(itemView);
tagName= itemView.findViewById(R.id.tag);
gender = itemView.findViewById(R.id.tag_gender);
distance = itemView.findViewById(R.id.tag_distance);
tagAge = itemView.findViewById(R.id.tag_age);
mDeleteImage = itemView.findViewById(R.id.tag_delete);
itemView.setOnClickListener(this);
mDeleteImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if(position!=RecyclerView.NO_POSITION){
mItemClickListener.onDeleteClick(position); <<--- HERE IS THE ERROR
}
}
});
}
#Override
public void onClick(View view) {
if (mItemClickListener != null) mItemClickListener.onItemClick(view, getAdapterPosition());
}
}
// convenience method for getting data at click position
String getItem(int id) {
return mTagsManagerObject.get(id).toString();
}
// allows clicks events to be caught
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
void onDeleteClick(int position);
}
}
So when i click delete button it crashes. Does anyone knows why?
Thanks.
You got this exception as you you call onDeleteClick(position) on an object that is null, so you have to set a value to the mItemClickListener before calling this method onDeleteClick(position)
In your code, you have to call setClickListener() of your adapter in your activity/fragment that uses this adapter.
You need to do two things.
First, you need to check mItemClickListener for null. Doing this will prevent from null pointer exception.
mDeleteImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
if(position!=RecyclerView.NO_POSITION){
if(mItemClickListener !=null){
mItemClickListener.onDeleteClick(position); <<--- HERE IS THE ERROR
}
}
}
}
);
And the second thing is you should set/call the setClickListener for the Activity/Fragment, using the instance of RecyclerviewAdapter.
Doing this will make the Activity/Fragment implement your ItemClickListener and onDeleteClick() method.
adapter.setClickListener(this);
Or, you can also use the Anonymous class here.
Thanks guys for help. The problem was that in my acvitivy I have:
1.Created adapter with empty taglist
2.set onclicklistener
adapter.setOnItemClickListener(new TagsManagerAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
}
#Override
public void onDeleteClick(int position) {
System.out.println("works?");
removeItem(position);
}
});
And then add items.
Instead
adapter.notifyDataSetChanged();
Ive used:
adapter = new TagsManagerAdapter(myTagsList);
and that caused problem, that it coudnt find my listener
Am trying to navigate from one fragment (A) to another (B), but the fragment, but the first fragment (A) has a recyclerView meaning when I click on any Item I should navigate to the next one. Am using android Navigation component but I couldn't resolve the method findNavController(xxx) since it requires the ApplicationContext of the fragment. , because I tried v.getContext(), v.getApplicationContext(), mContext, but there wasn't luck.
How can I resolve this issue, below is the onBindViewHolder() in the RecyclerView Adapter class. ?
What could be the best way to reslve this
#Override
public void onBindViewHolder(#NonNull final CoordinatesViewHolder holder, int position) {
final Coordinates coord = mCoordinates.get(position);
holder.place_name.setText(coord.getmUPlaceName());
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavHostFragment.findNavController(xxx).navigate(R.id.action_bookmarking_to_weatherFragment);
}
});
}
It is not the responsibility of RecyclerView's adapter to redirect to another fragment.
Create interface like
public interface OnItemClickListener {
void onItemClicked(int position)
}
Inside your RecyclerView's adapter add method:
public class YourAdapterName extend RecyclerView.Adapter...
private OnItemClickListener onItemClickListener
void setOnItemClickListener(OnItemClickListener listener) {
onItemClickListener = listener
}
...
#Override
public void onBindViewHolder(#NonNull final CoordinatesViewHolder holder, int position) {
final Coordinates coord = mCoordinates.get(position);
holder.place_name.setText(coord.getmUPlaceName());
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) {
onItemClickListener.onItemClicked(position)
}
}
});
}
In your fragment with recycler, in place where you set adapter add code:
YourAdapterClassName adapter = new YourAdapterClassName(...init adapter...)
adapter.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClicked(int position) {
//Navigate here
}
})
yourRecyclerName.setAdapter(adapter)
Hope it'll help )
I have an activity that has two different recyclerviews that use two different adapters. Is there a way for the recyclerviews to communicate with each other?
For example:
userChoiceRecyclerView(second recyclerview) has a bunch of cardview objects that display text
displayRecyclerView(first recyclerview) has no information until you click a cardview object in userChoiceRecyclerView, and when you click an object in displayRecyclerView it will remove the cardview object in this recycler.
public class AnswerRecyclerViewAdapter extends RecyclerView.Adapter<AnswerViewHolder> {
private Context mContext;
private char[] mCharacterNameToChar;
public AnswerRecyclerViewAdapter(Context context, String name) {
mContext = context;
mCharacterNameToChar = name.toUpperCase().toCharArray();
}
public void answerUpdateName(String name) {
mCharacterNameToChar = name.toUpperCase().toCharArray();
notifyDataSetChanged();
}
#Override
public AnswerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
...
return viewHolder;
}
#Override
public void onBindViewHolder(AnswerViewHolder holder, int position) {
holder.mTextView.setText(...);
holder.mCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// removes the cardviews that was updated from the
// userChoiceRecyclerView
}
});
}
#Override
public int getItemCount() {
return mCharacterNameToChar.length;
}
}
The userChoiceRecyclerView is basically the same as this recyclerview except that it already has information in cardviews. I was wondering how I can make the recyclers communicate with each other
Firs of all you will need to add a Clicklistener for every object inside for ReRecyclerView .
you will need to add this code in your adapters :
private static MyClickListener myClickListener;
.
.
public void setOnItemClickListener(MyClickListener myClickListenerHolder) {
myClickListener = myClickListenerHolder;
}
.
.
public interface MyClickListener {
void onItemClick(int position, View v);
}
and in your cardView ClickListener add :
holder.mCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// removes the cardviews that was updated from the
myClickListener.onItemClick(position,view);
}
});
now at MainActivity.java you can control your objects clicks by adding them in onResume at your main .
example:
RecyclerView.Adapter SecondAdapter, displayRecyclerAdapter;
RecyclerView displayRecyclerView= (RecyclerView) findViewById(R.id.my_recycler_view);
.
.
#Override
protected void onResume() {
super.onResume();
((AnswerRecyclerViewAdapter) SecondAdapter).setOnItemClickListener(new AnswerRecyclerViewAdapter
.MyClickListener() {
#Override
public void onItemClick(int position, View v) {
//from here you can send data to your other RecyclerView like this
displayRecyclerAdapter = new displayRecyclerAdapterClass(Context,SomeString);
displayRecyclerView.setAdapter(displayRecyclerAdapter);
}
});
}
i hope this could help you ^_^
I have fragment with ListView, say MyListFragment, and custom CursorAdapter.
I'm setting onClickListener in this adapter for the button in the list row.
public class MyListAdapter extends CursorAdapter {
public interface AdapterInterface {
public void buttonPressed();
}
...
#Override
public void bindView(final View view, final Context context, final Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
...
holder.button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// some action
// need to notify MyListFragment
}
});
}
}
public MyListFragment extends Fragment implements AdapterInterface {
#Override
public void buttonPressed() {
// some action
}
}
I need to notify fragment when the button is pressed. How to invoke this interface?
Help, please.
Make a new constructor and an instance variable:
AdapterInterface buttonListener;
public MyListAdapter (Context context, Cursor c, int flags, AdapterInterface buttonListener)
{
super(context,c,flags);
this.buttonListener = buttonListener;
}
When the Adapter is made, the instance variable will be given the proper reference to hold.
To call the Fragment from the click:
public void onClick(View v) {
buttonListener.buttonPressed();
}
When making the Adapter, you will have to also pass your Fragment off to the Adapter. For example
MyListAdapter adapter = new MyListAdapter (getActivity(), myCursor, myFlags, this);
since this will refer to your Fragment, which is now an AdapterInterface.
Keep in mind that on orientation of the Fragment changes, it will most likely be recreated. If your Adapter isn't recreated, it can potentially keep a reference to a nonexistent object, causing errors.
Using Eventbus:
Examples:
https://github.com/kaushikgopal/RxJava-Android-Samples/tree/master/app/src/main/java/com/morihacky/android/rxjava/rxbus
or
https://github.com/greenrobot/EventBus
Using Interfaces:
I understand the current answer but needed a more clear example. Here is an example of what I used with an Adapter(RecyclerView.Adapter) and a Fragment.
Create Callback Interface:
public interface AdapterCallback {
void onMethodCallback();
}
Passing in Callback/Fragment:
This will implement the interface that we have in our Adapter. In this example, it will be called when the user clicks on an item in the RecyclerView.
In your Fragment:
public class MyFragment extends Fragment implements AdapterCallback {
private MyAdapter mMyAdapter;
#Override
public void onMethodCallback() {
// do something
}
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mMyAdapter = new MyAdapter(this); // this class implements callback
}
}
Use the Callback in your Adapter:
In the Fragment, we initiated our Adapter and passed this as an argument to the constructer. This will initiate our interface for our callback method. You can see that we use our callback method for user clicks.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(AdapterCallback callback) {
this.mAdapterCallback = callback;
}
#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();
}
});
}
}
or Use the Fragment in your Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private AdapterCallback mAdapterCallback;
public MyAdapter(Fragment fragment) {
try {
this.mAdapterCallback = ((AdapterCallback) fragment);
} catch (ClassCastException e) {
throw new ClassCastException("Fragment 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) {
try {
mAdapterCallback.onMethodCallback();
} catch (ClassCastException exception) {
// do something
}
}
});
}
}
Follow the 2 steps below for receive callback from Adapter in Fragment (or Activity)
First: In your Adapter
public class ListAdapter extends RecyclerView.Adapter < RecyclerListAdapter.ItemViewHolder > {
...
private ListAdapterListener mListener;
public interface ListAdapterListener { // create an interface
void onClickAtOKButton(int position); // create callback function
}
public RecyclerListAdapter(Context mContext, ArrayList < Items > listItems, ListAdapterListener mListener) { // add the interface to your adapter constructor
...
this.mListener = mListener; // receive mListener from Fragment (or Activity)
}
...
public void onBindViewHolder(final ItemViewHolder holder, final int position) {
holder.btnOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// use callback function in the place you want
mListener.onClickAtOKButton(position);
}
});
...
}
...
}
Second: In your Fragment (or Activity), there are 2 ways for implement callback method
Way 1
public MyListFragment extends Fragment {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
...
ListAdapter adapter = new ListAdapter(getActivity(), listItems, new ListAdapter.ListAdapterListener() {
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
});
...
}
}
Way 2
public MyListFragment extends Fragment implements ListAdapter.ListAdapterListener {
...
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
ListAdapter adapter = new ListAdapter (getActivity(), listItems, this);
...
}
#Override
public void onClickAtOKButton(int position) {
Toast.makeText(getActivity(), "click ok button at" + position, Toast.LENGTH_SHORT).show();
}
}
This is very similar to the way an activity and a fragment should communicate. In the constructor of your adapter, pass a reference of your fragment, cast it to your interface and just call yourReference.buttonPressed() on your onClick method.
a solution for NPE is first to make conctractor in your Fragment like that
public MyFragment MyFragment(){
return this;
}
then initialize your listener is adapter like that
Lisener lisener = new MyFragment();
Make a constructor like that:
public MyAdapter(Activity activity,AlertMessageBoxOk alertMessageBoxOk) {
this.mActivity = activity;
mAlertMessageBoxOk = alertMessageBoxOk;
}
call the interface from adapter use any event
mAlertMessageBoxOk.onOkClick(5);
after that implement AlertMessageBoxOk interface to your fragment like this,
class MyFragment extends Fragment implements AlertMessageBoxOk {
#Override
public void onOkClick(int resultCode) {
if(resultCode==5){
enter code here
}
}
}