RecyclerView로 확장 가능한 목록?


95

새로운 RecyclerView에서 확장 가능한 목록 항목을 사용할 수 있습니까? ExpandableListView처럼?


1
당신은 안드로이드 6.0의 구글 소스에서 안드로이드 시계를 볼 수 있습니다
zys

@zys이 안드로이드 시계 예제 소스 코드는 어디에서 찾을 수 있습니까?
AppiDevo

1
확장 버튼을 클릭하면 다른 viewType을 사용하여 다른 레이아웃을로드 할 수 있습니다.이 솔루션은 Android Clock에서 사용됩니다. android.googlesource.com/platform/packages/apps/DeskClock
zys


답변:


121

이것은 주식 LayoutManagers로 간단하게 할 수 있으며, 모두 어댑터를 관리하는 방법에 따라 다릅니다.

섹션을 확장하려면 헤더 뒤에 새 항목을 어댑터에 추가하기 만하면됩니다. 이 작업을 수행 할 때 notifyItemRangeInserted를 호출해야합니다. 섹션을 축소하려면 관련 항목을 제거하고 notifyItemRangeRemoved ()를 호출하면됩니다. 적절하게 통보 된 데이터 변경에 대해 리사이클 러 뷰는 뷰를 애니메이션합니다. 항목을 추가 할 때 새 항목으로 채워질 영역이 만들어지고 새 항목이 희미 해집니다. 제거는 그 반대입니다. 어댑터 외에해야 할 일은 뷰의 스타일을 지정하여 사용자에게 논리적 구조를 전달하는 것입니다.

업데이트 : Ryan Brooks는 이제 이를 수행하는 방법에 대한 기사 를 작성했습니다 .


좋은 제안입니다. 왜 다른 사람이이 답변을 찬성하지 않았는지 궁금합니다 !!
x-treme 2015 년

다음 릴리스의 일부로 SuperSLiM에 예제로 추가하는 방법을 살펴 보겠습니다.
Tonic Artos 2015-06-05

Ryan Brooks는 이제 이를 수행하는 방법에 대한 기사 를 작성했습니다 .
Tonic Artos 2015-08-09

좋은 제안입니다. 이 답변이 페이지 아래로 내려 가서 찾기 위해 스크롤해야하는 이유는 무엇입니까? 더 많은 사람들이이 아름다운 답을 더 쉽게 찾을 수 있도록 맨 위에 표시되어야합니다.
RestInPeace

1
Ryan Brooks는 자신의 라이브러리를 더 이상 사용하지 않는 것으로 표시했습니다. 나는 그가 지원을 중단했다 그냥 있는지 궁금 아니면 ...이 방법 나누기 뭔가 밝혀 또는 메모리 누수 또는 STH를 생성
Varvara 칼리 니나

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경우 확장 된 콘텐츠는 어댑터에서 가져온 항목이 포함 된 목록 자체이므로 "처럼 " 작동하지 않습니다 . 이것은 그룹 내에서 자식으로 허용되는 항목이 하나 뿐인 퇴화 솔루션입니다.
TWiStErRob

도 아니다 전혀 재활용받을 전망 제대로 작동하지
takecare

RecyclerList에서보기를 여러 번 반복 될 수있는대로 그것은 그래서 당신은 하나 개의 항목을 확장 할 때, 제대로 작동하지 않습니다, 당신은 참조 목록에 여러 항목이 확대된다
호세인 Shahdoost

3

https://github.com/gabrielemariotti/cardslib

이 라이브러리에는 recyclerview가있는 확장 가능한 목록이 구현되어 있습니다 ( "CardViewNative"-> "List, Grid 및 RecyclerView"-> "Expandable cards"아래의 데모 앱 참조). 또한 카드 / 목록의 다른 멋진 조합이 많이 있습니다.


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
"Lazy Solution"은 정말 나쁜 생각입니다. 스크롤 가능한 뷰 내부의 선형 레이아웃에 뷰를 추가하는 것은 매우 비효율적입니다.
Matthew

적어도 사용 가능한 것 이상이라고 생각합니다. 테스트 한 모든 장치에서 원활하게 빠르게 작동합니다. 그건 그렇고, 목록보기가 보이지 않을
때보기가

이것은 깔끔했습니다! 톤 감사합니다
Pawan Kumar

1
@Matthew가 언급했듯이 이것은 좋은 생각이 아닙니다. LinearLayouts가있는 하나의 큰 스크롤 가능한보기를 갖는 것은 잘 확장되지 않습니다. RecyclerView / ListView 및 기타 유사한 뷰가 작성된 가장 큰 이유 중 하나는 알 수없는 크기의 대규모 데이터 지원 목록을 최적화하기 위해서였습니다. 여러 뷰가 추가 된 단일 뷰를 빌드하면 제공된 모든 최적화가 제거됩니다. 뷰 재활용은 큰 이점이며 앱 메모리를 효율적으로 만듭니다. 항목 수가 적지 않으면보기 바인딩을 처리하기 위해 목록을 사용하여 많은 작업이 절약됩니다.
munkay

당신 말이 맞습니다. 물론 이것은 완벽하지 않고 어떤 것도 최적화하지 않습니다 ... 제 사용 사례에서는 항상 몇 가지 항목 만있었습니다 ... 그래서 이것은 문제가되지 않았습니다 ... Btw, 그동안 방법을 찾았습니다 중첩 된 리사이클 러 뷰의 경우 ... 고정 높이 수평 리사이클 러 뷰 (모든 사용 사례에 완벽하지는 않지만 여전히)를 중첩으로 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), 이름, 항목의 유형 (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.