จะสร้าง RecyclerView ด้วยมุมมองหลายประเภทได้อย่างไร


864

จากhttps://developer.android.com/preview/material/ui-widgets.html

เมื่อเราสร้างRecyclerView.Adapterเราจะต้องระบุViewHolderว่าจะผูกกับอะแดปเตอร์

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private String[] mDataset;

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);

        //findViewById...

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.mTextView.setText(mDataset[position]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

ดังนั้นมันเป็นไปได้ที่จะสร้างRecyclerViewด้วยมุมมองหลายประเภท?


5
ด้านบนของคำตอบของ Anton ให้ดูคำตอบของฉันที่นี่: stackoverflow.com/questions/25914003/…
ticofab

ตรวจสอบลิงค์เหล่านี้ซึ่งอาจมีประโยชน์สำหรับคุณ stackoverflow.com/a/39972276/3946958
Ravindra Kushwaha

1
กวดวิชาที่ดีที่นี่: guide.codepath.com/android/…
Gene Bo

ตรวจสอบลิงค์เหล่านี้มันสามารถใช้การได้stackoverflow.com/questions/39971350/หากมีปัญหามากกว่าโปรดแจ้งให้เราทราบ
Ravindra Kushwaha

ห้องสมุดที่ยอดเยี่ยมที่จะใช้มันgithub.com/vivchar/RendererRecyclerViewAdapter
Vitaly

คำตอบ:


1269

ใช่มันเป็นไปได้ เพียงแค่ใช้getItemViewType ()และดูแลของพารามิเตอร์ในviewTypeonCreateViewHolder()

ดังนั้นคุณทำสิ่งที่ชอบ:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    class ViewHolder0 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder0(View itemView){
        ...
        }
    }

    class ViewHolder2 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder2(View itemView){
        ...
    }

    @Override
    public int getItemViewType(int position) {
        // Just as an example, return 0 or 2 depending on position
        // Note that unlike in ListView adapters, types don't have to be contiguous
        return position % 2 * 2;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case 0: return new ViewHolder0(...);
             case 2: return new ViewHolder2(...);
             ...
         }
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        switch (holder.getItemViewType()) {
            case 0:
                ViewHolder0 viewHolder0 = (ViewHolder0)holder;
                ...
                break;

            case 2:
                ViewHolder2 viewHolder2 = (ViewHolder2)holder;
                ...
                break;
        }
    }
}

3
นั่นเป็นประเด็นของฉันเนื่องจากมี ViewHolder เพียงหนึ่งเดียวเท่านั้นใน RecyclerView.Adapter ที่คุณจะเพิ่มหลายรายการในนั้นได้อย่างไร
Pongpat

48
จากนั้นคุณต้องโยนประเภทผู้ชมในวิธีการ onBindViewHolder () ซึ่งฉันคิดว่ามันเอาชนะวัตถุประสงค์ประเภททั่วไป ขอบคุณมากสำหรับคำตอบของคุณ
Pongpat

32
คุณสามารถสร้าง BaseHolder และขยายมันสำหรับประเภทที่จำเป็นทั้งหมด จากนั้นเพิ่มนามธรรม setData ซึ่งจะเป็น overriden (overrode?) ในผู้ดำเนินการ วิธีนี้คุณจะให้ความแตกต่างของประเภทการจัดการภาษา แม้ว่าจะใช้งานได้หากคุณมีชุดข้อมูลเดียวที่ทุกรายการสามารถตีความได้
DariusL

2
ไฟล์เลย์เอาต์ที่แตกต่างกันคืออะไร optionMenuItemฉันต้องการที่จะเปลี่ยนรูปแบบได้ที่ เป็นไปได้อย่างไร? @AntonSavin
Pratik Butani

5
ViewHolders ของคุณควรจะคงที่หากพวกเขาอยู่ในอะแดปเตอร์ RecyclerView ของคุณ
Samer

88

หากเลย์เอาต์สำหรับประเภทมุมมองมีเพียงไม่กี่รายการและการเชื่อมโยงแบบล็อกจะง่ายให้ทำตามวิธีแก้ไขของ Anton
แต่รหัสจะยุ่งเหยิงหากคุณต้องการจัดการเลย์เอาต์ที่ซับซ้อนและ logics ที่มีผลผูกพัน

ฉันเชื่อว่าวิธีแก้ไขปัญหาต่อไปนี้จะมีประโยชน์สำหรับคนที่ต้องจัดการกับประเภทมุมมองที่ซับซ้อน

คลาส DataBinder พื้นฐาน

abstract public class DataBinder<T extends RecyclerView.ViewHolder> {

    private DataBindAdapter mDataBindAdapter;

    public DataBinder(DataBindAdapter dataBindAdapter) {
        mDataBindAdapter = dataBindAdapter;
    }

    abstract public T newViewHolder(ViewGroup parent);

    abstract public void bindViewHolder(T holder, int position);

    abstract public int getItemCount();

......

}

ฟังก์ชั่นที่จำเป็นในการกำหนดในคลาสนี้ค่อนข้างคล้ายกับคลาสอะแดปเตอร์เมื่อสร้างประเภทมุมมองเดียว
สำหรับแต่ละประเภทมุมมองให้สร้างคลาสโดยขยาย DataBinder นี้

ตัวอย่างคลาส DataBinder

public class Sample1Binder extends DataBinder<Sample1Binder.ViewHolder> {

    private List<String> mDataSet = new ArrayList();

    public Sample1Binder(DataBindAdapter dataBindAdapter) {
        super(dataBindAdapter);
    }

    @Override
    public ViewHolder newViewHolder(ViewGroup parent) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
            R.layout.layout_sample1, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void bindViewHolder(ViewHolder holder, int position) {
        String title = mDataSet.get(position);
        holder.mTitleText.setText(title);
    }

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

    public void setDataSet(List<String> dataSet) {
        mDataSet.addAll(dataSet);
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView mTitleText;

        public ViewHolder(View view) {
            super(view);
            mTitleText = (TextView) view.findViewById(R.id.title_type1);
        }
    }
}

เพื่อจัดการคลาส DataBinder ให้สร้างคลาสอะแด็ปเตอร์

คลาสฐาน DataBindAdapter

