Create a Generic Adapter

3

Good Night! dear, I'm with a doubt, has anyone ever come across the situation of having multiple lists but with similar information? a simple listing of information coming from the service? I wonder if it's possible to create a generic Adapter. Thanks in advance

    
asked by anonymous 12.11.2017 / 02:31

2 answers

2

Yes it is possible.

However, you will need to write a ViewHolder for each item layout type, a ViewHolderFactory, and your Model will need to implement an interface.

ViewHolder is required to connect Model with Views. The ViewHolderFactory for the adapter to build the ViewHolder that it will use. The interface that Model implements is used to indicate, through the viewType parameter of the onCreateViewHolder() method, which layout to use to create the ViewHolder.

For this to work, you need to create some abstractions:

  • TypeProvider interface to be implemented by Model

    public interface TypeProvider {
        int type(ViewHolderFactory viewHolderFactory);
    }
    
  • GenericViewHolder , an abstract class that each ViewHolder should inherit.

    public abstract class GenericViewHolder<T> extends RecyclerView.ViewHolder{
        public GenericViewHolder(View itemView) {
            super(itemView);
        }
    
        public abstract void bind(T item);
    }
    
  • ViewHolderFactory interface, each ViewModel type will be built by the corresponding implementation of this class.

    public interface ViewHolderFactory {        
        int type();
        GenericViewHolder createViewHolder(View parent);
    }
    

These abstractions will allow the adapter to work with any type of data (Model) and ViewHolder.

Adapter implementation:

public class GenericRecyclerAdapter extends RecyclerView.Adapter<GenericViewHolder> {

    private ArrayList<TypeProvider> items;
    private ViewHolderFactory viewHolderFactory;

    public GenericRecyclerAdapter(ArrayList<TypeProvider> items, ViewHolderFactory viewHolderFactory){

        this.items = items;
        this.viewHolderFactory = viewHolderFactory;
    }

    @Override
    public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext())
                                  .inflate(viewType, parent, false);
        return viewHolderFactory.createViewHolder(view);
    }

    @Override
    public void onBindViewHolder(GenericViewHolder holder, int position) {
        holder.bind(items.get(position));
    }

    @Override
    public int getItemViewType(int position) {
        return items.get(position).type(viewHolderFactory);
    }

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

}

The infrastructure is done.

Example usage.

  • Model, in this example represents an image and its name.

    public class Model implements TypeProvider {
    
        private int imageId;
        private String imageName;
    
        public Model(int imageId, String imageName){
            this.imageId = imageId;
            this.imageName = imageName;
        }
    
        @Override
        public int type(ViewHolderFactory viewHolderFactory) {
            // Ao delegar para o viewHolderFactory a obtenção do Layout
            // evita-se que o model dependa do framework Android, neste caso da classe R.
            return viewHolderFactory.type();
        }
    
        public int getImageId() {
            return imageId;
        }
    
        public String getImageName() {
            return imageName;
        }
    }
    
  • ViewHolder, follows the classic implementation: in the constructor, the references to the layout views are obtained and in the bind() method, the values of the model are assigned to the respective views:

    public class ModelViewHolder extends GenericViewHolder<Model> {
    
        //A ser usado pelo ViewHolderFactory para, através do model,
        //indicar, no método onCreateViewHolder(), qual o layout a usar
        public static final int LAYOUT = R.layout.item_view;
    
        private ImageView mImageView;
        private TextView mTextView;
    
        public ModelViewHolder(View itemView) {
            super(itemView);
            mImageView = (ImageView) itemView.findViewById(R.id.image);
            mTextView = (TextView)itemView.findViewById(R.id.text);
        }
    
        @Override
        public void bind(Model item) {
            mTextView.setText(item.getImageName());
            mImageView.setImageResource(item.getImageId());
        }
    }
    
  • ViewHolderFactory

    public class ModelViewHolderFactory implements ViewHolderFactory {
        @Override
        public int type() {
            return ModelViewHolder.LAYOUT;
        }
    
        @Override
        public GenericViewHolder createViewHolder(View parent) {
            return new ModelViewHolder(parent);
        }
    }
    
  • Put everything together in Activity

    public class GenericAdapterActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
            ArrayList<TypeProvider> items = new ArrayList<>();
    
            items.add(new Model(R.mipmap.ic_launcher, "item1"));
            items.add(new Model(R.mipmap.ic_launcher, "item2"));
            items.add(new Model(R.mipmap.ic_launcher, "item3"));
            items.add(new Model(R.mipmap.ic_launcher, "item4"));
            items.add(new Model(R.mipmap.ic_launcher, "item5"));
    
            recyclerView.setHasFixedSize(true);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
    
            ModelViewHolderFactory modelViewHolderFactory = new ModelViewHolderFactory();
    
            GenericRecyclerAdapter adapter = new GenericRecyclerAdapter(items, modelViewHolderFactory);
            recyclerView.setAdapter(adapter);
        }
    }
    

