Danh sách có thể mở rộng với RecyclerView?


95

Có thể sử dụng các mục danh sách có thể mở rộng với RecyclerView mới không? Thích ExpandableListView?


1
bạn có thể thấy đồng hồ Android trong nguồn google trên android 6.0
zys

@zys Tôi có thể tìm mã nguồn mẫu đồng hồ android này ở đâu?
AppiDevo

1
Bạn có thể sử dụng viewType khác nhau để tải bố trí khác nhau, khi bạn nhấp mở rộng giải pháp button.This được sử dụng bởi Android Clock: android.googlesource.com/platform/packages/apps/DeskClock
zys

xem câu trả lời đơn giản của tôi stackoverflow.com/a/48092441/5962715
Kiran Benny Joseph

Câu trả lời:


121

Điều này rất đơn giản để thực hiện với LayoutManagers có sẵn, tất cả phụ thuộc vào cách bạn quản lý bộ điều hợp của mình.

Khi bạn muốn mở rộng một phần, bạn chỉ cần thêm các mục mới vào bộ điều hợp sau tiêu đề. Hãy nhớ gọi thông báo ItemRangeInserted khi bạn làm điều này. Để thu gọn một phần, bạn chỉ cần loại bỏ các mục có liên quan và gọi thông báo SmartItemRangeRemoved (). Đối với bất kỳ thay đổi dữ liệu nào được thông báo thích hợp, chế độ xem trình tái chế sẽ tạo hoạt ảnh cho các chế độ xem. Khi thêm các mục, một khu vực được lấp đầy với các mục mới sẽ được tạo ra, với các mục mới sẽ mờ dần. Việc loại bỏ ngược lại. Tất cả những gì bạn cần làm ngoài công cụ điều hợp là tạo kiểu cho các quan điểm của bạn để truyền tải cấu trúc hợp lý cho người dùng.

Cập nhật: Ryan Brooks hiện đã viết một bài báo về cách thực hiện việc này.


Gợi ý tuyệt vời. Tự hỏi tại sao không ai khác ủng hộ câu trả lời này !!
x-treme

Tôi sẽ xem về việc thêm điều này làm ví dụ cho SuperSLiM như một phần của bản phát hành tiếp theo.
Tonic Artos

Ryan Brooks hiện đã viết một bài báo về cách thực hiện điều này.
Tonic Artos,

Gợi ý tuyệt vời. Tại sao câu trả lời này ở tận trang mà tôi phải cuộn xuống để tìm nó? Nó phải được hiển thị ở trên cùng để nhiều người có thể tìm thấy câu trả lời hay này dễ dàng hơn.
RestInPeace

1
Ryan Brooks đã đánh dấu thư viện của mình là không dùng nữa. Tôi tự hỏi nếu nó chỉ rằng ông đã ngừng hỗ trợ nó hay nó quay ra rằng điều này phá vỡ cách tiếp cận một cái gì đó hoặc tạo ra rò rỉ bộ nhớ hoặc sth ...
Varvara Kalinina

6

Nhận triển khai mã mẫu từ đây

Đặt ValueAnimator bên trong onClick of ViewHolder

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

}

Đây là mã cuối cùng

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
Điều này không hoạt động "giống như ExpandableListView", bởi vì nội dung mở rộng trong trường hợp đó là một danh sách với các mục đến từ bộ điều hợp. Đây là một giải pháp thoái hóa chỉ được phép sử dụng 1 mục là trẻ em trong nhóm.
TWiStErRob

cũng không cho nó hoạt động đúng với quan điểm mà có được tái chế ở tất cả
TakeCare

Nó không hoạt động đúng như một cái nhìn trong RecyclerList có thể được lặp đi lặp lại nhiều lần, vì vậy khi bạn mở rộng một mục, Bạn thấy nhiều mục trong danh sách được mở rộng
Hossein Shahdoost

3

https://github.com/gabrielemariotti/cardslib

Thư viện này có triển khai danh sách có thể mở rộng với chế độ xem lại (tham khảo ứng dụng demo trong "CardViewNative" -> "Danh sách, Grid và RecyclerView" -> "Thẻ có thể mở rộng"). Nó cũng có rất nhiều kết hợp thẻ / danh sách thú vị khác.