abstract public class DataBindAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return getDataBinder(viewType).newViewHolder(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        int binderPosition = getBinderPosition(position);
        getDataBinder(viewHolder.getItemViewType()).bindViewHolder(viewHolder, binderPosition);
    }

    @Override
    public abstract int getItemCount();

    @Override
    public abstract int getItemViewType(int position);

    public abstract <T extends DataBinder> T getDataBinder(int viewType);

    public abstract int getPosition(DataBinder binder, int binderPosition);

    public abstract int getBinderPosition(int position);

......

}

สร้างคลาสโดยขยายคลาสพื้นฐานนี้จากนั้นสร้างอินสแตนซ์คลาส DataBinder และแทนที่เมธอด abstract

  1. getItemCount
    ส่งคืนจำนวนรวมของ DataBinders

  2. getItemViewType
    กำหนดตรรกะการทำแผนที่ระหว่างตำแหน่งอะแดปเตอร์และประเภทมุมมอง

  3. getDataBinder
    ส่งคืนอินสแตนซ์ DataBinder ตามชนิดมุมมอง

  4. getPosition
    กำหนดแปลงตรรกะเป็นตำแหน่งอะแดปเตอร์จากตำแหน่งใน DataBinder ที่ระบุ

  5. getBinderPosition
    กำหนดแปลงตรรกะเป็นตำแหน่งใน DataBinder จากตำแหน่งอะแดปเตอร์

หวังว่าโซลูชันนี้จะเป็นประโยชน์
ฉันออกจากการแก้ปัญหาและตัวอย่างรายละเอียดเพิ่มเติมใน GitHub ดังนั้นโปรดอ้างอิงลิงค์ต่อไปนี้หากคุณต้องการ
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter


4
ฉันสับสนเล็กน้อยกับรหัสของคุณบางทีคุณสามารถช่วยฉันฉันไม่ต้องการให้มุมมองของฉันถูกกำหนดโดยตำแหน่งในรายการ แต่จากมุมมองของพวกเขา ดูเหมือนว่ามุมมองในรหัสของคุณจะถูกกำหนดตามตำแหน่งของพวกเขาคือ ดังนั้นหาก im บนตำแหน่ง 1 มุมมอง 1 จะปรากฏขึ้นตำแหน่ง 3 มุมมอง 3 จะปรากฏขึ้นและทุกอย่างอื่นจะแสดงมุมมองตำแหน่ง 2 ฉันไม่ต้องการให้มุมมองของฉันอยู่ที่ตำแหน่ง แต่เป็น viewtypes - ดังนั้นหากฉันระบุว่าประเภทการดูคือรูปภาพมันควรจะแสดงรูปภาพ ฉันจะทำสิ่งนั้นได้อย่างไร
Simon

ขออภัยฉันไม่เข้าใจคำถามของคุณอย่างเต็มที่ ... แต่คุณต้องเขียนตรรกะบางอย่างเพื่อผูกตำแหน่งและมุมมอง
yqritc

1
รหัสนี้ไม่สับสนนี่คือรูปแบบของ RecyclerView Adaptor และควรยกเว้นเช่นเดียวกับคำตอบที่ถูกต้องของคำถาม ตามลิงค์ของ @yqritc ใช้เวลาเล็กน้อยในการค้นพบและคุณจะมีรูปแบบที่สมบูรณ์แบบสำหรับ RecyclerView ที่มีเลย์เอาต์ชนิดต่าง ๆ
Stoycho Andreev

ใหม่ที่นี่public class DataBinder<T extends RecyclerView.ViewHolder>มีคนบอกฉันได้ไหมว่าอะไรที่<T someClass>เรียกว่าดังนั้นฉันสามารถ google ถ้าฉันได้คำ นอกจากนี้เมื่อฉันบอกabstract public class DataBinder<T extends RecyclerView.ViewHolder>ว่านั่นหมายความว่าคลาสนี้เป็นประเภทViewHolderดังนั้นผลลัพธ์ทุกคลาสที่ขยายคลาสนี้จะเป็นประเภทviewHolderที่เป็นความคิดหรือไม่
rgv

1
@cesards คุณทำให้ฉันต้องรีเฟรชความรู้ของฉันอีกครั้งเกี่ยวกับ polymorphism lol .... Java ไม่เลวเลย
Paul Okeke

37

ด้านล่างไม่ใช่รหัสเทียมและฉันได้ทำการทดสอบและมันได้ผลสำหรับฉัน

ฉันต้องการสร้าง headerview ใน recyclerview ของฉันแล้วแสดงรายการรูปภาพใต้ส่วนหัวที่ผู้ใช้สามารถคลิก

ฉันใช้สวิทช์สองสามตัวในรหัสของฉันไม่รู้ว่าเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการทำเช่นนี้หรือไม่โปรดแสดงความคิดเห็นของคุณ:

   public class ViewHolder extends RecyclerView.ViewHolder{

        //These are the general elements in the RecyclerView
        public TextView place;
        public ImageView pics;

        //This is the Header on the Recycler (viewType = 0)
        public TextView name, description;

        //This constructor would switch what to findViewBy according to the type of viewType
        public ViewHolder(View v, int viewType) {
            super(v);
            if (viewType == 0) {
                name = (TextView) v.findViewById(R.id.name);
                decsription = (TextView) v.findViewById(R.id.description);
            } else if (viewType == 1) {
                place = (TextView) v.findViewById(R.id.place);
                pics = (ImageView) v.findViewById(R.id.pics);
            }
        }
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent,
                                         int viewType)
    {
        View v;
        ViewHolder vh;
        // create a new view
        switch (viewType) {
            case 0: //This would be the header view in my Recycler
                v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.recyclerview_welcome, parent, false);
                vh = new ViewHolder(v,viewType);
                return  vh;
            default: //This would be the normal list with the pictures of the places in the world
                v = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.recyclerview_picture, parent, false);
                vh = new ViewHolder(v, viewType);
                v.setOnClickListener(new View.OnClickListener(){

                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(mContext, nextActivity.class);
                        intent.putExtra("ListNo",mRecyclerView.getChildPosition(v));
                        mContext.startActivity(intent);
                    }
                });
                return vh;
        }
    }

    //Overriden so that I can display custom rows in the recyclerview
    @Override
    public int getItemViewType(int position) {
        int viewType = 1; //Default is 1
        if (position == 0) viewType = 0; //if zero, it will be a header view
        return viewType;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //position == 0 means its the info header view on the Recycler
        if (position == 0) {
            holder.name.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"name clicked", Toast.LENGTH_SHORT).show();
                }
            });
            holder.description.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,"description clicked", Toast.LENGTH_SHORT).show();
                }
            });
            //this means it is beyond the headerview now as it is no longer 0. For testing purposes, I'm alternating between two pics for now
        } else if (position > 0) {
           holder.place.setText(mDataset[position]);
            if (position % 2 == 0) {
               holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic1));
            }
            if (position % 2 == 1) {
                holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic2));
            }

        }
    }

