2016-01-18 12 views
25

タイトルが付いたグループでRecyclerViewの要素を分割する必要があります(下の図のInboxアプリケーションのように)ので、どのようなアプローチが良いのか理解してください。 1)異種レイアウトを使用することはできますが、同じグループの要素がすでに追加されているかどうかを確認する必要があるため、または新しい仕切りを追加する必要があるため、新しい要素をグループに挿入すると便利ではありません。この場合、このようなデータ構造を持つすべての操作を別のクラスにラップします。RecyclerView内のグループで要素を分割する

2)理論的には、各グループを独自のRecyclerViewでラップしてラベルを付けることができますか?例えば

Inbox app

+0

スクロールに問題があるため、複数のrecyclerviewを使用してUIレベルで要素をグループ化することはできません。 2つの要素タイプ(ヘッダー用とアイテム用)の1つのrecyclerviewだけを使用できます。 – thetonrifles

+0

@thetonriflesあなたは正しいUIをできるだけシンプルにする必要があります、私は質問をしていたときに忘れてしまったようですね、ありがとう! – Leo

答えて

56

次のことができます。

  1. は、日付によって分割要素のためのTreeMap<Date,List<Event>>を使用してください。これは、ビジネスオブジェクトを保持するためのコレクションになります。もちろん、あなたがすでに似たような構造を持っていれば、それを保つことができます。正しい要素の順序でUIを実装するための項目のリストを簡単に作成するために何かを持つことは重要です。

  2. Listの専用抽象タイプ(たとえば、ListItem)を定義して、ビジネスオブジェクトをラップします。その実装は次のようなものが考えられます。

    public abstract class ListItem { 
    
        public static final int TYPE_HEADER = 0; 
        public static final int TYPE_EVENT = 1; 
    
        abstract public int getType(); 
    } 
    
  3. (ここでは、私はちょうど2つのタイプを追加しましたが、あなたが必要として、あなたは多くを使用することができます)あなたのリストの要素型のそれぞれのクラスを定義します。

    public class HeaderItem extends ListItem { 
    
        private Date date; 
    
        // here getters and setters 
        // for title and so on, built 
        // using date 
    
        @Override 
        public int getType() { 
         return TYPE_HEADER; 
        } 
    
    } 
    
    public class EventItem extends ListItem { 
    
        private Event event; 
    
        // here getters and setters 
        // for title and so on, built 
        // using event 
    
        @Override 
        public int getType() { 
         return TYPE_EVENT; 
        } 
    
    } 
    
  4. (mEventsMapマップは、ポイント1で構築している場合)、次のように

    リストを作成します。

    List<ListItem> mItems; 
    // ... 
    mItems = new ArrayList<>(); 
    for (Date date : mEventsMap.keySet()) { 
        HeaderItem header = new HeaderItem(); 
        header.setDate(date); 
        mItems.add(header); 
        for (Event event : mEventsMap.get(date)) { 
         EventItem item = new EventItem(); 
         item.setEvent(event); 
         mItems.add(item); 
        } 
    } 
    
  5. は、あなたのRecyclerView用のアダプタを定義する作業しますListに重要であるものをここで点4で定義され、以下のようにgetItemViewTypeメソッドをオーバーライドすることです:

    @Override 
    public int getItemViewType(int position) { 
        return mItems.get(position).getType(); 
    } 
    

    を次にあなたは、ヘッダとイベントアイテムのために2つのレイアウトとViewHolderを持っている必要があります。アダプタ方法は、それに応じて、このの世話をする必要があります。

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
        if (viewType == ListItem.TYPE_HEADER) { 
         View itemView = mLayoutInflater.inflate(R.layout.view_list_item_header, parent, false); 
         return new HeaderViewHolder(itemView); 
        } else { 
         View itemView = mLayoutInflater.inflate(R.layout.view_list_item_event, parent, false); 
         return new EventViewHolder(itemView); 
        } 
    } 
    
    
    @Override 
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) { 
        int type = getItemViewType(position); 
        if (type == ListItem.TYPE_HEADER) { 
         HeaderItem header = (HeaderItem) mItems.get(position); 
         HeaderViewHolder holder = (HeaderViewHolder) viewHolder; 
         // your logic here 
        } else {    
         EventItem event = (EventItem) mItems.get(position); 
         EventViewHolder holder = (EventViewHolder) viewHolder; 
         // your logic here 
        } 
    } 
    

Hereそれ以上説明したアプローチの実装を提供するGitHubの上のリポジトリです。

+0

