I have an array: Object[] array, and an array adapter that extends ArrayAdapter<Object>.
When i try to delete from it using adapter.remove(Object obj) i get an UnsupportedOperationException exception, just as this post.
The provided answers suggest using an ArrayList instead. This is not an option for me. I need to stick with array. So i copied the array to another array, but without the item i want to delete. Then i just did:
oldArray = newArray;
and then called adapter.notifyDataSetChanged();.
This works fine except it doesn't refresh the screen. I need to close then reopen the screen to see the changes. Shouldn't notifyDataSetChanged() do the refreshing?
edit:
Following #MD's answer, this is what i'm doing right now:
controller.onRemove(id);
public void onRemove(int id) {
int userListLength = usersArray.length;
int j = 0;
User[] newUserList = new User[userListLength-1];
for(int i=0; i<userListLength; i++)
{
if(id != usersArray[i].getId())
{
newUserList[j] = new User();
newUserList[j] = usersArray[i];
j++;
}
}
usersArray = newUserList;
//store in database
//...
view.getAdapter().refresh( usersArray );
}
public void refresh(User[] items)
{
this.userArray = items;
notifyDataSetChanged();
}
adapter construction:
adapter = new myUserAdapter( controller.getBaseContext(), R.layout.user_row, userArrayList);
usersListView.setAdapter( adapter );
and in myUserAdapter i have:
private User[] userArray;
Solution:
#MD's answer works. But I also had to override getCount() in the adapter:
#Override
public int getCount () {
return userArray.length;
}
It's explained in the accepted answer here.
i have a way
Add refresh method in your adapter:
public void refresh(List<String> items)
{
this.items = items;
notifyDataSetChanged();
}
and call from Activity like
yourAdapter.refresh(items); // items new arrayList or Array
ArrayAdapter simply wraps the List<T> you pass to its constructor and when you call remove(), it calls the remove() method of that list.
If you pass an array instead of a list to the constructor, it converts it with Arrays.asList().
/**
* Constructor
*
* #param context The current context.
* #param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
* #param objects The objects to represent in the ListView.
*/
public ArrayAdapter(Context context, int resource, T[] objects) {
init(context, resource, 0, Arrays.asList(objects));
}
In older Android versions Arrays.asList returns a readonly list. That's why you get the exception
You must explicitly create an ArrayList out of your array:
adapter = new myUserAdapter( controller.getBaseContext(), R.layout.user_row, new ArrayList<User>(Arrays.asList(userArrayList)));
Related
I'm making a memo app that uses a RecyclerView to show an ArrayList<Note> of data where Note is a data class for each item, and I save and retrieve them using another class called NotesController (using Gson). In order to update the list, what I do now is reassign a new value to the NotesAdapter (the RecyclerView Adapter) I've set to my list. Here's my code:
NotesController:
public class NotesController {
private ArrayList<Note> notesList;
private String notesPath;
private Gson gson = new Gson();
private Type type = new TypeToken<ArrayList<Note>>() {
}.getType();
public NotesController(String notesPath) {
this.notesPath = notesPath;
if (FileUtil.isExistFile(notesPath)) {
getNotesList();
} else {
createNewList();
}
}
/**
* Creates a new list if it doesn't exist. Internal class use only.
*/
private void createNewList() {
notesList = new ArrayList<>();
saveLatestData();
}
/**
* Reads the saved notes.json file and retrieves the ArrayList of items of class {#link Note}.
* #return An ArrayList<<h>Note</h>> containing all notes saved in file <b>notes.json</b>
*/
public ArrayList<Note> getNotesList() {
String json = FileUtil.readFile(notesPath);
notesList = gson.fromJson(json, type);
return notesList;
}
/**
* Saves latest changes to the list {#linkplain NotesController#notesList} to notes.json file. Internal class use only.
*/
private void saveLatestData() {
String json = gson.toJson(notesList, type);
FileUtil.writeFile(notesPath, json);
}
/**
* Adds an item of type {#link Note} to the list and saves data by calling {#link NotesController#saveLatestData()}.
* #param note The {#link Note} instance to get added.
*/
public void add(Note note) {
notesList.add(0, note);
saveLatestData();
}
/**
* Replaces an existing item with a new one of type {#link Note} in the list {#link NotesController#notesList} and saves data by calling {#link NotesController#saveLatestData()}.
* #param position The position of the item to get replaced.
* #param note The {#link Note} instance to replace the old item.
* #throws ArrayIndexOutOfBoundsException When position is out of {#link NotesController#notesList} range.
*/
public void set(int position, Note note) {
notesList.set(position, note);
saveLatestData();
}
/**
* Gets the {#link Note} item from the specified position.
* #param position The position of the item to return.
* #return The item at the position specified.
* #throws ArrayIndexOutOfBoundsException When position is out of {#link NotesController#notesList} range.
*/
public Note get(int position) {
return notesList.get(position);
}
/**
* Removes the {#link Note} item in the specified position from the list.
* #param position The position of the item to remove.
* #throws ArrayIndexOutOfBoundsException When position is out of {#link NotesController#notesList} range.
*/
public void remove(int position) {
notesList.remove(position);
saveLatestData();
}
/**
* Indexes the notes list for the given text and returns items that contain the query either in the title or the content.
* #param query The text query to search for (low cased).
* #return The notes whose title or content contains the query (all trimmed and low cased).
*/
public ArrayList<Note> search(String query) {
ArrayList<Note> results = new ArrayList<>();
for (Note note: getNotesList()) {
if (note.getTitle().trim().toLowerCase().contains(query.trim().toLowerCase()) || note.getContent().trim().toLowerCase().contains(query.trim().toLowerCase())) {
results.add(note);
}
}
return results;
}
/**
* Simple method to convert many int parameters to an int[] array.
* #param categories The varargs int[] array.
* #return int[] array from parameters.
*/
public int[] categories(int... categories) {
return categories;
}
}
MainActivity: (just the relevant codes)
public class MainActivity extends AppCompatActivity {
private NotesAdapter notesAdapter;
public static NotesController notesController;
private RecyclerView notesRecyclerView;
private String notesDir;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Answers(), new Crashlytics());
setContentView(R.layout.activity_main);
...
notesDir = ContextCompat.getDataDir(this).getPath() + "/files/notes.json";
notesController = new NotesController(notesDir);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
notesRecyclerView.setLayoutManager(layoutManager);
updateRecyclerView();
notesAdapter.setOnItemActionListener(new NotesAdapter.ActionListener() {
#Override
public void onItemClick(final int position, View v) {
...
}
#Override
public void onItemLongClick(final int position, View v) {
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this).setTitle("Delete?").setMessage("Just for testing.");
dialog.setPositiveButton("DELETE", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
notesController.remove(position);
updateRecyclerView();
}
});
dialog.show();
}
});
...
}
...
private void updateRecyclerView() {
notesAdapter = new NotesAdapter(notesController.getNotesList(), getApplicationContext());
notesRecyclerView.setAdapter(notesAdapter);
notesAdapter.notifyDataSetChanged();
}
}
Now looking at updateRecyclerView() method you see that I reassign the Adapter once again with the new data from the NotesController then notify the list that the data changed.
But I need somehow, without eliminating the controller, to make list make deletion animation when I delete (e.g. by long click) or add something (just the default one). And for that, Android RecyclerView Adapter class provides us with notifyItemInserted(int) and notifyItemRemoved(int) but they didn't work in my case (even with removing notifyDataSetChanged() which interrupts these animations).
Please don't suggest me to eliminate the NotesController as it helps accessing notes from different parts of the app easily, and I just need a way for those two insertion and deletion notifying methods to work without problems (any other solution is welcome btw).
Note: Here's my adapter type: public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder>.
You shouldn't create new instance of adapter whenever you want to update items in recyclerview. You should create one instance of adapter and assign it to recyclerview, if you want to add or remove items in recyclerview, you just need to replace data in adapter. I would recommend ListAdapter because it has function submitList(list) which can be easily use to update data.
Also if you want to achieve animations you can use this
I fixed the problem by using -as suggested by Milan Kundacina- the function submitList(list) instead of reassigning the adapter. But as this function comes only with ListAdapter while I'm using Adapter only, I've created my own one as follows (suuuuuper simple):
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NoteViewHolder> {
private ArrayList<Note> notesList;
...
NotesAdapter(ArrayList<Note> notesList) {
super();
this.notesList = notesList;
}
public void submitList(ArrayList<Note> list) {
this.notesList = list;
}
...
}
And used this way to update the list:
// inside MainActivity:
private void notifyListAdd(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemInserted(position);
}
private void notifyListRemove(int position) {
notesAdapter.submitList(notesController.getNotesList());
notesAdapter.notifyItemRemoved(position);
}
I want to get all items that contains the search input based on itemName. In c#, I can use lambda, but I could not find any references for android.
Here is the model class:
public class ModelItem {
public long itemId;
public String itemName;
public double price;
}
Here is my list:
public static ArrayList<ModelItem> items;
I will use the list to get the items. Thank you in advance.
Use below code
public void getAllItems(ArrayList<ModelItem> items, String searchItem) {
for(ModelItem item : items) {
if(item.getItemName().contains(searchItem)) {
// here you are getting item which matches inside your list
}
}
I think you have a listview with items. Now you want to filter them with a search string.
You have to implement Filterable in your custom adapter.
How to filter an adapter
First step, copy items into tempList
private ArrayList<ModelItem> items; // You have data into this list
private ArrayList<ModelItem> tempData = new ArrayList<>();
for (ModelItem item : items) {
tempData.add(item);
}
This is to filter items based on query
public void filter(String query) {
items.clear();
if (query.length() > 0) {
for (ModelItem currItem : tempData) {
// Add data into list, if item is having query string
if (currItem.getItemName().toLowerCase().contains(query)) {
mData.add(currItem);
}
}
} else {
// Adding all the items, if query is empty
for (ModelItem item : tempData) {
items.add(item);
}
}
notifyDataSetChanged(); // notify the changes, if you are using an adapter.
}
hey i got a example for your requirement in github, you need to use QueryTextListener in main class, then setFilter to adapter as given in example
please check this link:https://github.com/Wrdlbrnft/Searchable-RecyclerView-Demo
I am currently trying to add a value to an ArrayList object from a method inside of another class.
Here is the class I have created for the ArrayList Object:
public class ArrayClass {
public static ArrayList<String> array = new ArrayList<>();
public static void add_val(String s){
array.add(s);
}
public static int get_size(){
return array.size();
}
public static String get_val(int i){
return array.get(i);
}
}
And the other class where I attempt to edit the ArrayList object:
ArrayClass fill = new ArrayClass();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explore);
Response.Listener<String> responseListener4 = new Response.Listener<String>(){
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse4 = new JSONObject(response);
boolean success = jsonResponse4.getBoolean("success");
if (success){
int l;
String filled;
int length4 = jsonResponse4.length();
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.add_val(filled);
}
}else{
AlertDialog.Builder builder = new AlertDialog.Builder(ExploreActivity.this);
builder.setMessage("Could not retrieve restaurant tables filled")
.setNegativeButton("Retry", null)
.create()
.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
FilledRequest filledRequest = new FilledRequest(responseListener4);
RequestQueue queue4 = Volley.newRequestQueue(ExploreActivity.this);
queue4.add(filledRequest);
If you look in the onResponse method, you can see the attempt to add a value from the jsonResponse into the ArrayClass object. However, when I launch my app, it does not add the value into the object. I'm used to python global variables and not having to deal with the semantics of java, so if you could shed some light on what changes need to be made, I would greatly appreciate it.
Apart from other given answers/solutions to the issue you are facing, let me share a best and optimized way to implement JSON parsing in Android.
I would suggest you to check GSON or Jackson libraries which provides Java serialization/deserialization that can convert Java Objects into JSON and back.
There are some benefits it does provide, one of the main benefits is you do not need to implement parsing manually and less chances of mistakes in implementing parsing, like you may make a mistake in mentioning key "Success" or "success" or any such silly mistakes!
Firstly, since your variable is static, and the methods are static too, you don't have to instantiate the object. You could do something like this:
ArrayClass.add_val("Hello");
But if you want to instantiate then you can do this:
public class ArrayClass {
private ArrayList<String> array;
public ArrayClass() {
array = new ArrayList<>();
}
public void add_val(String s){
array.add(s);
}
public int get_size(){
return array.size();
}
public String get_val(int i){
return array.get(i);
}
}
To make sure the values are filled in, you can check the array size like this:
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.add_val(filled);
}
Log.d("TEST", String.valueOf(fill.get_size());
Remove all cases of the static keyword in ArrayClass. Static methods are class level methods, ie. are called on the class itself, rather than an instance of the class.
You can also try this, for ArrayList:
First do some changes in your ArrayClass. Use get And Set method to access your array.
public class ArrayClass {
private ArrayList<String> array = new ArrayList<>();
public ArrayList<String> getArray() {
return array;
}
public void setArray(ArrayList<String> array) {
this.array = array;
}
}
And your other class where you attempt to edit the ArrayList use getArray And SetArray method and some predefined method of ArrayList like this:
Store the data in ArrayList:
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.getArray().add(filled);
}
Get Size of ArrayList:
fill.getArray().size();
And also you can store an another ArrayList like
ArrayList<String> tempArrayList = new ArrayList<String>();
tempArrayList.add("string 1");
tempArrayList.add("string 2");
tempArrayList.add("string 3");
tempArrayList.add("string 4");
fill.setArray(tempArrayList)
I have a ListView with a custom ArrayAdapter with a custom object. The ListView contains headers. I loop through to get the header names on my custom objects. Some of them have the SAME value for headerTitle. I do NOT want them to get added to the row if that header already exists. This is my code (in my ArrayAdapter) to try and see if the same header has been added but it does absolutely nothing:
public static List<FriendsVideoLVModel> list = new ArrayList<FriendsVideoLVModel>();
#Override
public void add(FriendsVideoLVModel obj) {
super.add(obj);
for (int i=0; i <list.size(); i++) {
if (!obj.eventTitle.equals(list.get(i).eventTitle)) {
list.add(obj);
notifyDataSetChanged();
}
}
}
I have a ListView and ListView adapter. I am adding objects to the adapter but I only want to add one row with an object that contains a certain String. This is my code but it does not work:
public static List<FriendsVideoLVModel> list = new ArrayList<FriendsVideoLVModel>();
#Override
public void add(FriendsVideoLVModel obj) {
super.add(obj);
for (int i=0; i <list.size(); i++) {
if (!obj.eventTitle.equals(list.get(i).eventTitle)) {
list.add(obj);
notifyDataSetChanged();
}
}
}
Please help. The logic looks fine to me but it just does not work. Nothing is in fact added.