Create a Generic Base Adapter

Create a Generic Base Adapter?

Answering my own Question after two years.

In order to make Generic Adapter you need abstract methods in parent class which should be implemented by child. So GenericAdapter must be Abstract and create abstract methods for data you want to change.

I have different types of ArrayList to show in Adapter only it's model class is different. So I decided to write a genericAdapter. This is for recyclerview if someOne interested for listview. Let me know.

GenericAdapter

public abstract class GenericAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private Context context;
private ArrayList<T> items;

public abstract RecyclerView.ViewHolder setViewHolder(ViewGroup parent);

public abstract void onBindData(RecyclerView.ViewHolder holder, T val);

public GenericAdapter(Context context, ArrayList<T> items){
this.context = context;
this.items = items;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = setViewHolder(parent);
return holder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onBindData(holder,items.get(position));
}

@Override
public int getItemCount() {
return items.size();
}

public void addItems( ArrayList<T> savedCardItemz){
items = savedCardItemz;
this.notifyDataSetChanged();
}

public T getItem(int position){
return items.get(position);
}
}

And I use it like

     adapter = new GenericAdapter<DataModel>(context,modelList) {
@Override
public RecyclerView.ViewHolder setViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_view_holder, parent, false);
ItemViewHolder viewHolder = new ItemViewHolder(context, view);
return viewHolder;
}

@Override
public void onBindData(RecyclerView.ViewHolder holder1, DataModel val) {
DataModel userModel = val;

ItemViewHolder holder = (ItemViewHolder)holder1;
holder.name.setText(userModel.getName());
holder.fatherName.setText(userModel.getFatherName());
}
};
mRecyclerView.setAdapter(adapter);

RecyclerView with Search sort functionality

How to create base adapter which has type parameter that extends another class and implements an interface?

So I discovered the answer as soon as I posted the question, I'll post this in case anyone need the answer:

Change the RecyclerView.Adapter<BaseAdapter.ViewHolder> to RecyclerView.Adapter<BaseAdapter<T>.ViewHolder>

I originally created the base adapter without the type parameter so it worked with RecyclerView.Adapter<BaseAdapter.ViewHolder>, but after adding the type parameter, I also need to add <T>.

Another solution is making the internal ViewHolder static as @chRyNaN suggested.

Create Base Adapters For All Recycler View Adapters

    import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public abstract class BaseAdapter extends RecyclerView.Adapter<BaseAdapter.MyViewHolder> {

public int layout_id;
protected List<?> dataList = new ArrayList<>();
Context BASE_CONTEXT;
public View itemview;

public BaseAdapter(Context context) {
this.BASE_CONTEXT = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(layout_id, viewGroup, false);
return new MyViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int position) {
onBindViewHold(position, dataList.get(position));
}

public abstract View getView(View view);

@Override
public int getItemCount() {
return dataList.size() == 0 ? 0 : dataList.size();
}

public abstract void onBindViewHold(int position, Object itemView);

class MyViewHolder extends RecyclerView.ViewHolder {

public MyViewHolder(@NonNull View itemView) {
super(itemView);
itemview = itemView;
getView(itemView);
}

}

public <T extends View> T bind(int id) {
return itemview.findViewById(id);
}

}

public class Adapter extends BaseAdapter {

TextView tv;

Adapter(Context context, ArrayList<String> arrayList){
super(context);
dataList=arrayList;
layout_id=R.layout.content_main2;

}

@Override
public View getView(View view) {
tv = bind(R.id.tv);
return view;
}

@Override
public void onBindViewHold( final int position, Object itemView) {

String text=(String) itemView;
Log.e("tv",tv.toString());
tv.setText(text);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(BASE_CONTEXT, ""+position, Toast.LENGTH_SHORT).show();
}
});
}

}

Custom android adapter with generic class

You cannot keep only one instance of ViewHolder. You need to create new object everytime the convertView is null and setTag.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View v = convertView;
ViewHolder h;
if (v == null) {
v = inflater.inflate(resources, parent, false);
h = new ViewHolder();
listener.init(v, h);
v.setTag(h);
} else {
h = (ViewHolder) v.getTag();
}
T object = (T) getItem(position);
listener.execute(object, h);
return v;
}

Type Mismatch with Kotlin Generic Adapter

that's because you're mixing type T and Any, try this:

BaseViewHolder

abstract class BaseViewHolder<T>(override val containerView: View) :
RecyclerView.ViewHolder(containerView),
LayoutContainer

BaseViewAdapter

abstract class BaseAdapter<T>(
@LayoutRes open val layoutId: Int,
private val dataList: ArrayList<T>?
) : RecyclerView.Adapter<BaseViewHolder<T>>() {

abstract fun setViewHolder(parent: ViewGroup): BaseViewHolder<T>
abstract fun bind(containerView: View, item: T)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<T> {
return setViewHolder(parent)
}

override fun onBindViewHolder(holder: BaseViewHolder<T>, position: Int) {
bind(holder.containerView, dataList!![position])
}

override fun getItemCount(): Int = dataList!!.size

override fun getItemViewType(position: Int): Int =
if (dataList!!.size == 0) IS_EMPTY else IS_NOT_EMPTY
}

How to use databinding with generics in an adapter class?

The error suggests, that you need to cast the item to type Item instead of I. This makes sense because your layout declares the variable with type Item. The ViewHolder however provides an object of an unconstrained type I, which clearly doesn't conform to Item.

Instead of a cast, you can constrain the generic type I to be a subtype of Item:

class ItemAdapter<I: Item>(...) {
...
inner class ItemViewHolder<I: Item>(...) {...}
}

This should make the assignment dataBinding.item = item valid.

Somewhat unrelated: I think it's unnecessary to redeclare the generic type I for the inner class. You should be able to just use the I of the outer class ItemAdapter<I: Item>.

I have to admit I don't fully understand your code, so I may be missing something. For your reference, here is a very similar implementation of an adapter with data binding I use in my project. It allows configurable layout. The only thing it assumes is a binding variable called model.

class DataBindingListAdapter<T>(@LayoutRes private val itemLayout: Int, diffCallback: DiffUtil.ItemCallback<T>) : ListAdapter<T, DataBindingListAdapter<T>.ViewHolder>(diffCallback) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, itemLayout, parent, false)
return ViewHolder(binding)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}

inner class ViewHolder(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(model: T?) {
binding.setVariable(BR.model, model)
binding.executePendingBindings()
}
}

}


Related Topics



Leave a reply



Submit