RecyclerView ile genişletilebilir liste?


95

Yeni RecyclerView ile genişletilebilir liste öğelerini kullanmak mümkün mü? ExpandableListView gibi mi?


1
android saatini android 6.0'da google kaynağında görebilirsiniz
zys

@zys Bu android saat örnek kaynak kodunu nerede bulabilirim?
AppiDevo

1
Genişlet düğmesine tıkladığınızda farklı bir düzen yüklemek için farklı viewType kullanabilirsiniz.Bu çözüm Android Clock tarafından kullanılır: android.googlesource.com/platform/packages/apps/DeskClock
zys


Yanıtlar:


121

Bu, stok LayoutManager'larla yapmak kolaydır, hepsi adaptörünüzü nasıl yönettiğinize bağlıdır.

Bir bölümü genişletmek istediğinizde, başlıktan sonra adaptörünüze yeni öğeler eklemeniz yeterlidir. Bunu yaptığınızda notifyItemRangeInserted'ı çağırmayı unutmayın. Bir bölümü daraltmak için ilgili öğeleri kaldırmanız ve notifyItemRangeRemoved () öğesini çağırmanız yeterlidir. Uygun şekilde bildirilen herhangi bir veri değişikliği için geri dönüşümcü görünümü görünümleri canlandıracaktır. Öğe eklerken, yeni öğelerle doldurulacak bir alan yapılır ve yeni öğeler kaybolur. Kaldırma tam tersidir. Bağdaştırıcı şeyler dışında yapmanız gereken tek şey, mantıksal yapıyı kullanıcıya iletmek için görünümlerinizi şekillendirmektir.

Güncelleme: Ryan Brooks şimdi bunun nasıl yapılacağına dair bir makale yazdı .


Harika bir öneri. Neden başka hiç kimsenin bu yanıta oy vermediğini merak ediyorum !!
x-treme

Bunu bir sonraki sürümün parçası olarak SuperSLiM'e örnek olarak eklemeyi göreceğim.
Tonic Artos

Ryan Brooks şimdi bunun nasıl yapılacağına dair bir makale yazdı .
Tonik Artos

Harika bir öneri. Bu cevap neden sayfanın o kadar aşağısında ki onu bulmak için aşağı kaydırmam gerekiyor? Daha fazla insanın bu güzel cevabı daha kolay bulabilmesi için en üstte gösterilmelidir.
RestInPeace

1
Ryan Brooks, kütüphanesini kullanımdan kaldırıldı olarak işaretledi. Merak ediyorum, sadece onu desteklemeyi bıraktı mı yoksa bu yaklaşımın bir şeyi kırdığı veya bellek sızıntıları veya sth yarattığı ortaya çıktı ...
Varvara Kalinina

6

Örnek kod uygulamasını buradan alın

OnClick of ViewHolder içinde ValueAnimator'ı ayarlayın

@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();

}

İşte son kod

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
Bu "gibi ExpandableListView" çalışmaz , çünkü bu durumda genişletilmiş içerik adaptörden gelen öğelerin bulunduğu bir listedir. Bu, grup içinde çocukken yalnızca 1 maddeye izin verilen dejenere bir çözümdür.
TWiStErRob

geri dönüştürülen görünümlerde de düzgün çalışmıyor
takecare

RecyclerList'teki bir görünüm birden çok kez tekrar edilebileceğinden düzgün çalışmıyor, bu nedenle bir öğeyi genişlettiğinizde, listedeki birden çok öğenin genişletildiğini görüyorsunuz
Hossein Shahdoost

3

https://github.com/gabrielemariotti/cardslib

Bu kitaplık, geri dönüşüm görünümüne sahip genişletilebilir bir liste uygulamasına sahiptir ("CardViewNative" -> "List, Grid ve RecyclerView" -> "Genişletilebilir kartlar" altındaki demo uygulamasına bakın). Aynı zamanda birçok başka harika kart / liste kombinasyonuna sahiptir.