นี่เป็นสิ่งที่ดีถ้าฉันต้องการส่วนหัวหลายตำแหน่งในตำแหน่งไดนามิก สมมติว่ารายการของรายการที่มีส่วนหัวกำหนดหมวดหมู่ วิธีการแก้ปัญหาของคุณดูเหมือนจะต้องมีส่วนหัวพิเศษที่จะอยู่ในตำแหน่งที่กำหนดไว้ล่วงหน้า
Bassinator

22

ใช่มันเป็นไปได้

เขียนเจ้าของมุมมองทั่วไป:

    public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
    public GenericViewHolder(View itemView) {
        super(itemView);
    }

    public abstract  void setDataOnView(int position);
}

จากนั้นสร้างผู้ถือมุมมองของคุณและทำให้พวกเขาขยาย GenericViewHolder ตัวอย่างเช่นอันนี้:

     public class SectionViewHolder extends GenericViewHolder{
    public final View mView;
    public final TextView dividerTxtV;

    public SectionViewHolder(View itemView) {
        super(itemView);
        mView = itemView;
        dividerTxtV = (TextView) mView.findViewById(R.id.dividerTxtV);
    }

    @Override
    public void setDataOnView(int position) {
        try {
            String title= sections.get(position);
            if(title!= null)
                this.dividerTxtV.setText(title);
        }catch (Exception e){
            new CustomError("Error!"+e.getMessage(), null, false, null, e);
        }
    }
}

ดังนั้นคลาส RecyclerView.Adapter จะมีลักษณะดังนี้:

public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter<MyClassRecyclerViewAdapter.GenericViewHolder> {

@Override
public int getItemViewType(int position) {
     // depends on your problem
     switch (position) {
         case : return VIEW_TYPE1;
         case : return VIEW_TYPE2;
         ...
     }
}

    @Override
   public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType)  {
    View view;
    if(viewType == VIEW_TYPE1){
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout1, parent, false);
        return new SectionViewHolder(view);
    }else if( viewType == VIEW_TYPE2){
        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout2, parent, false);
        return new OtherViewHolder(view);
    }
    // Cont. other view holders ...
    return null;
   }

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

วิธีการใช้งานแล้วในกิจกรรม? ประเภทควรจะผ่านคิดวิธี?
skm

วิธีใช้อแด็ปเตอร์นี้ในกิจกรรม และมันรู้ได้อย่างไรว่า Type ไหนอยู่ในรายการ
skm

20

สร้าง ViewHolder ที่แตกต่างกันสำหรับเค้าโครงที่แตกต่างกัน

ป้อนคำอธิบายรูปภาพที่นี่
RecyclerView สามารถมีจำนวนผู้ดูที่คุณต้องการ แต่เพื่อให้อ่านง่ายขึ้นให้ดูวิธีการสร้างหนึ่งด้วยสอง ViewHolders

สามารถทำได้ในสามขั้นตอนง่ายๆ

  1. แทนที่ public int getItemViewType(int position)
  2. กลับ ViewHolders ที่แตกต่างกันขึ้นอยู่กับ ViewType ในonCreateViewHolder()วิธีการ
  3. เติมมุมมองตามรายการ viewType ในonBindViewHolder()วิธีการ

นี่คือข้อมูลโค้ดขนาดเล็ก

public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

   private static final int LAYOUT_ONE= 0;
   private static final int LAYOUT_TWO= 1;

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

   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

      View view =null;
      RecyclerView.ViewHolder viewHolder = null;

      if(viewType==LAYOUT_ONE)
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false);
          viewHolder = new ViewHolderOne(view);
      }
      else
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false);
          viewHolder= new ViewHolderTwo(view);
      }

      return viewHolder;
   }

   @Override
   public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {

      if(holder.getItemViewType()== LAYOUT_ONE)
      {
            // Typecast Viewholder 
            // Set Viewholder properties 
            // Add any click listener if any 
      }
      else {

        ViewHolderOne vaultItemHolder = (ViewHolderOne) holder;
        vaultItemHolder.name.setText(displayText);
        vaultItemHolder.name.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
            .......
           }
         });

       }

   }

  //****************  VIEW HOLDER 1 ******************//

   public class ViewHolderOne extends RecyclerView.ViewHolder {

      public TextView name;

      public ViewHolderOne(View itemView) {
         super(itemView);
         name = (TextView)itemView.findViewById(R.id.displayName);
     }
   }


   //****************  VIEW HOLDER 2 ******************//

   public class ViewHolderTwo extends RecyclerView.ViewHolder{

      public ViewHolderTwo(View itemView) {
         super(itemView);

        ..... Do something
      }
   }
}

getItemViewType (ตำแหน่ง int) เป็นกุญแจสำคัญ

ในความคิดของฉันจุดเริ่มต้นในการสร้าง recyclerView ประเภทนี้คือความรู้เกี่ยวกับวิธีการนี้ เนื่องจากวิธีนี้เป็นทางเลือกในการลบล้างดังนั้นจึงไม่สามารถมองเห็นได้ในคลาส RecylerView ตามค่าเริ่มต้นซึ่งจะทำให้นักพัฒนาหลายคน (รวมถึงฉัน) สงสัยว่าจะเริ่มต้นที่ใด เมื่อคุณรู้ว่าวิธีนี้มีอยู่แล้วการสร้าง RecyclerView ดังกล่าวจะเป็นจุดเชื่อมต่อ

ให้ดูตัวอย่างหนึ่งเพื่อพิสูจน์จุดของฉัน หากคุณต้องการแสดงสองเค้าโครงที่ตำแหน่งอื่นให้ทำเช่นนี้

@Override
public int getItemViewType(int position)
{
   if(position%2==0)       // Even position 
     return LAYOUT_ONE;
   else                   // Odd position 
     return LAYOUT_TWO;
}

