ListAdapter renewed
ListAdapter renewed
Every Android developer has worked with the RecyclerView when a list of content needs to be shown. Before the RecyclerView Android offered the widget ListAdapter, with bad performance and several other issues.Since the Android support lib version 27.1.0 Google added a new ListAdapter which extends RecyclerView.Adapter to make life even easier. Yes it has the name ListAdapter again :(, but for the rest it is completely different!

The simplified ListAdapter
To create a custom ListAdapter override the methods onCreateViewHolder() and onBindViewHolder(). All other methods are already handled by the ListAdapter and don't need any attention anymore.- onCreateViewHolder():
Called when RecyclerView needs a new RecyclerView.ViewHolder of the given type to represent an item.
- onBindViewHolder():
Called by RecyclerView to display the data at the specified position.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.recyclerview; | |
public class CarAdapter extends ListAdapter<Car, CarViewHolder> { | |
public CarAdapter() { | |
super(Car.DIFF_CALLBACK); | |
} | |
@NonNull | |
@Override | |
public CarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | |
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); | |
View view = layoutInflater.inflate(R.layout.bool_list_row, parent, false); | |
return new CarViewHolder(view); | |
} | |
@Override | |
public void onBindViewHolder(@NonNull CarViewHolder holder, int position) { | |
Car car = getItem(position); | |
if (car != null) { | |
holder.bindTo(car); | |
} | |
} | |
} |
How about DiffUtil.ItemCallback?
When working with a RecyclerView the adapter is responsible to validate if items where added/changed/removed. Previously all actions needed to be controlled manual, these days are over. This task has been taken over by the AsyncListDiffer, a helper for computing differences between two lists with on DiffUtil. Another advantage is that the AsyncListDiffer runs by default on a background thread.Simply implement a DiffUtil.ItemCallback and provide it as parameter with the constructor as in below example.
The DiffUtil.ItemCallback contains two important functions which need to be implemented:
- areItemsTheSame():
Called to check whether two items have the same data.
- areContentsTheSame():
Called to check whether two objects represent the same item.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.recyclerview; | |
public class Car { | |
private String name; | |
private String manufacturer; | |
public Car(String name, String author) { | |
this.name = name; | |
this.manufacturer = author; | |
} | |
static DiffUtil.ItemCallback<Car> DIFF_CALLBACK = new DiffUtil.ItemCallback<Car>() { | |
@Override | |
public boolean areItemsTheSame(@NonNull Car oldItem, @NonNull Car newItem) { | |
return oldItem.name.equals(newItem.name); | |
} | |
@Override | |
public boolean areContentsTheSame(@NonNull Car oldItem, @NonNull Car newItem) { | |
return oldItem.equals(newItem); | |
} | |
}; | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
Car car = (Car) o; | |
return Objects.equals(name, car.name) && | |
Objects.equals(manufacturer, car.manufacturer); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(name, manufacturer); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.recyclerview; | |
public class CarAdapter extends ListAdapter<Car, CarViewHolder> { | |
public CarAdapter() { | |
super(Car.DIFF_CALLBACK); | |
} | |
} |
Let's go to the total image
You have learned about the ListAdapter and DiffUtil.ItemCallback. Now it's time to use them to populate a RecyclerView.This example populates a list of cars. Every two seconds one list item is modified to show the animations.
- activity_main.xml: Main view used within MainActivity.java
- car_list_row.xml: RecyclerView item used within CarAdapter.java
- Car.java: The Car object
- CarAdapter.java: CarAdapter extends the ListAdapter
- Download.javaData provider, this can Room, Http request or another dataprovider
- MainActivity.javaThe MainActivity connects all items together and shows all cars
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.recyclerview; | |
public class Car { | |
public String name; | |
public String manufacturer; | |
public Car(String name, String author) { | |
this.name = name; | |
this.manufacturer = author; | |
} | |
static DiffUtil.ItemCallback<Car> DIFF_CALLBACK = new DiffUtil.ItemCallback<Car>() { | |
@Override | |
public boolean areItemsTheSame(@NonNull Car oldItem, @NonNull Car newItem) { | |
return oldItem.name.equals(newItem.name); | |
} | |
@Override | |
public boolean areContentsTheSame(@NonNull Car oldItem, @NonNull Car newItem) { | |
return oldItem.equals(newItem); | |
} | |
}; | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
Car car = (Car) o; | |
return Objects.equals(name, car.name) && | |
Objects.equals(manufacturer, car.manufacturer); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(name, manufacturer); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.recyclerview; | |
public class CarAdapter extends ListAdapter<Car, CarViewHolder> { | |
public CarAdapter() { | |
super(Car.DIFF_CALLBACK); | |
} | |
@NonNull | |
@Override | |
public CarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | |
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); | |
View view = layoutInflater.inflate(R.layout.car_list_row, parent, false); | |
return new CarViewHolder(view); | |
} | |
@Override | |
public void onBindViewHolder(@NonNull CarViewHolder holder, int position) { | |
Car car = getItem(position); | |
if (car != null) { | |
holder.bindTo(car); | |
} | |
} | |
static class CarViewHolder extends RecyclerView.ViewHolder { | |
private TextView name; | |
private TextView manufacturer; | |
CarViewHolder(View view) { | |
super(view); | |
name = view.findViewById(R.id.name); | |
manufacturer = view.findViewById(R.id.manufacturer); | |
} | |
void bindTo(Car car) { | |
name.setText(car.name); | |
manufacturer.setText(car.manufacturer); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication.data; | |
public class Download { | |
public List<Car> getDefaultList() { | |
List<Car> cars = new ArrayList<>(); | |
cars.add(new Car("V40", "Unknown")); | |
cars.add(new Car("Cayenne coupe", "Porsche")); | |
cars.add(new Car("Model 3", "Tesla")); | |
cars.add(new Car("SL 55 AMG ", "Mercedes")); | |
cars.add(new Car("911 GT3", "Porsche")); | |
cars.add(new Car("AMG GT C Roadster", "Mercedes")); | |
cars.add(new Car("DB11 ", "Aston Martin")); | |
return cars; | |
} | |
public List<Car> getListChangedCarItem() { | |
List<Car> carList = getDefaultList(); | |
carList.get(0).manufacturer = "Volvo"; | |
return carList; | |
} | |
public List<Car> getListAddCarItem() { | |
List<Car> carList = getListChangedCarItem(); | |
carList.add(2, new Car("TT Coupe", "Audi")); | |
return carList; | |
} | |
public List<Car> getListRemoveCarItem() { | |
List<Car> carList = getListAddCarItem(); | |
carList.remove(1); | |
return carList; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.test.myapplication; | |
public class MainActivity extends AppCompatActivity { | |
private CarAdapter mAdapter; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
Toolbar toolbar = findViewById(R.id.toolbar); | |
setSupportActionBar(toolbar); | |
setRecyclerView(); | |
} | |
private void setRecyclerView() { | |
RecyclerView recyclerView = findViewById(R.id.list); | |
mAdapter = new CarAdapter(); | |
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); | |
recyclerView.setLayoutManager(mLayoutManager); | |
recyclerView.setItemAnimator(new DefaultItemAnimator()); | |
recyclerView.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL)); | |
recyclerView.setAdapter(mAdapter); | |
Download download = new Download(); | |
mAdapter.submitList(download.getDefaultList()); | |
Disposable d = Observable.just(true) | |
.delay(2000, TimeUnit.MILLISECONDS) | |
.doOnNext(aBoolean -> mAdapter.submitList(download.getListChangedCarItem())) | |
.delay(2000, TimeUnit.MILLISECONDS) | |
.doOnNext(aBoolean -> mAdapter.submitList(download.getListAddCarItem())) | |
.delay(2000, TimeUnit.MILLISECONDS) | |
.doOnNext(aBoolean -> mAdapter.submitList(download.getListRemoveCarItem())) | |
.subscribe(); | |
} | |
} |

Good to know is that the ListAdapter keeps a reference towards the provided list in submitList(). Always make sure to provide an other list with other item instances. If the references to the items are the same, DiffUtil compares the same items which are always equal, and the RecyclerView will not be updated.
Within the MainAcitivty RxJava is used to re-populate the list with modifications. With the ListAdapter Google created another simplified tool for developers to provide the users the best Android experience with the least of costs.
With this road to App Bundle you've learned to create and use the App Bundle.
Thank you for reading this article, If you liked the article, help others find this article by sharing it.
Reacties
Een reactie posten