RecyclerViewで拡張可能なリスト?


95

新しいRecyclerViewで拡張可能なリストアイテムを使用することは可能ですか?ExpandableListViewが好きですか?


1
あなたはアンドロイド6.0のグーグルソースでアンドロイド時計を見ることができます
zys

@zysこのアンドロイドクロックのサンプルソースコードはどこにありますか?
AppiDevo 2016

1
展開ボタンをクリックすると、さまざまなビュータイプを使用してさまざまなレイアウトを読み込むことができます。このソリューションはAndroid Clockによって使用されます:android.googlesource.com/platform/packages/apps/DeskClock
zys

私の簡単な回答をご覧ください stackoverflow.com/a/48092441/5962715
キランベニージョセフ

回答:


121

これは、標準のLayoutManagerで簡単に実行できます。すべては、アダプターの管理方法によって異なります。

セクションを展開する場合は、ヘッダーの後にアダプターに新しい項目を追加するだけです。これを行うときは、必ずnotifyItemRangeInsertedを呼び出してください。セクションを折りたたむには、関連するアイテムを削除し、notifyItemRangeRemoved()を呼び出します。適切に通知されるデータ変更については、リサイクラービューがビューをアニメーション化します。アイテムを追加すると、新しいアイテムで塗りつぶされる領域が作成され、新しいアイテムがフェードインします。削除は反対です。アダプターのほかに必要なことは、ビューをスタイル設定して論理構造をユーザーに伝えることだけです。

更新:Ryan Brooksはこれを行う方法についての記事を書いています。


素晴らしい提案。なぜ誰もこの回答に賛成しなかったのだろうか!
x-treme 2015年

これを例として、次のリリースの一部としてSuperSLiMに追加する方法を見ていきます。
トニックArtos

Ryan Brooksはこれを行う方法についての記事を書いています。
Tonic Artos、2015

素晴らしい提案。なぜこの回答がページの一番下にあるので、下にスクロールして見つけなければならないのですか?一番上に表示して、より多くの人がこの美しい答えをより簡単に見つけられるようにする必要があります。
RestInPeace 2016年

1
Ryan Brooksは自分のライブラリを非推奨としてマークしました。彼がサポートをやめただけなのか、それともこのアプローチが何かを壊したり、メモリリークやsthを作成したりしたのか...
Varvara Kalinina

6

ここからサンプルコードの実装を取得します

ViewHolderのonClick内にValueAnimatorを設定します

@Override
public void onClick(final View view) {
    if (mOriginalHeight == 0) {
        mOriginalHeight = view.getHeight();
    }
    ValueAnimator valueAnimator;
    if (!mIsViewExpanded) {
        mIsViewExpanded = true;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
    } else {
        mIsViewExpanded = false;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
    }
    valueAnimator.setDuration(300);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            view.getLayoutParams().height = value.intValue();
            view.requestLayout();
        }
    });
    valueAnimator.start();

}

これが最終的なコードです

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView mFriendName;
    private int mOriginalHeight = 0;
    private boolean mIsViewExpanded = false;


    public ViewHolder(RelativeLayout v) {
        super(v);
        mFriendName = (TextView) v.findViewById(R.id.friendName);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(final View view) {
        if (mOriginalHeight == 0) {
            mOriginalHeight = view.getHeight();
        }
        ValueAnimator valueAnimator;
        if (!mIsViewExpanded) {
            mIsViewExpanded = true;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
        } else {
            mIsViewExpanded = false;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
        }
        valueAnimator.setDuration(300);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                view.getLayoutParams().height = value.intValue();
                view.requestLayout();
            }
        });
        valueAnimator.start();

    }
}

11
これは「のようにExpandableListView」機能しません。その場合の展開されたコンテンツは、アダプターからのアイテムを含むリスト自体であるためです。これは、グループ内の子として許可されているアイテムが1つだけの縮退したソリューションです。
TWiStErRob 2015年

また、まったくリサイクルされるビューでも適切に機能しません
takecare '31 / 07/31

RecyclerListのビューが複数回繰り返される可能性があるため、正しく機能しないため、1つのアイテムを展開すると、リストの複数のアイテムが展開されます
Hossein Shahdoost

3

https://github.com/gabrielemariotti/cardslib

このライブラリには、recyclerviewを使用した拡張可能なリストの実装があります(「CardViewNative」->「リスト、グリッド、およびRecyclerView」->「拡張可能なカード」のデモアプリを参照してください)。カード/リストの他のクールな組み合わせもたくさんあります。


2
この拡張可能なカードリストはRecyclerViewの子ではありません(RecyclerViewを拡張せず、ExpandableListVIewを拡張するだけです)
whizzzkey

0

上記の解決策はリストビューでは拡張可能なコンテンツとして使用できないと誰かが不満を述べました。ただし、簡単な解決策がありますリストビューを作成し、このリストビューに行を手動で入力します