ลิงค์ที่เกี่ยวข้อง:

ลองดูโครงการที่ฉันใช้งานไป


15

ใช่มันเป็นไปได้ ในอะแดปเตอร์ของคุณ getItemViewType Layout แบบนี้ ....

  public class MultiViewTypeAdapter extends RecyclerView.Adapter {

        private ArrayList<Model>dataSet;
        Context mContext;
        int total_types;
        MediaPlayer mPlayer;
        private boolean fabStateVolume = false;

        public static class TextTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            CardView cardView;

            public TextTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.cardView = (CardView) itemView.findViewById(R.id.card_view);
            }
        }

        public static class ImageTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            ImageView image;

            public ImageTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.image = (ImageView) itemView.findViewById(R.id.background);
            }
        }

        public static class AudioTypeViewHolder extends RecyclerView.ViewHolder {

            TextView txtType;
            FloatingActionButton fab;

            public AudioTypeViewHolder(View itemView) {
                super(itemView);

                this.txtType = (TextView) itemView.findViewById(R.id.type);
                this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab);
            }
        }

        public MultiViewTypeAdapter(ArrayList<Model>data, Context context) {
            this.dataSet = data;
            this.mContext = context;
            total_types = dataSet.size();
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

            View view;
            switch (viewType) {
                case Model.TEXT_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false);
                    return new TextTypeViewHolder(view);
                case Model.IMAGE_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false);
                    return new ImageTypeViewHolder(view);
                case Model.AUDIO_TYPE:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false);
                    return new AudioTypeViewHolder(view);
            }
            return null;
        }

        @Override
        public int getItemViewType(int position) {

            switch (dataSet.get(position).type) {
                case 0:
                    return Model.TEXT_TYPE;
                case 1:
                    return Model.IMAGE_TYPE;
                case 2:
                    return Model.AUDIO_TYPE;
                default:
                    return -1;
            }
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {

            Model object = dataSet.get(listPosition);
            if (object != null) {
                switch (object.type) {
                    case Model.TEXT_TYPE:
                        ((TextTypeViewHolder) holder).txtType.setText(object.text);

                        break;
                    case Model.IMAGE_TYPE:
                        ((ImageTypeViewHolder) holder).txtType.setText(object.text);
                        ((ImageTypeViewHolder) holder).image.setImageResource(object.data);
                        break;
                    case Model.AUDIO_TYPE:

                        ((AudioTypeViewHolder) holder).txtType.setText(object.text);

                }
            }
        }

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

สำหรับลิงค์อ้างอิง: https://www.journaldev.com/12372/android-recyclerview-example


ฉันจัดรูปแบบรหัสใหม่เพื่อเลียนแบบข้อมูลนี้และตอนนี้ทำงานได้อย่างสมบูรณ์ ปัญหาที่ฉันมีอยู่คือการปัดหน้าเว็บเกินกว่าจะทำให้เกิดปัญหาได้ Crash no More! แบบจำลองที่ยอดเยี่ยม ขอบคุณ . ทำได้ดี.
462990

ไม่พบสิ่งใดที่มีประโยชน์สำหรับวันจนกว่าฉันจะเห็นสิ่งนี้ขอบคุณ!
Itay Braha

7

ติดตามวิธีการแก้ปัญหาของ Anton มากับสิ่งนี้ViewHolderซึ่งถือ / จัดการ / มอบหมายประเภทของรูปแบบที่แตกต่างกัน แต่ไม่แน่ใจว่าการแทนที่โครงร่างใหม่จะทำงานเมื่อมุมมองการรีไซเคิลViewHolderไม่ใช่ประเภทของการม้วนข้อมูล

ดังนั้นโดยพื้นฐานแล้ว onCreateViewHolder(ViewGroup parent, int viewType)จะถูกเรียกเฉพาะเมื่อจำเป็นต้องใช้เค้าโครงมุมมองใหม่

getItemViewType(int position)จะถูกเรียกสำหรับviewType;

onBindViewHolder(ViewHolder holder, int position)จะถูกเรียกเสมอเมื่อรีไซเคิลมุมมอง (นำข้อมูลใหม่มาและพยายามแสดงด้วยViewHolder)

ดังนั้นเมื่อ onBindViewHolderViewHolderจะเรียกว่าเป็นความต้องการที่จะใส่ในรูปแบบมุมมองด้านขวาและปรับปรุง

วิธีที่ถูกต้องในการแทนที่เค้าโครงมุมมองสำหรับสิ่งที่ViewHolderจะนำเข้ามาหรือปัญหาใด ๆ ขอบคุณสำหรับความคิดเห็นใด ๆ !

public int getItemViewType(int position) {
    TypedData data = mDataSource.get(position);
    return data.type;
}

public ViewHolder onCreateViewHolder(ViewGroup parent, 
    int viewType) {
    return ViewHolder.makeViewHolder(parent, viewType);
}

public void onBindViewHolder(ViewHolder holder, 
    int position) {
    TypedData data = mDataSource.get(position);
    holder.updateData(data);
}

///
public static class ViewHolder extends 
    RecyclerView.ViewHolder {

    ViewGroup mParentViewGroup;
    View mCurrentViewThisViewHolderIsFor;
    int mDataType;

    public TypeOneViewHolder mTypeOneViewHolder;
    public TypeTwoViewHolder mTypeTwoViewHolder;

    static ViewHolder makeViewHolder(ViewGroup vwGrp, 
        int dataType) {
        View v = getLayoutView(vwGrp, dataType);
        return new ViewHolder(vwGrp, v, viewType);
    }

    static View getLayoutView(ViewGroup vwGrp, 
        int dataType) {
        int layoutId = getLayoutId(dataType);
        return LayoutInflater.from(vwGrp.getContext())
                             .inflate(layoutId, null);
    }

    static int getLayoutId(int dataType) {
        if (dataType == TYPE_ONE) {
            return R.layout.type_one_layout;
        } else if (dataType == TYPE_TWO) {
            return R.layout.type_two_layout;
        }
    }

    public ViewHolder(ViewGroup vwGrp, View v, 
        int dataType) {
        super(v);
        mDataType = dataType;
        mParentViewGroup = vwGrp;
        mCurrentViewThisViewHolderIsFor = v;

        if (data.type == TYPE_ONE) {
            mTypeOneViewHolder = new TypeOneViewHolder(v);
        } else if (data.type == TYPE_TWO) {
            mTypeTwoViewHolder = new TypeTwoViewHolder(v);
        }
    }

    public void updateData(TypeData data) {
        mDataType = data.type;
        if (data.type == TYPE_ONE) {
            mTypeTwoViewHolder = null;
            if (mTypeOneViewHolder == null) {
                View newView = getLayoutView(mParentViewGroup,
                               data.type);

                /**
                 *  how to replace new view with 
                    the view in the parent 
                    view container ???
                 */
                replaceView(mCurrentViewThisViewHolderIsFor, 
                            newView);
                mCurrentViewThisViewHolderIsFor = newView;

                mTypeOneViewHolder = 
                    new TypeOneViewHolder(newView);
            }
            mTypeOneViewHolder.updateDataTypeOne(data);

        } else if (data.type == TYPE_TWO){
            mTypeOneViewHolder = null;
            if (mTypeTwoViewHolder == null) {
                View newView = getLayoutView(mParentViewGroup, 
                               data.type);

                /**
                 *  how to replace new view with 
                    the view in the parent view 
                    container ???
                 */
                replaceView(mCurrentViewThisViewHolderIsFor, 
                            newView);
                mCurrentViewThisViewHolderIsFor = newView;

                mTypeTwoViewHolder = 
                    new TypeTwoViewHolder(newView);
            }
            mTypeTwoViewHolder.updateDataTypeOne(data);
        }
    }
}

public static void replaceView(View currentView, 
    View newView) {
    ViewGroup parent = (ViewGroup)currentView.getParent();
    if(parent == null) {
        return;
    }
    final int index = parent.indexOfChild(currentView);
    parent.removeView(currentView);
    parent.addView(newView, index);
}

แก้ไข: ViewHolder มีสมาชิก mItemViewType เพื่อเก็บมุมมอง

แก้ไข:ดูเหมือนใน onBindViewHolder (ผู้ถือ ViewHolder ตำแหน่ง int) ViewHolder ผ่านเข้ามาได้รับการหยิบขึ้นมา (หรือสร้างขึ้น) โดยดูที่ getItemViewType (ตำแหน่ง int) เพื่อให้แน่ใจว่ามันเป็นการแข่งขันดังนั้นจึงไม่ต้องกังวลว่า ViewHolder ของ ประเภทไม่ตรงกับประเภทข้อมูล [ตำแหน่ง] ไม่มีใครรู้เพิ่มเติมว่า ViewHolder ใน onBindViewHolder () เป็นอย่างไร

แก้ไข:ดูเหมือนรีไซเคิลViewHolderจะถูกเลือกตามประเภทดังนั้นจึงไม่มีนักรบอยู่ที่นั่น

แก้ไข: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ตอบคำถามนี้

มันได้รับการรีไซเคิลViewHolderเช่น:

holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));