現時点では、ほぼ同じソリューション(リスト変換の明示的なマップなし)がありますが、機能しますが、ソリューションをサポートするのが簡単ではないと思われます(おそらく私は間違っているでしょう)。おそらくいくつかのベストプラクティスが既に存在します。 – Leo

+0

@Leoこのアプローチでは、おそらく主な問題は、多くのビュータイプの場合にコードをきれいに保つことです。そのような場合には、各ViewHolderと対応するバインディングロジックを専用のクラスにカプセル化して、アダプターの実装を軽くする可能性があります。 – thetonrifles

+0

良いアドバイス、ありがとうございます。幸運にも私の場合、私は2種類のアイテムしか持っていません(デバイダとアイテムのデータ):)しかし、おそらくそれが変更されることを知っている人 – Leo

6

あなたのプロジェクトでこの問題を解決するために書いたライブラリを使用できます。Gradleの依存性(レポが含まjcenterを必要とします):

dependencies { 
    //your other dependencies 
    compile 'su.j2e:rv-joiner:1.0.3'//latest version by now 
} 

次に、あなたの状況で、あなたはこのようななめらか行うことができます:あなたがアイテムを追加する必要がある場合

//init your RecyclerView as usual 
RecyclerView rv = (RecyclerView) findViewById(R.id.rv); 
rv.setLayoutManager(new LinearLayoutManager(this)); 

//construct a joiner 
RvJoiner rvJoiner = new RvJoiner(); 
rvJoiner.add(new JoinableLayout(R.layout.today)); 
YourAdapter todayAdapter = new YourAdapter(); 
rvJoiner.add(new JoinableAdapter(todayAdapter)); 
rvJoiner.add(new JoinableLayout(R.layout.yesterday)); 
YourAdapter yesterdayAdapter = new YourAdapter(); 
rvJoiner.add(new JoinableAdapter(yesterdayAdapter)); 

//set join adapter to your RecyclerView 
rv.setAdapter(rvJoiner.getAdapter()); 

を、適切なアダプタに追加、のように:あなたは、動的にリサイクルビューに新しいグループを追加する必要がある場合

if (timeIsToday) { 
    todayAdapter.addItem(item);//or other func you've written 
} else if (timeIsYesterday) { 
    yesterdayAdapter.addItem(item); 
} 

、あなたは、このメソッドを使用することができます。

rvJoiner.add(new JoinableLayout(R.layout.tomorrow)); 
YourAdapter tomorrowAdapter = new YourAdapter(); 
rvJoiner.add(new JoinableAdapter(tomorrowAdapter)); 

詳細なライブラリの説明については、this linkを確認できます。私はそれがあなたの目標を達成するための最良の方法だとは言えませんが、ときどき私を助けます。

UPD:

私は外部のライブラリを使用せずに、このを行う方法を見つけました。 RecyclerView.ItemDecorationクラスを使用してください。たとえば、グループ内の3つのアイテムでアイテムをグループ化するには、これを行うことができます:

recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 

     private int textSize = 50; 
     private int groupSpacing = 100; 
     private int itemsInGroup = 3; 

     private Paint paint = new Paint(); 
     { 
      paint.setTextSize(textSize); 
     } 

     @Override 
     public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 
      for (int i = 0; i < parent.getChildCount(); i++) { 
       View view = parent.getChildAt(i); 
       int position = parent.getChildAdapterPosition(view); 
       if (position % itemsInGroup == 0) { 
        c.drawText("Group " + (position/itemsInGroup + 1), view.getLeft(), 
          view.getTop() - groupSpacing/2 + textSize/3, paint); 
       } 
      } 
     } 

     @Override 
     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 
      if (parent.getChildAdapterPosition(view) % itemsInGroup == 0) { 
       outRect.set(0, groupSpacing, 0, 0); 
      } 
     } 
    }); 

希望すると助かります。

+0

それは大きなプロジェクトではない、私はアンドロイドの開発では新しいので、私は可能な限り多くのものを把握したいベストプラクティスのように、プロジェクトで可能な限り長くGoogleアンドロイドのライブラリを使用しようとします。しかしおそらく後で私はそのような図書館を探すでしょう。ありがとう! – Leo

+1

@Leo Googleのlibsだけでこれを行う方法を示す答えを更新しました。見てみましょう:) – j2esu

+0

更新された答えは非常に創造的です!新しいItemDecorationを使用する方法、古いバージョンのAndroidもサポートしています。このコードを使用してiOSメニューのようなメニューを生成しました。 – Arjun

関連する問題