Recyclerview ViewHolder : onCreateViewHolder , view binding and onBindViewHolder in the same class

会有一股神秘感。 提交于 2019-12-13 17:20:59

问题


In RecyclerView.ViewHolder , a view is passed to the constructor. This view is an inflated layout. The RecyclerView.ViewHolder only bind the views with findViewById.

The RecyclerView.Adapter has the role to :

  • inflate the layout in onCreateViewHolder
  • bind the ViewHolder with the data set with onBindViewHolder

I have several RecyclerViews displaying the same list of data. I want each RecyclerView to display differently with their respective ViewHolder. My goal is to use the same generic RecyclerView.Adapter for each RecyclerView. So the ViewHolder has to be passed to this RecyclerView.Adapter. I'm trying to implement a ViewHolder that can implement all 3 methods. Any idea how to achieve this ?

I looked at different projects. The best so far, AdapterDelegates circumvents this problem. But you still have to deal with AdapterDelegate and ViewHolder classes . How to combine both in the same class ? ( without inner class )


回答1:


As promised I have created a new answer, which does not use reflection. It technically uses two classes (a factory class and a holder class), not one, but is just the same. The code is tested, and it works.

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>
{
    List<Object> _data;
    MyViewHolder.Factory _factory;

    MyAdapter(List data, MyViewHolder.Factory factory)
    {
        _data = data;
        _factory = factory;
        if (_data == null || _factory == null)
        {
            throw new NullPointerException("Both data and factory cannot be null!");
        }
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        return _factory.createViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position)
    {
        holder.bindViewHolder(_data.get(position));
    }

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

MyViewHolder.java

public abstract class MyViewHolder extends RecyclerView.ViewHolder
{
    public interface Factory
    {
        public abstract MyViewHolder createViewHolder(ViewGroup parent, int viewType);
    }

    public MyViewHolder(View itemView)
    {
        super(itemView);
    }

    public abstract void bindViewHolder(Object data);
}

MainActivity.java

public class MainActivity extends AppCompatActivity
{

    static class NameViewHolder extends MyViewHolder
    {
        // If preferred, the following can be created anonymously in code
        static class Factory implements MyViewHolder.Factory
        {
            @Override
            public MyViewHolder createViewHolder(ViewGroup parent, int viewType)
            {
                View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_recycler_item, parent, false);
                return new NameViewHolder(v);
            }
        };

        TextView tv;
        NameViewHolder(View v)
        {
            super(v);
            tv = (TextView)v.findViewById(R.id.textView);
        }

        @Override
        public void bindViewHolder(Object data)
        {
            tv.setText((String)data);
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        RecyclerView rv = (RecyclerView)findViewById(R.id.my_recycler_view);
        rv.setHasFixedSize(true);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(layoutManager);

        ArrayList<String> data = new ArrayList();
        for (int i = 1; i < 100; ++i)
            data.add(Integer.toString(1000 + i));

        MyAdapter adapter = new MyAdapter(data, new NameViewHolder.Factory());
        rv.setAdapter(adapter);
    }

}



回答2:


I would suggest creating an abstract ViewHolder parent Class. It should have a static instantate method and a bindViewHolder method. Design the Adapter constructor to accept the ViewHolder parent Class object. When used, pass the child Class Object, and in onCreateViewHolder, use Reflection to create the child ViewHolder. When you get an onBindViewHolder, just pass it to the ViewHolder.

Here is a working example. I tested it, and it worked. I have removed non-essential code.

MainActivity.java

public class MainActivity extends AppCompatActivity
{

    static class NameViewHolder extends MyViewHolder
    {
        TextView tv;
        public static MyViewHolder instantate(ViewGroup parent, int viewType)
        {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_recycler_item, parent, false);
            MyViewHolder vh = new NameViewHolder(v);
            return vh;
        }
        NameViewHolder(View v)
        {
            super(v);
            tv = (TextView)v.findViewById(R.id.textView);
        }

        @Override
        public void bindViewHolder(Object data)
        {
            tv.setText((String)data);
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RecyclerView rv = (RecyclerView)findViewById(R.id.my_recycler_view);
        rv.setHasFixedSize(true);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        rv.setLayoutManager(layoutManager);

        ArrayList<String> data = new ArrayList();
        for (int i = 1; i < 100; ++i)
            data.add(Integer.toString(1000 + i));
        MyAdapter adapter = new MyAdapter(data, NameViewHolder.class);
        rv.setAdapter(adapter);
    }
}

MyViewHolder.java

public abstract class MyViewHolder extends RecyclerView.ViewHolder
{
    // The following has to be declared in sub class. As Java 7 does not support static interface, we commented it out here
    //public static MyViewHolder instantate(ViewGroup parent, int viewType);

    public MyViewHolder(View itemView)
    {
        super(itemView);
    }
    public abstract void bindViewHolder(Object data);
}

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>
{
    List<Object> _data;
    Class _holderClass;

    MyAdapter(List data, Class holderClass)
    {
        _data = data;
        _holderClass = holderClass;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        MyViewHolder vh = null;
        try
        {
            Class[] cArg = {ViewGroup.class, int.class};
            Method instantateMethod = _holderClass.getMethod("instantate", cArg);
            vh = (MyViewHolder) instantateMethod.invoke(null, parent, viewType);
        }
        catch (NoSuchMethodException e)
        {
            e.printStackTrace();
        }
        catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }
        catch (IllegalAccessException e)
        {
            e.printStackTrace();
        }
        return vh;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position)
    {
        holder.bindViewHolder(_data.get(position));
    }

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

}



回答3:


You can by making passing R.layout to your RVadapter

static int layoutResource;

public RVadapter(Context context, int id) {
    layoutResource = id;
}

then you can do this in your ViewHolder

if(RVadapter.layoutResource == R.layout.message_layout) {
            message_view = (CardView) itemView.findViewById(R.id.card_message);
            message_image = (ImageView) itemView.findViewById(R.id.message_image);
            message_name = (TextView) itemView.findViewById(R.id.messenger);
            message_details = (TextView) itemView.findViewById(R.id.details);
        }
        else if(RVadapter.layoutResource == R.layout.guide_layout){
            guide_name = (TextView) itemView.findViewById(R.id.new_travel_name);
            guide_gendr_age = (TextView) itemView.findViewById(R.id.new_travel_gender_age);
            guide_tours = (TextView) itemView.findViewById(R.id.new_travel_tour);
            rb = (RatingBar) itemView.findViewById(R.id.new_travel_rb);
        }


来源:https://stackoverflow.com/questions/39238674/recyclerview-viewholder-oncreateviewholder-view-binding-and-onbindviewholder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!