หรือสร้างใหม่หากไม่พบการรีไซเคิลViewHolderประเภทที่ถูกต้อง

public ViewHolder getRecycledView(int viewType) {
        final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
        if (scrapHeap != null && !scrapHeap.isEmpty()) {
            final int index = scrapHeap.size() - 1;
            final ViewHolder scrap = scrapHeap.get(index);
            scrapHeap.remove(index);
            return scrap;
        }
        return null;
    }

View getViewForPosition(int position, boolean dryRun) {
    ......

    if (holder == null) {
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                        + "position " + position + "(offset:" + offsetPosition + ")."
                        + "state:" + mState.getItemCount());
            }

            final int type = mAdapter.getItemViewType(offsetPosition);
            // 2) Find from scrap via stable ids, if exists
            if (mAdapter.hasStableIds()) {
                holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
                if (holder != null) {
                    // update position
                    holder.mPosition = offsetPosition;
                    fromScrap = true;
                }
            }
            if (holder == null && mViewCacheExtension != null) {
                // We are NOT sending the offsetPosition because LayoutManager does not
                // know it.
                final View view = mViewCacheExtension
                        .getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view which does not have a ViewHolder");
                    } else if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view that is ignored. You must call stopIgnoring before"
                                + " returning this view.");
                    }
                }
            }
            if (holder == null) { // fallback to recycler
                // try recycler.
                // Head to the shared pool.
                if (DEBUG) {
                    Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
                            + "pool");
                }
                holder = getRecycledViewPool()
                        .getRecycledView(mAdapter.getItemViewType(offsetPosition));
                if (holder != null) {
                    holder.resetInternal();
                    if (FORCE_INVALIDATE_DISPLAY_LIST) {
                        invalidateDisplayListInt(holder);
                    }
                }
            }
            if (holder == null) {
                holder = mAdapter.createViewHolder(RecyclerView.this,
                        mAdapter.getItemViewType(offsetPosition));
                if (DEBUG) {
                    Log.d(TAG, "getViewForPosition created new ViewHolder");
                }
            }
        }
        boolean bound = false;
        if (mState.isPreLayout() && holder.isBound()) {
            // do not update unless we absolutely have to.
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            if (DEBUG && holder.isRemoved()) {
                throw new IllegalStateException("Removed holder should be bound and it should"
                        + " come here only in pre-layout. Holder: " + holder);
            }
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            mAdapter.bindViewHolder(holder, offsetPosition);
            attachAccessibilityDelegate(holder.itemView);
            bound = true;
            if (mState.isPreLayout()) {
                holder.mPreLayoutPosition = position;
            }
        }

        final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        final LayoutParams rvLayoutParams;
        if (lp == null) {
            rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else if (!checkLayoutParams(lp)) {
            rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else {
            rvLayoutParams = (LayoutParams) lp;
        }
        rvLayoutParams.mViewHolder = holder;
        rvLayoutParams.mPendingInvalidate = fromScrap && bound;
        return holder.itemView;
}

6

ฉันมีวิธีแก้ปัญหาที่ดีกว่าซึ่งอนุญาตให้สร้างมุมมองหลายประเภทในวิธีที่เปิดเผยและปลอดภัย มันเขียนใน Kotlin ซึ่ง btw ดีจริงๆ

ตัวยึดมุมมองแบบง่ายสำหรับประเภทการดูที่จำเป็นทั้งหมด

class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
    val label: TextView = itemView.findViewById(R.id.label) as TextView
}