レイジーなものの解決策:コードをあまり変更したくない場合は、簡単な解決策があります。アダプタを手動で使用してビューを作成し、それらをに追加するだけLinearLayoutです。

次に例を示します。

if (mIsExpanded)
{
    // llExpandable... is the expandable nested LinearLayout
    llExpandable.removeAllViews();
    final ArrayAdapter<?> adapter = ... // create your adapter as if you would use it for a ListView
    for (int i = 0; i < adapter.getCount(); i++)
    {
        View item = adapter.getView(i, null, null);
        // if you want the item to be selectable as if it would be in a default ListView, then you can add following code as well:
        item.setBackgroundResource(Functions.getThemeReference(context, android.R.attr.selectableItemBackground));
        item.setTag(i);
        item.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // item would be retrieved with: 
                // adapter.getItem((Integer)v.getTag())
            }
        });
        llExpandable.addView(item);
    }
    ExpandUtils.expand(llExpandable, null, 500);
}
else
{
    ExpandUtils.collapse(llExpandable, null, 500);
}

ヘルパー関数:getThemeReference

public static int getThemeReference(Context context, int attribute)
{
    TypedValue typeValue = new TypedValue();
    context.getTheme().resolveAttribute(attribute, typeValue, false);
    if (typeValue.type == TypedValue.TYPE_REFERENCE)
    {
        int ref = typeValue.data;
        return ref;
    }
    else
    {
        return -1;
    }
}

ヘルパークラス:ExpandUtils

Kavin Varnan postetはレイアウトをアニメーション化する方法をすでに説明しています...しかし、私のクラスを使用したい場合は、自由に行ってください。要旨を投稿しました:https : //gist.github.com/MichaelFlisar/738dfa03a1579cc7338a


9
「レイジーソリューション」は本当に悪い考えです。スクロール可能なビュー内のlinearlayoutにビューを追加することは非常に非効率的です。
マシュー

少なくとも使える以上のものだと思います。私がテストしたすべてのデバイスで高速かつスムーズに動作します。ちなみに、ビューはリストビューが表示されていないときに追加されます...すでに入力されているリストビューのみが後で表示されます...
prom85

これはすてきでした!おかげで
Pawan Kumar

1
@マシューが述べたように、これは本当に良い考えではありません。LinearLayoutsで1つの大きなスクロール可能なビューを使用すると、適切にスケーリングされません。RecyclerView / ListViewおよび他の同様のビューが作成された最大の理由の1つは、サイズが不明なデータに基づく大きなリストの最適化でした。多数のビューが追加された単一のビューを作成すると、提供されているすべての最適化が無効になります。ビューのリサイクルは大きなメリットであり、アプリのメモリを効率的にします。アイテムの数が少ない場合を除き、リストを使用してビューバインディングを処理することで、多くの作業を節約できます。
ムンカイ、2015年

あなたは完全に正しいです、もちろんこれは完璧ではなく、何も最適化しません...私のユースケースでは、私は常にいくつかのアイテムしか持っていませんでした...したがって、これは問題ありませんでした...ところで、私は方法を見つけましたネストされたリサイクラービューの場合...固定高さの水平リサイクラービューを使用するだけで(すべてのユースケースに最適ではありませんが)、ネストされたビューrecyclerviewを展開/非表示にして、recyclerview
prom85


0

これは、@ TonicArtosがアイテムを追加および削除し、実行中にアニメーション化するためのサンプルコードです。これは、RecyclerViewアニメーションおよびGitHubサンプルから取得されます。

1)onCreateViewHolder()内にリスナーを追加して、onClickに登録します

2)アダプター内にカスタムOnClickListenerを作成します

private View.OnClickListener mItemListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        TextView tv = (TextView) v.findViewById(R.id.tvItems);
        String selected = tv.getText().toString();
        boolean checked = itemsList.get(recyclerView.getChildAdapterPosition(v)).isChecked();

        switch (selected){
            case "Item1":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;
            case "Item2":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;                 
            default:
                //In my case I have checkList in subItems,
                //checkItem(v);
                break;
        }
    }
};

3) addItem()とdeleteItem()を追加します

private void addItem(View view){
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION){
        navDrawItems.add(position+1,new mObject());
        navDrawItems.add(position+2,new mObject());
        notifyItemRangeInserted(position+1,2);
    }
}


private void deleteItem(View view) {
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION) {
        navDrawItems.remove(position+2);
        navDrawItems.remove(position+1);
        notifyItemRangeRemoved(position+1,2);
    }
}

4) RecyclerViewAdapterがRecycler Viewと同じアクティビティにない場合は、作成中にrecyclerViewのインスタンスをアダプタに渡します

5) itemListは、アイテムの状態(Open / Close)、名前、Itemのタイプ(subItems / mainItem)を維持し、値に基づいてテーマを設定するのに役立つmObjectタイプのArrayListです。

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.