I\'ve made a list of items a few times using Android\'s RecyclerView
, but it is a rather complicated process. Going through one of the numerous tutorials online
Start by adding recyclerview library.
implementation 'androidx.recyclerview:recyclerview:1.1.0'
Create model class.
public class UserModel implements Serializable {
private String userName;
public UserModel(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
create adapter class.
public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UsersAdapterVh> implements Filterable {
private List<UserModel> userModelList;
private List<UserModel> getUserModelListFiltered;
private Context context;
private SelectedUser selectedUser;
public UsersAdapter(List<UserModel> userModelList,SelectedUser selectedUser) {
this.userModelList = userModelList;
this.getUserModelListFiltered = userModelList;
this.selectedUser = selectedUser;
}
@NonNull
@Override
public UsersAdapter.UsersAdapterVh onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
return new UsersAdapterVh(LayoutInflater.from(context).inflate(R.layout.row_users,null));
}
@Override
public void onBindViewHolder(@NonNull UsersAdapter.UsersAdapterVh holder, int position) {
UserModel userModel = userModelList.get(position);
String username = userModel.getUserName();
String prefix = userModel.getUserName().substring(0,1);
holder.tvUsername.setText(username);
holder.tvPrefix.setText(prefix);
}
@Override
public int getItemCount() {
return userModelList.size();
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults filterResults = new FilterResults();
if(charSequence == null | charSequence.length() == 0){
filterResults.count = getUserModelListFiltered.size();
filterResults.values = getUserModelListFiltered;
}else{
String searchChr = charSequence.toString().toLowerCase();
List<UserModel> resultData = new ArrayList<>();
for(UserModel userModel: getUserModelListFiltered){
if(userModel.getUserName().toLowerCase().contains(searchChr)){
resultData.add(userModel);
}
}
filterResults.count = resultData.size();
filterResults.values = resultData;
}
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
userModelList = (List<UserModel>) filterResults.values;
notifyDataSetChanged();
}
};
return filter;
}
public interface SelectedUser{
void selectedUser(UserModel userModel);
}
public class UsersAdapterVh extends RecyclerView.ViewHolder {
TextView tvPrefix;
TextView tvUsername;
ImageView imIcon;
public UsersAdapterVh(@NonNull View itemView) {
super(itemView);
tvPrefix = itemView.findViewById(R.id.prefix);
tvUsername = itemView.findViewById(R.id.username);
imIcon = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
selectedUser.selectedUser(userModelList.get(getAdapterPosition()));
}
});
}
}
}
create layout row_uses.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="50dp"
android:background="@drawable/users_bg"
android:layout_height="50dp">
<TextView
android:id="@+id/prefix"
android:layout_width="wrap_content"
android:textSize="16sp"
android:textColor="@color/headerColor"
android:text="T"
android:layout_centerInParent="true"
android:layout_height="wrap_content"/>
</RelativeLayout>
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:textSize="16sp"
android:textColor="@color/headerColor"
android:text="username"
android:layout_marginStart="90dp"
android:layout_centerVertical="true"
android:layout_height="wrap_content"/>
<ImageView
android:layout_width="wrap_content"
android:id="@+id/imageView"
android:layout_margin="10dp"
android:layout_alignParentEnd="true"
android:src="@drawable/ic_navigate_next_black_24dp"
android:layout_height="wrap_content"/>
</RelativeLayout>
</LinearLayout>
Find recyclerview and populate data.
Toolbar toolbar;
RecyclerView recyclerView;
List<UserModel> userModelList = new ArrayList<>();
String[] names = {"Richard","Alice","Hannah","David"};
UsersAdapter usersAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerview);
toolbar = findViewById(R.id.toolbar);
this.setSupportActionBar(toolbar);
this.getSupportActionBar().setTitle("");
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
for(String s:names){
UserModel userModel = new UserModel(s);
userModelList.add(userModel);
}
usersAdapter = new UsersAdapter(userModelList,this);
recyclerView.setAdapter(usersAdapter);
}
find full tutorial and source code here:
Android RecyclerView
Since I cant comment yet im gonna post as an answer the link.. I have found a simple, well organized tutorial on recyclerview http://www.androiddeft.com/2017/10/01/recyclerview-android/
Apart from that when you are going to add a recycler view into you activity what you want to do is as below and how you should do this has been described on the link
Dependencies
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:multidex:1.0.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.lguipeng.bubbleview:library:1.0.0'
compile 'com.larswerkman:HoloColorPicker:1.5'
compile 'com.mcxiaoke.volley:library-aar:1.0.0'
One Class For Click Item
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildPosition(childView));
return true;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
Second Class RecyclerView
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class SLByTopics extends Fragment {
public static ArrayList<MByTopics> byTopicsMainArrayList=new ArrayList<>();
TabRefreshReceiver tabRefreshReceiver;
RecyclerView recyclerView;
SAdpByTopics sAdpByTopics;
public ArrayList<MByTopics> mByTopicsArrayList=new ArrayList<>();
ProgressDialog progressDialog;
public SLByTopics(){
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.sl_fragment_by_topics, container, false);
progressDialog = new ProgressDialog(getActivity());
if (IsOnline.isNetworkAvailable(getActivity())) {
getCategoryTree();
} else{
IsOnline.showNoInterNetMessage(getActivity());
}
tabRefreshReceiver = new TabRefreshReceiver();
LocalBroadcastManager.getInstance(getContext()).registerReceiver(tabRefreshReceiver, new IntentFilter("BY_TOPICS"));
setUpView(view);
return view;
}
private void setUpView(View view) {
recyclerView=(RecyclerView)view.findViewById(R.id.by_topics_list_recyclerView);
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
}
@Override
public void onResume() {
super.onResume();
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, final int position) {
if (mByTopicsArrayList.get(position).getChild().size()>0){
Intent intent = new Intent(getActivity(), SByTopicCategory.class);
intent.putExtra("selectedCategoryName",mByTopicsArrayList.get(position).getCatname());
intent.putExtra("jsonData",mByTopicsArrayList.get(position).getMainTopicJson());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
}else {
Intent intent = new Intent(getActivity(), SByCategoryQuestionList.class);
intent.putExtra("selectedSubCategoryName",mByTopicsArrayList.get(position).getCatname());
intent.putExtra("catID",mByTopicsArrayList.get(position).getId());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
}
}
}));
}
private class TabRefreshReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(SLByTopics.this).attach(SLByTopics.this).commit();
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(tabRefreshReceiver);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void getCategoryTree() {
progressDialog.setMessage("Please Wait...");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest stringRequest = new StringRequest(Request.Method.POST, Const.HOSTNAME + Const.STUDENT_GET_CATEGORY_TREE,
new Response.Listener<String>() {
@SuppressLint("LongLogTag")
@Override
public void onResponse(String response) {
try {
JSONObject object = new JSONObject(response);
String status = object.getString("status");
int i = Integer.parseInt(status);
switch (i) {
case 0:
progressDialog.dismiss();
// Toast.makeText(getActivity(), "getCategorySuccess", Toast.LENGTH_SHORT).show();
Log.e("getCategoryTree Response", "getCategoryTree Response : " + response);
try {
byTopicsMainArrayList.clear();
JSONArray info = object.getJSONArray("info");
if (info.length() > 0) {
for (i = 0; i < info.length(); i++) {
JSONObject data = info.getJSONObject(i);
MByTopics mByTopics = new MByTopics();
mByTopics.setId(data.getString("id"));
mByTopics.setCatname(data.getString("catname"));
mByTopics.setMainTopicJson(data.toString());
JSONArray topicChildren = data.getJSONArray("children");
ArrayList<SMByTopicCategory> byChildrenArrayList = new ArrayList<>();
for (int j = 0; j < topicChildren.length(); j++) {
JSONObject topicChildrenData = topicChildren.getJSONObject(j);
SMByTopicCategory smByTopicCategory = new SMByTopicCategory();
smByTopicCategory.setId(topicChildrenData.getString("id"));
smByTopicCategory.setCatname(topicChildrenData.getString("catname"));
smByTopicCategory.setChildTopicJson(topicChildrenData.toString());
JSONArray topicChildrenQuestion = topicChildrenData.getJSONArray("children");
ArrayList<SMByTopicSubCategory> byChildrenSubArrayList = new ArrayList<>();
for (int k = 0; k < topicChildrenQuestion.length(); k++) {
JSONObject topicChildrenSubData = topicChildrenQuestion.getJSONObject(k);
SMByTopicSubCategory smByTopicSubCategory = new SMByTopicSubCategory();
smByTopicSubCategory.setId(topicChildrenSubData.getString("id"));
smByTopicSubCategory.setCatname(topicChildrenSubData.getString("catname"));
smByTopicSubCategory.setChildSubTopicJson(topicChildrenSubData.toString());
byChildrenSubArrayList.add(smByTopicSubCategory);
}
smByTopicCategory.setQuestions(byChildrenSubArrayList);
byChildrenArrayList.add(smByTopicCategory);
}
mByTopics.setChild(byChildrenArrayList);
byTopicsMainArrayList.add(mByTopics);
}
mByTopicsArrayList.clear();
mByTopicsArrayList=byTopicsMainArrayList;
sAdpByTopics=new SAdpByTopics(mByTopicsArrayList,getActivity());
recyclerView.setAdapter(sAdpByTopics);
sAdpByTopics.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}
break;
default:
progressDialog.dismiss();
// Toast.makeText(getActivity(), "getCategoryError : " + response, Toast.LENGTH_SHORT).show();
Log.e("getCategoryTree Not Response", "getCategoryTree Uploading Not Response : " + response);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Log.e("getCategoryTree Error :","getCategoryTree Error :"+error.getMessage());
// Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_LONG).show();
}
}){
};/* {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = new HashMap<String, String>();
// map.put("uid", String.valueOf(ConfigManager.getUserId()));
return map;
}
};*/
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
0,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(stringRequest);
}
}
Adapter Class For Recycler Item
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class SAdpByTopics extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<MByTopics> topicsArrayList=new ArrayList<>();
Activity activity;
public SAdpByTopics(ArrayList<MByTopics> topicsArrayList,Activity activity){
this.topicsArrayList=topicsArrayList;
this.activity=activity;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemeView= LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_by_topic_list,parent,false);
RecyclerView.ViewHolder holder=new Holder(itemeView);
holder.setIsRecyclable(false);
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Holder classHolder = (Holder) holder;
try{
classHolder.txt_topic_name.setText(topicsArrayList.get(position).getCatname());
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public int getItemCount() {
return topicsArrayList.size();
}
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView txt_topic_name;
public Holder(View itemView) {
super(itemView);
txt_topic_name = (TextView) itemView.findViewById(R.id.txt_topic_name);
}
@Override
public void onClick(View v) {
}
}
}
Module Class
public class MByTopics {
String id;
String topicName;
String catname;
String MainTopicJson;
ArrayList<SMByTopicCategory> child;
ArrayList<SMByTopicSubCategory> questions;
public void setId(String id){
this.id=id;
}
public String getId(){
return id;
}
public void setCatname(String catname) {
this.catname = catname;
}
public String getCatname() {
return catname;
}
public void setTopicName(String topicName) {
this.topicName = topicName;
}
public String getTopicName() {
return topicName;
}
public void setChild(ArrayList<SMByTopicCategory> child) {
this.child = child;
}
public String getMainTopicJson() {
return MainTopicJson;
}
public void setMainTopicJson(String mainTopicJson) {
MainTopicJson = mainTopicJson;
}
public ArrayList<SMByTopicCategory> getChild() {
return child;
}
public void setQuestions(ArrayList<SMByTopicSubCategory> questions) {
this.questions = questions;
}
public ArrayList<SMByTopicSubCategory> getQuestions() {
return questions;
}
public ArrayList<MByTopics> getByTopicList() {
ArrayList<MByTopics> mByTopicsArrayList = new ArrayList<>();
for (int i=0;i<11;i++){
MByTopics mQuestionBankCategory=new MByTopics();
if (i==1 || i== 5|| i==9){
mQuestionBankCategory.setTopicName("Microeconomics");
}else if (i==2 || i== 10|| i==6) {
mQuestionBankCategory.setTopicName("Macroeconomics");
}else {
mQuestionBankCategory.setTopicName("Current Isssues");
}
mByTopicsArrayList.add(mQuestionBankCategory);
}
return mByTopicsArrayList;
}
}
RecyclerView
onBindViewHolder
, No onCreateViewHolder
handling.I show here setting two different RecyclerView
by 1 adapter -
activity_home.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="listOne"
type="java.util.List"/>
<variable
name="listTwo"
type="java.util.List"/>
<variable
name="onItemClickListenerOne"
type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>
<variable
name="onItemClickListenerTwo"
type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
rvItemLayout="@{@layout/row_one}"
rvList="@{listOne}"
rvOnItemClick="@{onItemClickListenerOne}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
/>
<android.support.v7.widget.RecyclerView
rvItemLayout="@{@layout/row_two}"
rvList="@{listTwo}"
rvOnItemClick="@{onItemClickListenerTwo}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
/>
</LinearLayout>
</layout>
You can see I pass list, item layout id and click listener from layout.
rvItemLayout="@{@layout/row_one}"
rvList="@{listOne}"
rvOnItemClick="@{onItemClickListenerOne}"
This custom attributes are created by BindingAdapter.
public class BindingAdapters {
@BindingAdapter(value = {"rvItemLayout", "rvList", "rvOnItemClick"}, requireAll = false)
public static void setRvAdapter(RecyclerView recyclerView, int rvItemLayout, List rvList, @Nullable OnItemClickListener onItemClickListener) {
if (rvItemLayout != 0 && rvList != null && rvList.size() > 0)
recyclerView.setAdapter(new GeneralAdapter(rvItemLayout, rvList, onItemClickListener));
}
}
Now from Activity, you pass list, click listener like
HomeActivity.java
public class HomeActivity extends AppCompatActivity {
ActivityHomeBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
binding.setListOne(new ArrayList()); // pass your list or set list from response of API
binding.setListTwo(new ArrayList());
binding.setOnItemClickListenerOne(new OnItemClickListener() {
@Override
public void onItemClick(View view, Object object) {
if (object instanceof ModelParent) {
// TODO: your action here
}
}
});
binding.setOnItemClickListenerTwo(new OnItemClickListener() {
@Override
public void onItemClick(View view, Object object) {
if (object instanceof ModelChild) {
// TODO: your action here
}
}
});
}
}
You can see GeneralAdapter.java
in above repo.
If you have problems while setting up data binding, please see this answer.
Based on different sources I have created Simple Implementation of RecyclerView using a Simple Library.
Add this line in build.gradle
implementation 'com.hereshem.lib:awesomelib:2.0.1'
AddCreate a RecyclerView by adding MyRecyclerView in activity_main.xml with
<com.hereshem.lib.recycler.MyRecyclerView
android:id="@+id/recycler"
app:layoutManager="LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Now in the MainActivity, Create a ViewHolder by passing the name of Class that needs to bind
public static class EVHolder extends MyViewHolder<Events> {
TextView date, title, summary;
public EVHolder(View v) {
super(v);
date = v.findViewById(R.id.date);
title = v.findViewById(R.id.title);
summary = v.findViewById(R.id.summary);
}
@Override
public void bindView(Events c) {
date.setText(c.date);
title.setText(c.title);
summary.setText(c.summary);
}
}
Create Items list variable and adapters with very few lines by passing items, class and layout in the adapter
List<Events> items = new ArrayList<>();
MyRecyclerView recycler = findViewById(R.id.recycler);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, items, EVHolder.class, R.layout.row_event);
recycler.setAdapter(adapter);
ClickListener can be added with following lines
recycler.setOnItemClickListener(new MyRecyclerView.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, "Recycler Item Clicked " + position, Toast.LENGTH_SHORT).show();
}
});
Its all done.
More example and implementation can be found here . Hope this helps !!!
You can use abstract adapter with diff utils and filter
SimpleAbstractAdapter.kt
abstract class SimpleAbstractAdapter<T>(private var items: ArrayList<T> = arrayListOf()) : RecyclerView.Adapter<SimpleAbstractAdapter.VH>() {
protected var listener: OnViewHolderListener<T>? = null
private val filter = ArrayFilter()
private val lock = Any()
protected abstract fun getLayout(): Int
protected abstract fun bindView(item: T, viewHolder: VH)
protected abstract fun getDiffCallback(): DiffCallback<T>?
private var onFilterObjectCallback: OnFilterObjectCallback? = null
private var constraint: CharSequence? = ""
override fun onBindViewHolder(vh: VH, position: Int) {
getItem(position)?.let { bindView(it, vh) }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return VH(parent, getLayout())
}
override fun getItemCount(): Int = items.size
protected abstract class DiffCallback<T> : DiffUtil.Callback() {
private val mOldItems = ArrayList<T>()
private val mNewItems = ArrayList<T>()
fun setItems(oldItems: List<T>, newItems: List<T>) {
mOldItems.clear()
mOldItems.addAll(oldItems)
mNewItems.clear()
mNewItems.addAll(newItems)
}
override fun getOldListSize(): Int {
return mOldItems.size
}
override fun getNewListSize(): Int {
return mNewItems.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return areItemsTheSame(
mOldItems[oldItemPosition],
mNewItems[newItemPosition]
)
}
abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return areContentsTheSame(
mOldItems[oldItemPosition],
mNewItems[newItemPosition]
)
}
abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean
}
class VH(parent: ViewGroup, @LayoutRes layout: Int) : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false))
interface OnViewHolderListener<T> {
fun onItemClick(position: Int, item: T)
}
fun getItem(position: Int): T? {
return items.getOrNull(position)
}
fun getItems(): ArrayList<T> {
return items
}
fun setViewHolderListener(listener: OnViewHolderListener<T>) {
this.listener = listener
}
fun addAll(list: List<T>) {
val diffCallback = getDiffCallback()
when {
diffCallback != null && !items.isEmpty() -> {
diffCallback.setItems(items, list)
val diffResult = DiffUtil.calculateDiff(diffCallback)
items.clear()
items.addAll(list)
diffResult.dispatchUpdatesTo(this)
}
diffCallback == null && !items.isEmpty() -> {
items.clear()
items.addAll(list)
notifyDataSetChanged()
}
else -> {
items.addAll(list)
notifyDataSetChanged()
}
}
}
fun add(item: T) {
items.add(item)
notifyDataSetChanged()
}
fun add(position:Int, item: T) {
items.add(position,item)
notifyItemInserted(position)
}
fun remove(position: Int) {
items.removeAt(position)
notifyItemRemoved(position)
}
fun remove(item: T) {
items.remove(item)
notifyDataSetChanged()
}
fun clear(notify: Boolean=false) {
items.clear()
if (notify) {
notifyDataSetChanged()
}
}
fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter {
return this.filter.setFilter(filter)
}
interface SimpleAdapterFilter<T> {
fun onFilterItem(contains: CharSequence, item: T): Boolean
}
fun convertResultToString(resultValue: Any): CharSequence {
return filter.convertResultToString(resultValue)
}
fun filter(constraint: CharSequence) {
this.constraint = constraint
filter.filter(constraint)
}
fun filter(constraint: CharSequence, listener: Filter.FilterListener) {
this.constraint = constraint
filter.filter(constraint, listener)
}
fun getFilter(): Filter {
return filter
}
interface OnFilterObjectCallback {
fun handle(countFilterObject: Int)
}
fun setOnFilterObjectCallback(objectCallback: OnFilterObjectCallback) {
onFilterObjectCallback = objectCallback
}
inner class ArrayFilter : Filter() {
private var original: ArrayList<T> = arrayListOf()
private var filter: SimpleAdapterFilter<T> = DefaultFilter()
private var list: ArrayList<T> = arrayListOf()
private var values: ArrayList<T> = arrayListOf()
fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter {
original = items
this.filter = filter
return this
}
override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
val results = Filter.FilterResults()
if (constraint == null || constraint.isBlank()) {
synchronized(lock) {
list = original
}
results.values = list
results.count = list.size
} else {
synchronized(lock) {
values = original
}
val result = ArrayList<T>()
for (value in values) {
if (constraint!=null && constraint.trim().isNotEmpty() && value != null) {
if (filter.onFilterItem(constraint, value)) {
result.add(value)
}
} else {
value?.let { result.add(it) }
}
}
results.values = result
results.count = result.size
}
return results
}
override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) {
items = results.values as? ArrayList<T> ?: arrayListOf()
notifyDataSetChanged()
onFilterObjectCallback?.handle(results.count)
}
}
class DefaultFilter<T> : SimpleAdapterFilter<T> {
override fun onFilterItem(contains: CharSequence, item: T): Boolean {
val valueText = item.toString().toLowerCase()
if (valueText.startsWith(contains.toString())) {
return true
} else {
val words = valueText.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (word in words) {
if (word.contains(contains)) {
return true
}
}
}
return false
}
}
}
And extend abstract adapter with implements methods
TasksAdapter.kt
import android.annotation.SuppressLint
import kotlinx.android.synthetic.main.task_item_layout.view.*
class TasksAdapter(private val listener:TasksListener? = null) : SimpleAbstractAdapter<Task>() {
override fun getLayout(): Int {
return R.layout.task_item_layout
}
override fun getDiffCallback(): DiffCallback<Task>? {
return object : DiffCallback<Task>() {
override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean {
return oldItem.items == newItem.items
}
}
}
@SuppressLint("SetTextI18n")
override fun bindView(item: Task, viewHolder: VH) {
viewHolder.itemView.apply {
val position = viewHolder.adapterPosition
val customer = item.customer
val customerName = if (customer != null) customer.name else ""
tvTaskCommentTitle.text = customerName + ", #" + item.id
tvCommentContent.text = item.taskAddress
ivCall.setOnClickListener {
listener?.onCallClick(position, item)
}
setOnClickListener {
listener?.onItemClick(position, item)
}
}
}
interface TasksListener : SimpleAbstractAdapter.OnViewHolderListener<Task> {
fun onCallClick(position: Int, item: Task)
}
}
Init adapter
mAdapter = TasksAdapter(object : TasksAdapter.TasksListener {
override fun onCallClick(position: Int, item:Task) {
}
override fun onItemClick(position: Int, item:Task) {
}
})
rvTasks.adapter = mAdapter
and fill
mAdapter?.addAll(tasks)
add custom filter
mAdapter?.setFilter(object : SimpleAbstractAdapter.SimpleAdapterFilter<MoveTask> {
override fun onFilterItem(contains: CharSequence, item:Task): Boolean {
return contains.toString().toLowerCase().contains(item.id?.toLowerCase().toString())
}
})
filter data
mAdapter?.filter("test")