มีรายการข้อมูลอะแดปเตอร์ย่อ โปรดทราบว่าประเภทมุมมองถูกแสดงโดย hashCode ของคลาสตัวยึดมุมมองเฉพาะ (KClass ใน Kotlin)

trait AdapterItem {
   val viewType: Int
   fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
}

abstract class AdapterItemBase<T>(val viewHolderClass: KClass<T>) : AdapterItem {
   override val viewType: Int = viewHolderClass.hashCode()  
   abstract fun bindViewHolder(viewHolder: T)
   override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
       bindViewHolder(viewHolder as T)
   }
}

เพียง แต่bindViewHolderจะต้องมีการแทนที่ในชั้นเรียนของรายการอะแดปเตอร์คอนกรีต (พิมพ์วิธีที่ปลอดภัย)

class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase<ViewHolderMedium>(ViewHolderMedium::class) {
    override fun bindViewHolder(viewHolder: ViewHolderMedium) {
        viewHolder.icon.setImageDrawable(icon)
        viewHolder.label.setText(label)
        viewHolder.itemView.setOnClickListener { onClick() }
    }
}

รายการของAdapterItemMediumวัตถุดังกล่าวเป็นแหล่งข้อมูลสำหรับอะแดปเตอร์ซึ่งยอมรับจริงList<AdapterItem>ดูด้านล่าง

ส่วนที่สำคัญของโซลูชันนี้คือโรงงานตัวยึดมุมมองซึ่งจะให้อินสแตนซ์ใหม่ของ ViewHolder เฉพาะ

class ViewHolderProvider {
    private val viewHolderFactories = hashMapOf<Int, Pair<Int, Any>>()

    fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
        val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
        val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
        return viewHolderFactory(view)
    }

    fun registerViewHolderFactory<T>(key: KClass<T>, layoutId: Int, viewHolderFactory: (View) -> T) {
        viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
    }
}

และคลาสอะแดปเตอร์อย่างง่ายจะมีลักษณะเช่นนี้

public class MultitypeAdapter(val items: List<AdapterItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

   val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2

   init {
        viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
            ViewHolderMedium(itemView)
        })
   }

   override fun getItemViewType(position: Int): Int {
        return items[position].viewType
    }

    override fun getItemCount(): Int {
        return items.size()
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
        return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
    }

    override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
        items[position].bindViewHolder(viewHolder)     
    }
}

เพียง 3 ขั้นตอนในการสร้างประเภทมุมมองใหม่:

  1. สร้างคลาสตัวยึดมุมมอง
  2. สร้างคลาสไอเท็มอะแด็ปเตอร์ (ขยายจาก AdapterItemBase)
  3. ลงทะเบียนระดับเจ้าของมุมมองใน ViewHolderProvider

นี่คือตัวอย่างของแนวคิดนี้: android-drawer-template มันยิ่งเพิ่มขึ้นไปอีก - ประเภทการดูที่ทำหน้าที่เป็นส่วนประกอบของสปินเนอร์รายการอะแดปเตอร์ที่เลือกได้


6

มันง่ายมากและตรงไปตรงมา

Just Override getItemViewType ()วิธีการในอะแดปเตอร์ของคุณ บนพื้นฐานของข้อมูลคืนค่า itemViewType ที่แตกต่างกัน เช่นพิจารณาวัตถุประเภทบุคคลที่มีสมาชิก isMale ถ้า isMale เป็นจริงกลับ 1 และ isMale เป็นเท็จกลับ 2 ในวิธีgetItemViewType ()

ตอนนี้มาถึงcreateViewHolder (ViewGroup parent, int viewType)บนพื้นฐานของ viewType yon ที่แตกต่างกันสามารถขยายไฟล์เลย์เอาต์ที่แตกต่างกันได้ ชอบดังต่อไปนี้

 if (viewType ==1){
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.male,parent,false);
    return new AdapterMaleViewHolder(view);
}
else{
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.female,parent,false);
    return new AdapterFemaleViewHolder(view);
}

ในonBindViewHolder (ตัวยึด VH ตำแหน่ง int)ตรวจสอบว่าตัวยึดเป็นอินสแตนซ์ของAdapterFemaleViewHolderหรือAdapterMaleViewHolderโดยinstanceofและกำหนดค่า

ViewHolder อาจเป็นแบบนี้

    class AdapterMaleViewHolder extends RecyclerView.ViewHolder {
            ...
            public AdapterMaleViewHolder(View itemView){
            ...
            }
        }

    class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
         ...
         public AdapterFemaleViewHolder(View itemView){
            ...
         }
    }

4

แม้ว่าคำตอบที่เลือกนั้นถูกต้อง แต่ฉันต้องการอธิบายเพิ่มเติมอย่างละเอียด ผมพบว่าที่นี่มีประโยชน์อะแดปเตอร์ที่กำหนดเองสำหรับหลายประเภทดูใน RecyclerView ใช้รุ่น Kotlin อยู่ที่นี่

Custom Adapter กำลังติดตาม

public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
ArrayList<String> list; // ArrayList of your Data Model
final int VIEW_TYPE_ONE = 1;
final int VIEW_TYPE_TWO = 2;

public CustomAdapter(Context context, ArrayList<String> list) { // you can pass other parameters in constructor
    this.context = context;
    this.list = list;
}

private class ViewHolder1 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder1(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

private class ViewHolder2 extends RecyclerView.ViewHolder {

    TextView yourView;
    ViewHolder2(final View itemView) {
        super(itemView);
        yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
    }
    void bind(int position) {
        // This method will be called anytime a list item is created or update its data
        //Do your stuff here
        yourView.setText(list.get(position));
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   if (viewType == VIEW_TYPE_ONE) {
       return new ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false));
   }
   //if its not VIEW_TYPE_ONE then its VIEW_TYPE_TWO
   return new ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false));

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        ((ViewHolder1) holder).bind(position);
    } else {
        ((ViewHolder2) holder).bind(position);
    }

}

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

@Override
public int getItemViewType(int position) {
    // here you can get decide from your model's ArrayList, which type of view you need to load. Like
    if (list.get(position).type == Something) { // put your condition, according to your requirements
        return VIEW_TYPE_ONE;
    }
    return VIEW_TYPE_TWO;
}
}

