Introduction to list showing in Android
Thu, 14 Nov 2024
TA
.RecyclerView
.RecyclerView
simplifies our code by doing most of the general code. We will just create frame and set our custom data.ListView
can also be used, but RecyclerView
is far better and flexible than it.RecyclerView
is a powerful and efficient view designed for displaying large datasets in a list or grid format.ViewHolder:
findViewById()
calls, improving performance.LayoutManager:
LinearLayoutManager
: Displays items in a vertical or horizontal list.GridLayoutManager
: Displays items in a grid format.StaggeredGridLayoutManager
: Displays items in a staggered grid format.Suppose we want to show the list of TA(teaching assistant)
of Bit2Byte club.
Let's make a class to represent a teaching assistant.(TA.java
).
public class TA {
private String username;
private String name;
private String email;
private String imageUrl;
public TA(String username, String name, String email, String imageUrl) {
this.username = username;
this.name = name;
this.email = email;
this.imageUrl = imageUrl;
}
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
}
Remember we need a frame to set data. This is nothing but a design file. Let's us create one(layout/each_ta_design.xml
)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:background="@drawable/little_round_back"
android:padding="8dp"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/ivProfile"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="Profile Picture"
android:src="@drawable/baseline_account_circle_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="0dp"
android:layout_marginStart="16dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/imageView"
app:layout_constraintStart_toEndOf="@+id/ivProfile"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="serif"
android:textSize="16sp"
android:textStyle="bold"
tools:text="saeed1907057" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="serif"
android:textSize="16sp"
android:textStyle="bold"
tools:text="Md Abu Saeed" />
</LinearLayout>
<ImageView
android:id="@+id/imageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/baseline_mail_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now we need a class that will init frames and set data. Since many things like animation, optimization is needed, we will use the default class with our custom data. Let's create one(TaAdapter.java
):
public class TaAdapter extends RecyclerView.Adapter<TaAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
static class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
RecyclerView.Adapter
class which performs the iteration for each item, the call the onCreateViewHolder
, onBindViewHolder
function to tell us init frame or set data.onCreateViewHolder
each_ta_design.xml
).ViewHolder
class, which is nothing but the object of the design file.RecyclerView
reuses the frame instead of creating for each item. So it may be called less than total items in our list.onBindViewHolder
ViewHolder holder
contains the object of the frame and int position
is the position on the list that we will bind to our frame.getItemCount
Let's complete the class first:
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
public class TaAdapter extends RecyclerView.Adapter<TaAdapter.ViewHolder> {
private final Context mContext;
private final List<TA> taList;
private TA curTA;
public TaAdapter(Context context, List<TA> taList) {
this.mContext = context;
this.taList = taList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.each_ta_design,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
curTA = taList.get(position);
holder.tvName.setText(curTA.getName());
holder.tvUserName.setText(curTA.getUsername());
Glide
.with(mContext)
.load(curTA.getImageUrl())
.into(holder.ivProfile);
holder.ivMail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openMail( taList.get(holder.getAdapterPosition()).getEmail() );
}
});
}
@Override
public int getItemCount() {
return taList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder{
private final ImageView ivProfile;
private final TextView tvUserName;
private final TextView tvName;
private final ImageView ivMail;
public ViewHolder(@NonNull View itemView) {
super(itemView);
ivProfile = itemView.findViewById(R.id.ivProfile);
tvUserName = itemView.findViewById(R.id.tvUserName);
tvName = itemView.findViewById(R.id.tvName);
ivMail = itemView.findViewById(R.id.ivMail);
}
}
private void openMail(String email) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + email));
intent.putExtra(Intent.EXTRA_SUBJECT, "Hello from Bit2Byte");
intent.putExtra(Intent.EXTRA_TEXT, "This is a sample email body.");
mContext.startActivity(intent);
}
}
Glide
is used inside onBindViewHolder
function. Add this inside app/build.gradle
file inside the dependecies
sectiondependencies {
// other dependencies
implementation 'com.github.bumptech.glide:glide:4.15.1'
}
<application>
tag.<uses-permission android:name="android.permission.INTERNET"/>
Everything is ready now. Now, include RecyclerView
in your XML layout file of the Activity we want to show the list.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvTA"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical"
app:reverseLayout="false"
tools:listitem="@layout/each_ta_design"
tools:itemCount="5"/>
tools:listitem
, tools:itemCount
are just for previewing. It won't be used later when app is running.
In the Java part of our Activity, create an object of the TaAdapter
class and set it to the recyclerview.
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class SecondActivity extends AppCompatActivity {
private RecyclerView rvTA;
private TaAdapter taAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
rvTA = findViewById(R.id.rvTA);
initRecyclerView();
}
private void initRecyclerView(){
final List<TA> taList = new ArrayList<>();
taList.add( new TA("Tahmid", "Farhan Tahmid", "tahmid@gmail.com","https://i.postimg.cc/hvG8T5VS/11.jpg") );
taList.add( new TA("Anika", "Anika Nawer", "nawer@gmail.com","https://i.postimg.cc/SRXBVDhh/120.jpg") );
taList.add( new TA("Ankon", "Ankon Roy", "roy@gmail.com","https://i.postimg.cc/SRXBVDhh/120.jpg") );
taList.add( new TA("Ariful", "Ariful Alam", "mahim@yahoo.com","https://i.postimg.cc/hvG8T5VS/11.jpg") );
taAdapter = new TaAdapter(this, taList);
rvTA.setAdapter(taAdapter);
}
}
If we run our application, we will get an output like this:
taAdapter.notifyDataSetChanged();
taAdapter.notifyItemChanged(index);
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
public class TaAdapter extends ListAdapter<TA, TaAdapter.ViewHolder> {
private final Context mContext;
private TA curTA;
public TaAdapter(Context context) {
super(callback);
this.mContext = context;
}
static final DiffUtil.ItemCallback<TA> callback = new DiffUtil.ItemCallback<TA>() {
@Override
public boolean areItemsTheSame(@NonNull TA oldItem, @NonNull TA newItem) {
return newItem.getUsername().equals(oldItem.getUsername());
}
@Override
public boolean areContentsTheSame(@NonNull TA oldItem, @NonNull TA newItem) {
return newItem.getUsername().equals(oldItem.getUsername()) &&
newItem.getName().equals(oldItem.getName()) &&
newItem.getImageUrl().equals(oldItem.getImageUrl());
}
};
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.each_ta_design,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
curTA = getItem(position);
holder.tvName.setText(curTA.getName());
holder.tvUserName.setText(curTA.getUsername());
Glide
.with(mContext)
.load(curTA.getImageUrl())
.into(holder.ivProfile);
holder.ivMail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openMail( getItem(holder.getAdapterPosition()).getEmail() );
}
});
}
public static class ViewHolder extends RecyclerView.ViewHolder{
private final ImageView ivProfile;
private final TextView tvUserName;
private final TextView tvName;
private final ImageView ivMail;
public ViewHolder(@NonNull View itemView) {
super(itemView);
ivProfile = itemView.findViewById(R.id.ivProfile);
tvUserName = itemView.findViewById(R.id.tvUserName);
tvName = itemView.findViewById(R.id.tvName);
ivMail = itemView.findViewById(R.id.ivMail);
}
}
private void openMail(String email) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + email));
intent.putExtra(Intent.EXTRA_SUBJECT, "Hello from Bit2Byte");
intent.putExtra(Intent.EXTRA_TEXT, "This is a sample email body.");
mContext.startActivity(intent);
}
}
static final DiffUtil.ItemCallback<TA> callback ...
public class SecondActivity extends AppCompatActivity {
private RecyclerView rvTA;
private TaAdapter taAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
rvTA = findViewById(R.id.rvTA);
initRecyclerView();
}
private void initRecyclerView(){
final List<TA> taList = new ArrayList<>();
taList.add( new TA("Tahmid", "Farhan Tahmid", "tahmid@gmail.com","https://i.postimg.cc/hvG8T5VS/11.jpg") );
taList.add( new TA("Anika", "Anika Nawer", "nawer@gmail.com","https://i.postimg.cc/SRXBVDhh/120.jpg") );
taList.add( new TA("Ankon", "Ankon Roy", "roy@gmail.com","https://i.postimg.cc/SRXBVDhh/120.jpg") );
taList.add( new TA("Ariful", "Ariful Alam", "mahim@yahoo.com","https://i.postimg.cc/hvG8T5VS/11.jpg") );
taAdapter = new TaAdapter(this);
rvTA.setAdapter(taAdapter);
taAdapter.submitList(taList); // only changes
}
}
taAdapter.submitList(taList);
This is a comment 1.
This is a comment 2.