2
Danh sách mở rộng Thẻ Đây không phải là một đứa trẻ của RecyclerView (nó không kéo dài RecyclerView, nó chỉ kéo dài ExpandableListVIew)
whizzzkey

0

Có người phàn nàn rằng giải pháp được đề cập ở trên không thể sử dụng được với listview là nội dung có thể mở rộng. Nhưng có một giải pháp đơn giản: tạo một listview và điền thủ công listview này vào các hàng của bạn .

Giải pháp cho những kẻ lười biếng: có một giải pháp đơn giản nếu bạn không muốn thay đổi nhiều mã của mình. Chỉ cần sử dụng thủ công bộ điều hợp của bạn để tạo chế độ xem và thêm chúng vào LinearLayout.

Đây là ví dụ:

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

chức năng trợ giúp: 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;
    }
}

lớp trợ giúp: ExpandUtils

Bài đăng của Kavin Varnan đã có cách tạo hiệu ứng cho bố cục ... Nhưng nếu bạn muốn sử dụng lớp học của tôi, vui lòng làm như vậy, tôi đã đăng ý chính: https://gist.github.com/MichaelFlisar/738dfa03a1579cc7338a


9
"Giải pháp lười biếng" là một ý tưởng thực sự tồi. Việc thêm các chế độ xem vào một tuyến tính bên trong một chế độ xem có thể cuộn như vậy không hiệu quả.
Matthew

Tôi nghĩ rằng nó ít nhất là nhiều hơn khả năng sử dụng. Hoạt động nhanh và mượt mà trên tất cả các thiết bị tôi đã thử nghiệm. Bằng cách này, các quan điểm được bổ sung khi listview không nhìn thấy được ... chỉ xem danh sách đã được điền sẽ sau đó được hiển thị ...
prom85

Điều này thật gọn gàng! Cảm ơn rất nhiều
Pawan Kumar

1
Như @Matthew đã đề cập, đây thực sự không phải là một ý kiến ​​hay. Có một chế độ xem lớn có thể cuộn duy nhất w / LinearLayouts không mở rộng quy mô tốt. Một trong những lý do lớn nhất mà RecyclerView / ListView và các chế độ xem tương tự khác được viết là để tối ưu hóa việc có các danh sách được hỗ trợ dữ liệu lớn với kích thước không xác định. Việc xây dựng một chế độ xem duy nhất với một loạt các chế độ xem được thêm vào sẽ loại bỏ tất cả các tối ưu hóa được cung cấp. Tái chế chế độ xem là một lợi ích to lớn và làm cho bộ nhớ ứng dụng của bạn hiệu quả. Trừ khi số lượng mục là rất nhỏ, có rất nhiều công việc được lưu bằng cách sử dụng danh sách để xử lý ràng buộc chế độ xem.
munkay

Bạn hoàn toàn đúng, tất nhiên điều này không hoàn hảo và không tối ưu hóa bất cứ thứ gì ... Đối với cơ sở dữ liệu của tôi, tôi luôn chỉ có một vài mục ... Vì vậy, điều này không có vấn đề gì ... Btw, trong lúc đó tôi đã tìm ra cách cho chế độ xem bộ tái chế lồng nhau ... Chỉ cần sử dụng chế độ xem bộ tái chế ngang có chiều cao cố định (không hoàn hảo cho mọi usecase, nhưng vẫn hoàn hảo) như một chế độ lồng nhau recyclerviewvà bạn có thể mở rộng / ẩn cái lồng nhau này và sử dụng tất cả các tối ưu củarecyclerview
prom85


0

Đây là mã mẫu cho những gì được đề cập bởi @TonicArtos để thêm và xóa các Mục và để tạo hoạt ảnh trong khi thực hiện, mã này được lấy từ mẫu RecyclerView AnimationsGitHub

1) Thêm Trình xử lý bên trong onCreateViewHolder () của bạn để đăng ký onClick

2) Tạo OnClickListener tùy chỉnh của bạn bên trong Bộ điều hợp của bạn

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) Thêm addItem () và deleteItem () của bạn

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) Nếu RecyclerViewAdapter của bạn không có cùng Hoạt động với Recycler View , hãy chuyển phiên bản của recyclerView vào Bộ điều hợp trong khi tạo

5) itemList là ArrayList kiểu mObject giúp duy trì trạng thái của item (Open / Close), tên, loại Item (subItems / mainItem) và đặt Theme dựa trên các giá trị

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.