3

ฉันแนะนำห้องสมุดนี้จาก Hannes Dorfmann มันสรุปตรรกะทั้งหมดที่เกี่ยวข้องกับประเภทมุมมองเฉพาะในวัตถุแยกต่างหากที่เรียกว่า "AdapterDelegate" https://github.com/sockeqwe/AdapterDelegates

public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {

  private LayoutInflater inflater;

  public CatAdapterDelegate(Activity activity) {
    inflater = activity.getLayoutInflater();
  }

  @Override public boolean isForViewType(@NonNull List<Animal> items, int position) {
    return items.get(position) instanceof Cat;
  }

  @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
    return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
  }

  @Override public void onBindViewHolder(@NonNull List<Animal> items, int position,
      @NonNull RecyclerView.ViewHolder holder, @Nullable List<Object> payloads) {

    CatViewHolder vh = (CatViewHolder) holder;
    Cat cat = (Cat) items.get(position);

    vh.name.setText(cat.getName());
  }

  static class CatViewHolder extends RecyclerView.ViewHolder {

    public TextView name;

    public CatViewHolder(View itemView) {
      super(itemView);
      name = (TextView) itemView.findViewById(R.id.name);
    }
  }
}

public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {

  public AnimalAdapter(Activity activity, List<Animal> items) {

    // DelegatesManager is a protected Field in ListDelegationAdapter
    delegatesManager.addDelegate(new CatAdapterDelegate(activity))
                    .addDelegate(new DogAdapterDelegate(activity))
                    .addDelegate(new GeckoAdapterDelegate(activity))
                    .addDelegate(23, new SnakeAdapterDelegate(activity));

    // Set the items from super class.
    setItems(items);
  }
}

1

อันที่จริงผมต้องการที่จะปรับปรุงในคำตอบของแอนตัน

เนื่องจากgetItemViewType(int position)ส่งคืนค่าจำนวนเต็มคุณสามารถส่งคืน ID ทรัพยากรรูปแบบที่คุณต้องการขยาย วิธีนี้จะช่วยให้คุณประหยัดตรรกะonCreateViewHolder(ViewGroup parent, int viewType)ได้

นอกจากนี้ฉันจะไม่แนะนำให้ทำการคำนวณอย่างเข้มข้นgetItemCount()เนื่องจากฟังก์ชั่นนั้นเรียกว่าอย่างน้อย 5 ครั้งในขณะที่เรนเดอร์รายการและในขณะที่เรนเดอร์แต่ละรายการนอกเหนือจากรายการที่มองเห็น น่าเศร้าที่notifyDatasetChanged()เมธอดเป็นที่สิ้นสุดคุณไม่สามารถแทนที่มันได้ แต่คุณสามารถเรียกมันได้จากฟังก์ชั่นอื่นภายในอะแดปเตอร์


3
ใช่นั่นอาจใช้งานได้ แต่จะสร้างความสับสนให้กับผู้พัฒนารายอื่น นอกจากนี้จากเอกสารNote: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.คุณควรเขียนโค้ดเพิ่มอีกเล็กน้อยและอย่าใช้แฮ็ก
Ioane Sharvadze

1
ฉันเห็นด้วย. ย้อนกลับไปตอนนั้นฉันพลาดประโยคนั้นไป
Dragas

นั่นเป็นเรื่องตลกเพราะ RecyclerView.Adapter: getItemViewType () เอกสารที่นี่developer.android.com/reference/android/support/v7/widget/…แนะนำสิ่งที่ Dragas โพสต์ไว้ว่าคุณควร "พิจารณาการใช้ทรัพยากร id เพื่อระบุประเภทมุมมองรายการโดยไม่ซ้ำกัน " เห็นได้ชัดว่าไม่สนใจข้อกำหนดสำหรับ getViewTypeCount ()
Deemoe

1

คุณสามารถใช้ห้องสมุด: https://github.com/vivchar/RendererRecyclerViewAdapter

mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); /* included from library */
mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this));
mRecyclerViewAdapter.registerRenderer(...); /* you can use several types of cells */

