David
David

Reputation: 3105

Android cards multiple layouts based on position

I want to use different layouts based on card position on screen. For example I need that first card (position 0 uses layout first_card.xml) and other cards should use other_cards.xml layout and it should be positioned horizontally (see picture below (picture taken from official google site)). enter image description here

Here's my code

public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder>{
    private List<CardData> mItems;
    private Context context;
    private SendInfoToCards cardDatas;
    private boolean mainMenu;
    private static final int FIRST = 1;
    private static final int SECOND = 2;

    public CardAdapter(Context c, SendInfoToCards datas, boolean main) {
        super();
        this.context = c;
        this.cardDatas = datas;
        mItems = datas.getList();
        this.mainMenu = main;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
        if(!mainMenu) {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false);
            ViewHolder viewHolder = new ViewHolder(v);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    TextView city = (TextView) v.findViewById(R.id.tv_movie);
                    String fragmentSwitcher = city.getText().toString();
                    if (fragmentSwitcher.equals("Zemelapis")) {
                        switchFragments(0);
                    } else {
                        switchFragments(1);
                    }
                }
            });
            return viewHolder;
        } else {
            View v;
            if(position == 0){
                v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
            }else{
                v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
            }
            ViewHolder viewHolder = new ViewHolder(v);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    TextView city = (TextView) v.findViewById(R.id.tv_movie);
                    String fragmentSwitcher = city.getText().toString();
                }
            });
            return viewHolder;
        }
    }

    private void switchFragments(int nr){
        if(nr == 0){
            Log.d("TAG", "mapo");
        } else {
            Intent intent = new Intent(context, DetailTourActivity.class);
            context.startActivity(intent);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        if(i != 0){

        } else {
            CardData data = mItems.get(i);
            viewHolder.tvMovie.setText(data.getName());
            viewHolder.imgThumbnail.setImageResource(data.getThumbnail());
        }
    }

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

    class ViewHolder extends RecyclerView.ViewHolder{
        public ImageView imgThumbnail;
        public TextView tvMovie;

        public ViewHolder(View itemView) {
            super(itemView);
            imgThumbnail = (ImageView)itemView.findViewById(R.id.img_thumbnail);
            tvMovie = (TextView)itemView.findViewById(R.id.tv_movie);
        }
    }
}

Sadly but onCreateViewHolder method second argument position is always 0. Why is that? How to fix it?

Upvotes: 1

Views: 396

Answers (2)

Much Overflow
Much Overflow

Reputation: 3140

Here is what your adapter class should roughly look like

public class CardAdapter extends RecyclerView.Adapter<RecylerView.ViewHolder>{
    private List<CardData> mItems;
    private Context context;
    private SendInfoToCards cardDatas;
    private boolean mainMenu;
    private static final int FIRST = 1;
    private static final int SECOND = 2;

    public CardAdapter(Context c, SendInfoToCards datas, boolean main) {
        super();
        this.context = c;
        this.cardDatas = datas;
        mItems = datas.getList();
        this.mainMenu = main;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
        if(type == SECOND){
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
            return new SecondViewViewHolder(v);
        } else {
            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
            return new FirstViewViewHolder(v);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        if(viewHolder instanceof SecondViewViewHolder){
            SecondViewViewHolder secondViewViewHolder = (SecondViewViewHolder) viewHolder;
            // bind second views
            secondViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
        } else {
            FirstViewViewHolder firstViewViewHolder = (FirstViewViewHolder) viewHolder;
            // bind firs view
            firstViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return FIRST;
        } else {
            return SECOND;
        }
    }

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

    class FirstViewViewHolder extends RecyclerView.ViewHolder{
        public ImageView imgThumbnail;
        public TextView tvMovie;

        public FirstViewViewHolder(View itemView) {
            super(itemView);
            imgThumbnail = (ImageView)itemView.findViewById(R.id.img_thumbnail);
            tvMovie = (TextView)itemView.findViewById(R.id.tv_movie);
        }
    }

    class SecondViewViewHolder extends RecyclerView.ViewHolder{
        public ImageView imgThumbnail;
        public TextView tvMovie;
        public ImageView imgThumbnail2;
        public TextView tvMovie2;

        public SecondViewViewHolder(View itemView) {
            super(itemView);
            //find viewby id
        }
    }
}

What is changed

Since recyclerview can handle multiple view types you can decide which layout to inflate depending on some condition. You do this by overriding getItemViewTypeMethod() of the RecylerView Adapter

 @Override
 public int getItemViewType(int position) {
    if (position == 0) {
         return FIRST;
    } else {
       return SECOND;
    }
}

When creating viewholder. the onCreateViewHolder method will get the view type. Based on the type you should inflate the appropriate layout and return the appropriate viewholder

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
   if(type == SECOND){
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
        return new SecondViewViewHolder(v);
    } else {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
        return new FirstViewViewHolder(v);
    }
}

Since there are two layouts involved, I have created two View Holders to hold first and second layouts. (FirstViewHolder & SecondViewHolder). Because of this you have to set your adapter's ViewHolder type to the base ViewHolder class like this

public class CardAdapter extends RecyclerView.Adapter<RecylerView.ViewHolder>{
   ...
}

and now in the onBindViewHolder method, you can determine which ViewHolder is currently being bound and do your work accordingly

 @Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
    if(viewHolder instanceof SecondViewViewHolder){
        SecondViewViewHolder secondViewViewHolder = (SecondViewViewHolder) viewHolder;
        // bind second views
            secondViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
    } else {
        FirstViewViewHolder firstViewViewHolder = (FirstViewViewHolder) viewHolder;
        // bind firs view
        firstViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
    }
}

Check this for an example

Upvotes: 2

archived
archived

Reputation: 647

The thing is that the second parameter is not position, it is viewType.

From the docs:

public abstract VH onCreateViewHolder (ViewGroup parent, int viewType)

Look at public void onBindViewHolder (VH holder, int position, List<Object> payloads), this is what you actually need

Upvotes: 1

Related Questions