It seems like a lot of work, however it's almost as much as having to do multiple adapters. Extra work is just the creation of abstractions, but they are created only once. ViewHolder and Model will always have to be created, regardless of approach.
However, this "is more object-oriented" than having multiple Adapters.

Source of inspiration:

  • Writing Better Adapters

    Note: post indicates a link for an improved implementation that allows items of different data types and layouts to be added to the same list.

12.11.2017 / 23:52
0

One option would be to create a Pessoa.java class and instantiate the other classes, for example Client and Official, in case you would make an adapter that would have the common values of the classes. Example:

Parent class:

public class Pessoa {
    private String nome;

    public Pessoa() {
    }

    public Pessoa(String nome) {
        this.nome = nome;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    @Override
    public String toString() {
        return "Pessoa{" +
                "nome='" + nome + '\'' +
                '}';
    }
}

Child classes:

public class Funcionario {
    private double salario;
    private Pessoa p;

    public Funcionario() {
    }

    public Funcionario(double salario, Pessoa p) {
        this.salario = salario;
        this.p = p;
    }

    public double getSalario() {
        return salario;
    }

    public void setSalario(double salario) {
        this.salario = salario;
    }

    public Pessoa getP() {
        return p;
    }

    public void setP(Pessoa p) {
        this.p = p;
    }

    @Override
    public String toString() {
        return "Funcionario{" +
                "salario=" + salario +
                ", p=" + p +
                '}';
    }
}

public class Cliente {
    private int idade;
    private Pessoa p;

    public Cliente() {
    }

    public Cliente(int idade, Pessoa p) {
        this.idade = idade;
        this.p = p;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public Pessoa getP() {
        return p;
    }

    public void setP(Pessoa p) {
        this.p = p;
    }

    @Override
    public String toString() {
        return "Cliente{" +
                "idade=" + idade +
                ", p=" + p +
                '}';
    }
}

Adapter:

public class Adapter extends BaseAdapter{
    private Context contexto;

    private ArrayList<Pessoa> ps;
    private LayoutInflater inflater;

    public TextView tvNome;

    public Adapter(Context contexto, ArrayList<Pessoa> ps) {
        this.contexto = contexto;
        this.ps = ps;
        inflater = LayoutInflater.from(contexto);
    }

    public void notifyDataChanged() {
        super.notifyDataSetChanged();
    }

    public void adicionar(Pessoa p) {
        ps.add(p);
    }

    public void remover(Pessoa p) {
        ps.remove(p);
    }

    @Override
    public int getCount() {
        return ps.size();
    }

    @Override
    public Object getItem(int posicao) {
        return ps.get(posicao);
    }

    @Override
    public long getItemId(int posicao) {
        return posicao;
    }

    @Override
    public View getView(int posicao, View convertView, ViewGroup parent) {
        Pessoa p = ps.get(posicao);

        if(convertView == null) {
            convertView = inflater.inflate(R.layout.linha, null);
        }

        //Instancia os objetos
        tvNome = (TextView) convertView.findViewById(R.id.tv_nome);

        tvNome.setText(p.getNome());

        return convertView;
    }
}

I made an extremely simple example just to show this possibility, any questions, just ask

    
12.11.2017 / 03:24