`

สำหรับแต่ละรายการคุณควรใช้ ViewRenderer, ViewHolder, SomeModel:

ViewHolder - มันเป็นมุมมองที่เรียบง่ายของมุมมองรีไซเคิล

SomeModel - เป็นโมเดลของคุณพร้อมItemModelอินเตอร์เฟส

public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {

  public SomeViewRenderer(final int type, final Context context) {
    super(type, context);
  }
  @Override
 public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) {
    holder.mTitle.setText(model.getTitle());
 }
 @NonNull
 @Override
 public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) {
    return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false));
 }
}

สำหรับรายละเอียดเพิ่มเติมคุณสามารถดูเอกสาร


1

การใช้งานประเภทการดูง่ายขึ้นด้วย kotlin ต่อไปนี้เป็นตัวอย่างสำหรับไลบรารี่ไลต์นี้https://github.com/Link184/KidAdapter

recyclerView.setUp {
    withViewType {
        withLayoutResId(R.layout.item_int)
        withItems(mutableListOf(1, 2, 3, 4, 5, 6))
        bind<Int> { // this - is adapter view hoder itemView, it - current item
            intName.text = it.toString()
        }
    }


    withViewType("SECOND_STRING_TAG") {
        withLayoutResId(R.layout.item_text)
        withItems(mutableListOf("eight", "nine", "ten", "eleven", "twelve"))
        bind<String> {
            stringName.text = it
        }
    }
}

1

คุณสามารถจัดการหลายViewTypesได้RecyclerAdapterโดยทำการgetItemViewType()คืนviewTypeค่าที่คาดไว้สำหรับตำแหน่งนั้น

ฉันเตรียมการMultipleViewTypeAdapterสำหรับการสร้างรายการ MCQ สำหรับการสอบซึ่งอาจทำให้เกิดคำถามที่อาจมีคำตอบที่ถูกต้อง 2 ข้อขึ้นไป (ตัวเลือกช่องทำเครื่องหมาย) และคำถามคำตอบเดียว (ตัวเลือก radiobutton)

สำหรับสิ่งนี้ฉันได้รับประเภทของคำถามจากการตอบสนองของ API และฉันใช้มันเพื่อตัดสินใจว่ามุมมองใดที่ฉันต้องแสดงให้กับคำถาม

public class MultiViewTypeAdapter extends RecyclerView.Adapter {

    Context mContext;
    ArrayList<Question> dataSet;
    ArrayList<String> questions;
    private Object radiobuttontype1; 


    //Viewholder to display Questions with checkboxes
    public static class Checkboxtype2 extends RecyclerView.ViewHolder {
        ImageView imgclockcheck;
        CheckBox checkbox;

        public Checkboxtype2(@NonNull View itemView) {
            super(itemView);
            imgclockcheck = (ImageView) itemView.findViewById(R.id.clockout_cbox_image);
            checkbox = (CheckBox) itemView.findViewById(R.id.clockout_cbox);


        }
    }

        //Viewholder to display Questions with radiobuttons

    public static class Radiobuttontype1 extends RecyclerView.ViewHolder {
        ImageView clockout_imageradiobutton;
        RadioButton clockout_radiobutton;
        TextView sample;

        public radiobuttontype1(View itemView) {
            super(itemView);
            clockout_imageradiobutton = (ImageView) itemView.findViewById(R.id.clockout_imageradiobutton);
            clockout_radiobutton = (RadioButton) itemView.findViewById(R.id.clockout_radiobutton);
            sample = (TextView) itemView.findViewById(R.id.sample);
        }
    }

    public MultiViewTypeAdapter(ArrayList<QueDatum> data, Context context) {
        this.dataSet = data;
        this.mContext = context;

    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {

        if (viewType.equalsIgnoreCase("1")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("2")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_cbox_list_row, viewGroup, false);
            view.setHorizontalFadingEdgeEnabled(true);
            return new Checkboxtype2(view);

        } else if (viewType.equalsIgnoreCase("3")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("4")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);

        } else if (viewType.equalsIgnoreCase("5")) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
            return new Radiobuttontype1(view);
        }


        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int viewType) {
        if (viewType.equalsIgnoreCase("1")) {
            options =  dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            ((radiobuttontype1) viewHolder).clockout_radiobutton.setChecked(false);
            ((radiobuttontype1) viewHolder).sample.setText(question);
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((radiobuttontype1) viewHolder).clockout_imageradiobutton);

        } else if (viewType.equalsIgnoreCase("2")) {
            options = (ArrayList<Clockout_questions_Option>) dataSet.get(i).getOptions();
            question = dataSet.get(i).getQuestion();
            image = options.get(i).getValue();
            //loading image bitmap in the ViewHolder's View
            Picasso.with(mContext)
                    .load(image)
                    .into(((Checkboxtype2) viewHolder).imgclockcheck);

        } else if (viewType.equalsIgnoreCase("3")) {
                //fit data to viewHolder for ViewType 3
        } else if (viewType.equalsIgnoreCase("4")) {
//fit data to viewHolder for ViewType 4   
        } else if (viewType.equalsIgnoreCase("5")) {
//fit data to viewHolder for ViewType 5     
        }
    }

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

    /**
     * returns viewType for that position by picking the viewType value from the 
     *     dataset
     */
    @Override
    public int getItemViewType(int position) {
        return dataSet.get(position).getViewType();

    }


}

คุณสามารถหลีกเลี่ยงการเติมข้อมูล viewHolder ตามเงื่อนไขหลายเงื่อนไขonBindViewHolder()โดยกำหนด id เดียวกันสำหรับมุมมองที่คล้ายกันทั่ว viewHolders ซึ่งแตกต่างกันในการวางตำแหน่ง


0

หากคุณต้องการใช้ร่วมกับ Android Data Binding ให้ดูที่https://github.com/evant/binding-collection-adapter - มันเป็นทางออกที่ดีที่สุดสำหรับประเภทการดูหลายประเภทRecyclerViewฉันเคยเห็น

คุณอาจใช้มันเหมือน

var items: AsyncDiffPagedObservableList<BaseListItem> =
        AsyncDiffPagedObservableList(GenericDiff)

    val onItemBind: OnItemBind<BaseListItem> =
        OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }

และจากนั้นในเค้าโครงที่รายการอยู่

 <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                app:enableAnimations="@{false}"
                app:scrollToPosition="@{viewModel.scrollPosition}"

                app:itemBinding="@{viewModel.onItemBind}"
                app:items="@{viewModel.items}"

                app:reverseLayoutManager="@{true}"/>

ไอเท็มรายการของคุณต้องใช้BaseListItemอินเตอร์เฟสที่มีลักษณะเช่นนี้

interface BaseListItem {
    val layoutRes: Int
}

และมุมมองรายการควรมีลักษณะเช่นนี้

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
                name="item"
                type="...presentation.somescreen.list.YourListItem"/>
    </data>

   ...

</layout>

YourListItemดำเนินการที่ไหนBaseListItem

หวังว่ามันจะช่วยใครซักคน


0

ก่อนอื่นคุณต้องสร้าง 2 เลย์เอาต์ xml หลังจากนั้นภายในอะแดปเตอร์ recyclerview TYPE_CALL และ TYPE_EMAIL คือสองค่าคงที่ที่มี 1 และ 2 ตามลำดับในคลาสอะแดปเตอร์

ตอนนี้กำหนดสองค่าคงที่ในระดับชั้นมุมมองอะแดปเตอร์ Recycler ตัวอย่างเช่น: ส่วนตัวคง int int TYPE_CALL = 1; ส่วนตัวคง int int TYPE_EMAIL = 2;

ตอนนี้สร้างตัวยึดมุมมองที่มีหลายมุมมองเช่นนี้:

class CallViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    CallViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}
class EmailViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    EmailViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}

ตอนนี้รหัสดังต่อไปนี้ในวิธี onCreateViewHolder และ onBindViewHolder ในอะแดปเตอร์ recyclerview:

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
    View view;
    if (viewType == TYPE_CALL) { // for call layout
        view = LayoutInflater.from(context).inflate(R.layout.item_call, viewGroup, false);
        return new CallViewHolder(view);

    } else { // for email layout
        view = LayoutInflater.from(context).inflate(R.layout.item_email, viewGroup, false);
        return new EmailViewHolder(view);
    }
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
    if (getItemViewType(position) == TYPE_CALL) {
        ((CallViewHolder) viewHolder).setCallDetails(employees.get(position));
    } else {
        ((EmailViewHolder) viewHolder).setEmailDetails(employees.get(position));
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.