2
Bu Genişletilebilir Kartlar listesi RecyclerView'ın alt öğesi değil (RecyclerView'ı genişletmez, yalnızca ExpandableListVIew'i genişletiyor)
whizzzkey

0

Birisi, yukarıda bahsedilen çözümün genişletilebilir içerik olarak bir liste görünümüyle kullanılamayacağından şikayet etti. Ancak basit bir çözüm var: bir liste görünümü oluşturun ve bu liste görünümünü manuel olarak satırlarınızla doldurun .

Tembel olanlar için çözüm : Kodunuzu fazla değiştirmek istemiyorsanız basit bir çözüm var. Görünümler oluşturmak için adaptörünüzü manuel olarak kullanın ve bunları LinearLayout.

İşte örnek:

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);
}

yardımcı işlevler: 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;
    }
}

yardımcı sınıf: ExpandUtils

Kavin Varnan postet zaten bir mizanpajı nasıl canlandırabilirim ... Ama eğer sınıfımı kullanmak istiyorsanız, bunu yapmaktan çekinmeyin, bir özet yayınladım: https://gist.github.com/MichaelFlisar/738dfa03a1579cc7338a


9
"Tembel Çözüm" gerçekten kötü bir fikir. Kaydırılabilir bir görünümün içindeki doğrusal düzene görünümler eklemek çok verimsizdir.
Matthew

En azından kullanışlı olandan daha fazla olduğunu düşünüyorum. Test ettiğim tüm cihazlarda hızlı ve sorunsuz çalışıyor. Bu arada, liste görünümü görünmediğinde görünümler eklenir ... sadece önceden doldurulmuş liste görünümü daha sonra gösterilecektir ...
prom85

Bu çok güzeldi! Çok teşekkürler
Pawan Kumar

1
@ Matthew'in bahsettiği gibi, bu gerçekten iyi bir fikir değil. LinearLayouts ile tek bir büyük kaydırılabilir görünüme sahip olmak iyi ölçeklenmez. RecyclerView / ListView ve diğer benzer görünümlerin yazılmasının en büyük nedenlerinden biri, bilinmeyen boyutta büyük veri destekli listeleri optimize etmekti. Bir grup görünüm eklenmiş tek bir görünüm oluşturmak, sağlanan tüm optimizasyonları dışarı atar. Görünümleri geri dönüştürmek büyük bir avantajdır ve uygulamanızın belleğini verimli hale getirir. Öğe sayısı az olmadıkça, görünüm bağlamayı işlemek için listeler kullanılarak kaydedilen çok iş vardır.
munkay

Tamamen haklısınız, elbette bu mükemmel değil ve hiçbir şeyi optimize etmiyor ... Kullanım durumlarım için her zaman sadece birkaç öğem vardı ... Yani bu sorun değildi ... Btw, bu arada bir yolunu buldum iç içe geçmiş geri dönüşümcü görünümleri için ... İç içe yerleştirilmiş olarak sabit yükseklikte yatay bir geri dönüşümcü görünümü kullanın (her kullanım için mükemmel değildir, ancak yine de) recyclerviewve iç içe geçmiş olanı genişletebilir / gizleyebilir verecyclerview
balonun


0

Bu, @TonicArtos tarafından Öğeleri eklemek ve kaldırmak ve bunu yaparken hareketlendirmek için bahsedilenler için örnek koddur , bu RecyclerView Animations ve GitHub örneğinden alınmıştır.

1) onClick'e kaydolmak için onCreateViewHolder () içerisine Dinleyici ekleyin

2) Bağdaştırıcınızın içinde özel OnClickListener oluşturun

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 () ve deleteItem () öğenizi ekleyin

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'ınız Recycler View ile aynı Activity'de değilse, oluştururken recyclerView örneğini Bağdaştırıcıya geçirin

5) itemList, öğenin durumlarını (Aç / Kapat), adı, Öğe türünü (subItems / mainItem) korumaya ve değerlere dayalı Tema ayarlamaya yardımcı olan mObject türünde bir ArrayListtir

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.