when i send some data it is give me this error java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1, i dont know why . i tried to search and i didnot foung any thing related to this error.
my code
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.book_items, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.onClick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, BookContentActivity.class);
intent.putExtra("title",bookList.get(viewHolder.getAdapterPosition()).getTitle());
intent.putExtra("author",bookList.get(viewHolder.getAdapterPosition()).getAuthor());
intent.putExtra("details",bookList.get(viewHolder.getAdapterPosition()).getDetails());
intent.putExtra("imageURL",bookList.get(viewHolder.getAdapterPosition()).getImgUrl());
intent.putExtra("rating",bookList.get(viewHolder.getAdapterPosition()).getRating());
intent.putExtra("pages",bookList.get(viewHolder.getAdapterPosition()).getPages());
intent.putExtra("downloadUrl",bookList.get(viewHolder.getAdapterPosition()).getDownloadURL());
context.startActivity(intent);
}
});
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.title.setText(this.bookList.get(position).getTitle());
holder.author.setText(this.bookList.get(position).getAuthor());
Glide.with(holder.itemView.getContext())
.load(this.bookList.get(position).getImgUrl())
.transforms(new CenterCrop(),new RoundedCorners(16))
.into(holder.imageBook);
holder.bookRating.setRating(this.bookList.get(position).getRating());
holder.pages.setText(this.bookList.get(position).getPages());
}
The click listener should be added where you're binding the View to the ViewHolder object (onBindViewHolder). For getting the that object and launching your next activity though, passing a custom listener interface to get the clicked object with would be better. As the adapter shouldn't be responsible for launching activities via intents.
Hopefully that helps.
Related
I am making an android app based on firebase Realtime database. I am reading the data in a RecyclerView. There are three items in my RecyclerView.
Text view = Name
Text view = Number
Button = Delete
When I delete some child from my RecyclerView three things can happen:
Sometimes it deletes the targeted child normally
Sometimes it deletes some other child (often which is below the targeted child.)
Sometimes my app crashes.
This is the code for my adapter:
public class holder extends RecyclerView.ViewHolder {
public Button btnDelete;
public TextView tvName;
public TextView tvRoll;
public holder(#NonNull View itemView) {
super(itemView);
btnDelete=(Button) itemView.findViewById(R.id.idDelete);
tvName=(TextView) itemView.findViewById(R.id.idName);
tvRoll=(TextView) itemView.findViewById(R.id.idRoll);
}
}
This is the code to show items in recycler view and code for delete button:
options = new FirebaseRecyclerOptions.Builder<basic>()
.setQuery(myRef, basic.class)
.build();
adapter = new FirebaseRecyclerAdapter<basic, holder>(options) {
#SuppressLint("SetTextI18n")
#Override
protected void onBindViewHolder(#NonNull holder holder, final int i, #NonNull final basic basic) {
holder.tvName.setText(basic.getFb01name());
holder.tvRoll.setText(basic.getFb04roll());
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myRef.child(getRef(i).getKey()).removeValue();
}
});
}
#NonNull
#Override
public holder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.items, parent, false);
return new holder(v);
}
};
//------------------------------
adapter.startListening();
Userlist.setAdapter(adapter);
//-----------------------------------
I cannot guess where is the problem.
Please also provide a practical solution.
This is the crash report:
2020-11-30 23:27:45.968 10796-10796/com.my App Name E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.my App Name, PID: 10796
java.lang.IndexOutOfBoundsException: Index: 4, Size: 4
at java.util.ArrayList.get(ArrayList.java:437)
at com.firebase.ui.common.BaseObservableSnapshotArray.getSnapshot(BaseObservableSnapshotArray.java:70)
at com.firebase.ui.database.FirebaseRecyclerAdapter.getRef(FirebaseRecyclerAdapter.java:112)
at com.my App Name.ViewActivity$1$1.onClick(ViewActivity.java:72)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2020-11-30 23:27:46.701 1467-10685/com.google.android.googlequicksearchbox E/sb.v.u.LiteSuggestSourc: On-device lite suggest model loading error.
2020-11-30 23:27:46.818 1467-1774/com.google.android.googlequicksearchbox E/MDD: DownloadProgressMonitor: Can't find file group for uri: android://com.google.android.googlequicksearchbox/files/sharedminusonemodule/shared/SharedMinusOneData.pb.tmp
It seems that when you access Firebase getRef(i) within the button onClick callback, it can reference a wrong position i causing IndexOutOfBoundsException
So, we'll get the key outside of the callback as below:
options = new FirebaseRecyclerOptions.Builder<basic>()
.setQuery(myRef, basic.class)
.build();
adapter = new FirebaseRecyclerAdapter<basic, holder>(options) {
#SuppressLint("SetTextI18n")
#Override
protected void onBindViewHolder(#NonNull holder holder, final int i, #NonNull final basic basic) {
holder.tvName.setText(basic.getFb01name());
holder.tvRoll.setText(basic.getFb04roll());
String refKey = getRef(i).getKey(); // <<<<<< Here is the change
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myRef.child(refKey)).removeValue();
}
});
}
#NonNull
#Override
public holder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.items, parent, false);
return new holder(v);
}
};
How can I add an Item Click Listener for my `RecyclerView.Adapter'
when the user clicks on the Card View item, Data sent to the PostContent Fragment?
Also, is it possible to send the data from this adapter to the new fragment using intent?
Please note my code:
public class PostDataAdapter extends RecyclerView.Adapter<PostDataAdapter.MyViewHolder> {
private List<PostData> PostDataList ;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView vPostContent, vPostDate, vPostAuthor, vPostTitr,VPostLikes,VPostViews;
public ImageView vPostPhoto;
public MyViewHolder(View v) {
super(v);
vPostContent = v.findViewById(R.id.PostContentTv);
vPostDate = v.findViewById(R.id.PostDateTv);
vPostAuthor = v.findViewById(R.id.PostAuthorTv);
vPostTitr = v.findViewById(R.id.PostTitrTv);
vPostPhoto = v.findViewById(R.id.PostPhoto);
VPostLikes=v.findViewById(R.id.PostLikeTv);
VPostViews=v.findViewById(R.id.PostViewTv);
}
}
public PostDataAdapter(List<PostData> postDataList) {
PostDataList = postDataList;
}
#Override
public PostDataAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_posts, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostDate.setText(PostDataList.get(position).getPostDate());
holder.vPostTitr.setText(PostDataList.get(position).getPostTitr());
holder.vPostContent.setText(PostDataList.get(position).getPostContent());
holder.vPostAuthor.setText(PostDataList.get(position).getPostAuthor());
holder.VPostViews.setText(PostDataList.get(position).getPostViews());
holder.VPostLikes.setText(PostDataList.get(position).getPostLikes());
new DownloadImageTask(holder.vPostPhoto).execute(PostDataList.get(position).getImgpost());
}
#Override
public int getItemCount() {
return PostDataList.size();
}
}
To add a ItemCLickListener for RecyclerView, you need to implement a custom Interface which the Fragment will implement. When the list item is clicked, then the callback function of the interface is called.
CustomItemClickListener.java:
public CustomItemClickListener {
void onItemClick(Object data);
}
Just add these to the PostDataAdapter:
PostDataAdapter.java:
private CustomItemClickListner clickListener;
public PostDataAdapter(CustomItemClickListner listener, List<PostData> postDataList) {
PostDataList = postDataList;
clickListener = listener
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Modify the parameters of the function according to what you want to send to the fragment
// As soon as this is called, the `onItemClick` function implemented in the Fragment gets called.
clickListener.onItemClick(Object data);
}
});
}
Fragment.java:
CustomFragment extends Fragment implements CustomItemClickListener {
public CustomFragment() {
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view;
PostDataAdapter adapter = new PostDataAdapter(this, new ArrayList<PostData>)
return view;
}
#Override
public void onItemClick(Object data) {
// Handle the data sent by the adapter on item click
}
}
Yu cand send data from Adapter to a Fragment with Intent:
Fragment fragment = new tasks();
FragmentManager fragmentManager = context.getSupportFragmentManager(); // this is the context of the Activity
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Bundle bundle=new Bundle();
bundle.putString("name", "Osmar Cancino"); //key and value
//set Fragmentclass Arguments
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.content_frame, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Although my suggestion is to manage the flow of screens from the parent activity, and manage the data through a Callback, even with a custom Interface
There are two ways you can do this.
Write recyclerview.onitem touchlistener(...). Then consume that event in your fragment. As you will get item position inside touchlistener callback, you can take out data from your list directly from the list you passed to your adapter (Assuming you have list reference outside in your fragment.)
Oobserver pattern.
Define a functional interface (one callback method with required parameters of the data you want to pass) implement inside your fragment. Send its reference with the constructor of adapter. Then Store reference in a interface type variable inside adapter. Write click listener on card. And on the card click, invoke method using interface type variable.
Intents can be used to send data to new activities but not fragments You'd have to use the Fragment Manager and attach a bundle to it to send data. You can refer to the documentation here on how to do so:
https://developer.android.com/training/basics/fragments/communicating#Deliver
To handle click on cards, you can create a listener when you create PostDataAdapter. Refer to the following link for a simple example:
https://stackoverflow.com/a/40584425/4260853
for adding click item for a Cardview, you can find the Cardview in MyViewHolder class by id and in onBindViewHolder set a click listerner for it like the following
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.vPostCardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//write your codes here
}
});
}
if you have an intent that you want to send it's data to a fragment, you can get the intent data and send them with bundle to your fragment. for example do something like the following.
Bundle bundle = new Bundle();
bundle.putString("your_key",intent.getStringExtra("your_item_key_in_intent"));
and after that send bundle to your fragment with
fragment.setArguments(bundle);
Question:
I'm adding views into a recyclerview with a click. When I click a view it opens a DialogFragment, how do I remove that view through the DialogFragment (by clickign on a button inside it)?
Adapter:
public class SubjectsAdapter extends RecyclerView.Adapter<SubjectsAdapter.ViewHolder> {
public List<String> items = new ArrayList<>();
public Activity mcontext;
public SubjectsAdapter(Activity context) {
this.mcontext=context;
}
public void addItem(String name) {
items.add(name);
notifyItemInserted(items.size() - 1);
}
public void removeItem(int position) {
items.remove(position);
notifyItemRemoved(position);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.requestFocus();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setButtonName(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
int i = 100;
public EditText EditName;
class ViewHolder extends RecyclerView.ViewHolder{
public Button GridButton;
public SharedPreferences prefs;
public ViewHolder(View itemView) {
super(itemView);
GridButton = (Button) itemView.findViewById(R.id.grid_button);
EditName = (EditText) itemView.findViewById(R.id.editName);
ClassName = (TextView) itemView.findViewById(R.id.ClassName);
prefs = mcontext.getPreferences(Context.MODE_PRIVATE);
GridButton.setId(++i);
EditName.requestFocus();
//Showing the DialogFragment
GridButton.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Fragment_Subject_Edit editFragment = Fragment_Subject_Edit.newInstance();
Bundle data = new Bundle();
data.putInt("ID", v.getId());
editFragment.setArguments(data);
editFragment.show(mcontext.getFragmentManager(), "Title");
return false;
}
});
}
public void setButtonName(String buttonName) {
GridButton.setText(buttonName);
}
}
}
Adding views in the activity:
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
final SubjectsAdapter adapter = new SubjectsAdapter(this);
recyclerView.addItemDecoration(new SampleItemDecoration());
recyclerView.setAdapter(adapter);
recyclerView.setItemViewCacheSize(15);
recyclerView.setNestedScrollingEnabled(false);
#Override
public void onClick(View v) {
adapter.addItem(prefs.getString("key1", null));
}
What I have from Lucas Crawford answer, although I'm not getting it right:
1:
public Activity mcontext;
public View.OnLongClickListener LongClicking;
public SubjectsAdapter(Activity context, View.OnLongClickListener longClick) {
this.mcontext = context;
this.LongClicking = longClick;
}
2:
View.OnLongClickListener LongClicker;
...
...
...
adapter = new SubjectsAdapter(this, LongClicker);
recyclerView.setAdapter(adapter);
3:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.grid_item_button, parent, false);
view.setOnLongClickListener(LongClicking);
return new ViewHolder(view);
}
4:
fm = getFragmentManager();
ClassEditor = new Fragment_Subject_Edit();
LongClicker = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Bundle data = new Bundle();
data.putInt("ID", v.getId());
ClassEditor.setArguments(data);
ClassEditor.show(fm, "Title");
return false;
}
};
Nothing happens when I longClick the button, nor anywhere in the view, what's wrong with these steps, and how to do number 5?
Easy Solution
Instead of assigning the onLongClickListener in the ViewHolder, I would assign the click listener as part of the constructor in the adapter's creation. This way, when you create the view holder in onCreateViewHolder, you give the new view for the view holder the adapter's passed click listener. This is better since it decouples the click event from the ViewHolder's work, as well as letting the activity that created the adapter handle what happens when clicked. (also the dialog fragment created is tied to an Activity lifecycle, why not create it there!).
Next, add a click listener to the dialog fragment's creation, possible as a member variable with a getter/setter. The button in the fragment is then assigned that click listener when you are inflating the dialog fragments view. This way, you do all the click event listening within your Activity, and use the same strategy in both the adapter and dialog fragment.
Can provide code IF required. I hope that makes sense.
Here is a list of what I suggested:
Add a click listener to the constructor of the recycler view adapter.
Pass an onLongClickListener object when you are creating the adapter within your activity.
Assign your view the click listener passed to the adapter in onCreateViewHolder.
Within the click listener created in your activity for the adapter, create the dialog fragment and assign a NEW click listener to handle the button press you desire. This click listener also is apart of the Activity.
When inflating the views in the dialog fragment, give the target button the listener you assigned to the dialog fragment.
The logic of removing the view (or item) from the RecyclerView adapter would be in the click listener you assign to the new dialog fragment. You need a reference to the item currently being removed as well (which you do via arguments).
Advanced Solution
A more advanced solution would be to use something like EventBus which handles listening for events. It cleans up a lot of code to use this. Another one is Otto by Square. It does the same thing, and I personally use Otto for event driven listening rather than passing around click listeners. You can decouple the need for a click listener being passed to your recycler's adapter by just setting a click listener in the adapter that posts an event that your activity is listening for, which then triggers the dialog fragment creation. The dialog fragment would then do the same thing by creating and assigning a new listener within the fragment that posts another event that your activity is listening for related to removing the particular adapter item.
My Android App "E-Mail" crashes when I try to open the "ReadActivity".
The method OpenMail(), declared in MainActivity, should start ReadActivity:
public void OpenMail(View v, int index) {
String[] mail = {"x", "y", "z"};
Intent readIntent = new Intent(this, ReadActivity.class); <-- Error occurs here
readIntent.putExtra("mail", mail);
startActivity(readIntent);
}
The method is called in the ListViewAdapter:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
Holder holder = new Holder();
View rowView = inflater.inflate(R.layout.listview_item, null);
holder.imageView = (ImageView) rowView.findViewById(R.id.imageView);
holder.txvSenderMail = (TextView) rowView.findViewById(R.id.txvSender);
holder.txvSubject = (TextView) rowView.findViewById(R.id.txvSubject);
holder.imageView.setImageResource(ImageID[position]);
holder.txvSenderMail.setText(SenderMail[position]);
holder.txvSubject.setText(Subject[position]);
rowView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new MainActivity().OpenMail(v, position);
}
});
return rowView;
}
The error: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference
I hope anybody can help me!
Thank you!
You are actually trying to create new MainActivity when you do
new MainActivity().OpenMail(v, context, position);
I think you wanted to do something like this, if you are currently at MainActivity class:
getActivity().OpenMail(v, context, position);
Of course, dont forget to add ReadActivity to AndroidManifest
I have a web api that returns a list of user posts that includes the user's name, username, image, post, comments etc.
In my Android app, I have a ListView which contains these user posts. I used the ViewHolder patterns in the adapter to set this up.
Now inside every ListView item, I need to setup click listeners on the post, the userimage, name, comment etc. In the listener I need to get position of the ViewHolder so as to get the corresponding Object from my data. How do I do this?
Whenever you first inflate a view, in your adapter, where convertView == null, inflate your view row, then call findViewById on the new inflated view to get your child views and set up click listeners on them individually. A handy way to store the position that I use for my app is to get a view, and call setId() on the view, with the position. Then you can call getId() on the view when you need the position.
You can also define your own listener(s) in your adapter with your post (or its id/position) for argument. For example:
public interface OnPostClickListener {
void onUserImageClick(Post post);
void onUserNameClick(Post post);
...
}
private OnPostClickListener mListener;
public void setOnPostClickListener(OnPostClickListener listener) {
mListener = listener;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Post post = getItem(position);
...
holder.userImage.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
if(mListener != null)
mListener.onUserImageClick(post);
}
});
holder.userName.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
if(mListener != null)
mListener.onUserNameClick(post);
}
});
...
}
And in your Activity:
mAdapter.setOnPostClickListener(new OnPostClickListener(){
#Override
public void onUserImageClick(Post post) {
// Do something
}
#Override
public void onUserNameClick(Post post) {
// Do something
}
});