Working on a project and have hit a mental wall trying to use a custom ArrayAdapter
to display only unique elements from a class. I have a Class I've created that is simply made up of 3 strings. SongName, Category and Artist.
With that class I have created an ArrayList
in my MainActivity
. I would like to use that ArrayList
in a few different activities. I've been able to use it in an ArrayAdapter
that I created that displays all 3 fields in TextViews
but I would like to create another ArrayAdapter
for a different activity that only displays 1 of those fields and doesn't show the repeating items. So, for example, an ArrayAdapter
that is only provided Category element and only displays the unique ones.
I'm having a hard time wrapping my head around how to only pass 1 element from the ArrayList
to ArrayAdapter
and make sure it doesn't display duplicates.
Fairly new to the android development and haven't been able to find an answer to this, so would appreciate it if you could help me or point me in the right direction, I would greatly appreciate it.
Edit Answer: was able to get it to work by adding this:
ArrayList<Music> Library = MainActivity.music;
ArrayList<String> artists = new ArrayList<String>();
Log.i("MainActivity", String.valueOf(Library.size()));
for (int i = 0; i<Library.size(); i++){
String artist = Library.get(i).getArtist();
if (!artists.contains(artist))
artists.add(artist);
Log.i("MainActivity", "artist");
}
Edit: including screenshot of the original ArrayList below. As you can see in my idea some of the categories and artists would repeat. I would like to create an activity that only displays the artists and another that only displays the categories. With the caveat being I only want the unique values to be displayed not duplicates & including github link.
By default the ArrayAdapter
will create views for each array item in your ArrayList
. To customise how the ArrayAdapter
create views for your data you need to extend it and override its getView()
method and use a different layout for each item like this
NOTE: Im assuming Music
is that class which has those 3 strings and you have created a new layout called unique_layout.xml
which has only one TextView
to display one you those strings.
public class CustomArrayAdapter extends ArrayAdapter<Music> {
public CustomArrayAdapter(Context context, ArrayList<Music> musicList) {
super(context, 0, users);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Music music = getItem(position);
// Here use that new layout that you created
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.unique_layout, parent, false);
}
// Assign text to the TextView from that new layout
TextView category = (TextView) convertView.findViewById(R.id.category);
category.setText(music.category);
return convertView;
}
}
UPDATE: Remove Duplicates from ArrayList
NOTE: The following methods removes duplicates and put the resulting list in a new ArrayList<String>
which you can use as source of your ArrayAdapter
. So you don't have to extend ArrayAdapter
now.
There are 3 common ways to remove duplicates from ArrayList
Streams
( only available for API 24: Nougat and greater )LinkedHashSet
( available since API 1 )Lets say you have array list defined as follows
ArrayList<Music> musicList = new ArrayList<>();
ArrayList<String> categoryList = new ArrayList<>();
musicList.add(new Music("song1", "category1", "artist1"));
musicList.add(new Music("song2", "category2", "artist2"));
musicList.add(new Music("song3", "category3", "artist3"));
musicList.add(new Music("song4", "category2", "artist4"));
musicList.add(new Music("song5", "category1", "artist1"));
musicList.add(new Music("song6", "category4", "artist5"));
musicList.add(new Music("song7", "category3", "artist2"));
Option 2 : Using Streams
What I'm doing here is that I'm extracting only the category
field using the map()
method and removing duplicates using the distinct()
method then finally collecting all this modification in categoryList
. If you need to extract unique artist, just change music.category
to music.artist
EZ !
NOTE: After the map()
method we are now dealing with array of strings so distinct()
uses the equals()
and hashCode()
methods of the String
class to find and remove duplicates. If you are using distinct()
directly on your musicList
which is an array of Music
class then your Music
class should override and provide implementation of equals()
and hashCode()
methods.
Read more about them here or here
categoryList = musicList.stream()
.map((music)-> music.category)
.distinct()
.collect(Collectors.toCollection(ArrayList::new));
Log.d("CATEGORY LIST" , categoryList.toString())
//LOG Output
CATEGORY LIST: [category1, category2, category3, category4]
Option 3 : Using LinkedHashSet
This method is rather simple. As before first extracting category
fields and storing them into categoryList
. Creating LinkedHashSet
will automatically remove duplicate values just like the distinct()
method did when using streams. Afterwards just clearing the categoryList
and updating it with unique values from the hash set.
musicList.forEach((music) -> {
categoryList.add(music.category);
});
LinkedHashSet<String> uniqueCategoryHashSet = new LinkedHashSet(categoryList);
categoryList.clear();
categoryList.addAll(uniqueCategoryHashSet);
Log.d("CATEGORY LIST" , categoryList.toString())
//LOG Output
CATEGORY LIST: [category1, category2, category3, category4]
Now by no means I would mark Option 3 as an efficient solution. Maybe you can do better doing it manually if you can't use streams due to API restriction. But this gets the job done.