จะแสดงมุมมองที่ว่างด้วย RecyclerView ได้อย่างไร


271

ฉันกำลังใช้ในการวางมุมมองพิเศษในไฟล์รูปแบบตามที่อธิบายไว้ในListActivityเอกสารที่จะปรากฏขึ้นเมื่อไม่มีข้อมูล "android:id/empty"มุมมองนี้มีประชาชนได้

<TextView
    android:id="@android:id/empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/no_data" />

ฉันสงสัยว่าจะสามารถทำสิ่งนี้กับใหม่ได้RecyclerViewอย่างไร


2
คุณสามารถใช้github.com/rockerhieu/rv-adapter-statesมันสนับสนุนไม่เพียง แต่มุมมองที่ว่างเปล่า แต่ยังโหลดมุมมองและมุมมองข้อผิดพลาด และคุณสามารถใช้ได้โดยไม่ต้องเปลี่ยนลอจิกของอะแดปเตอร์ที่มีอยู่
Hieu Rocker

20
มันไม่มีอะไรนอกจากที่น่าหลงใหลที่ดูเหมือนจะไม่มีsetEmptyView()วิธีการที่ง่าย ๆสำหรับRecyclerView...
Subby

นี่คือคำตอบของฉันโปรดตรวจสอบลิงค์นี้stackoverflow.com/a/58411351/5449220
Rakesh Verma

@Subby เห็นด้วย แต่setEmptyView()ก็มีข้อเสีย เมื่อโหลดข้อมูลเราไม่ต้องการแสดงมุมมองที่ว่างเปล่าListViewแต่เป็นเช่นนั้น ดังนั้นเราต้องใช้ตรรกะบางอย่าง ปัจจุบันผมใช้stackoverflow.com/a/48049142/2914140
CoolMind

คำตอบ:


305

บนเค้าโครงเดียวกันกับที่กำหนดไว้ให้RecyclerViewเพิ่มTextView:

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

<TextView
    android:id="@+id/empty_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:visibility="gone"
    android:text="@string/no_data_available" />

ที่onCreateหรือโทรกลับที่เหมาะสมคุณตรวจสอบว่าชุดข้อมูลที่ฟีดของคุณRecyclerViewว่างเปล่า หากชุดข้อมูลว่างเปล่าก็จะเป็นชุดข้อมูลRecyclerViewว่างเช่นกัน ในกรณีนั้นข้อความจะปรากฏขึ้นบนหน้าจอ ถ้าไม่เปลี่ยนการมองเห็น:

private RecyclerView recyclerView;
private TextView emptyView;

// ...

recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
emptyView = (TextView) rootView.findViewById(R.id.empty_view);

// ...

if (dataset.isEmpty()) {
    recyclerView.setVisibility(View.GONE);
    emptyView.setVisibility(View.VISIBLE);
}
else {
    recyclerView.setVisibility(View.VISIBLE);
    emptyView.setVisibility(View.GONE);
}

11
สิ่งนี้ไม่ได้ผลสำหรับฉันเนื่องจากเลย์เอาต์มุมมอง recycler นั้นมีขนาดเล็กลงในขณะที่การตัดสินใจว่าจะแสดงเลย์เอาต์รายการปกติหรือโครงร่างไม่มีไอเท็มทำในอะแด็ปเตอร์
JJD

1
@JJD เมื่อใช้ Fragment จะใช้วิธีเดียวกัน เมื่อคุณขยายมุมมองแฟรกเมนต์ของคุณคุณจะมี recyclerview และเลย์เอาต์อื่นที่จะใช้สำหรับมุมมองที่ว่างเปล่าของคุณ โดยปกติในส่วนของฉันฉันจะมีมุมมองดังต่อไปนี้: 1) recyclerview, 2) มุมมองที่ว่างเปล่าและ 3) มุมมองแถบความคืบหน้า ฉันมักจะผ่านอะแดปเตอร์ไม่ว่ารายการจะเป็นอะไร แต่ขึ้นอยู่กับขนาดของรายการที่ฉันจะซ่อน recyclerview และแสดงว่างเปล่าหรือในทางกลับกัน หาก recyclerview มีรายการว่างเปล่ามันจะไม่ทำอะไรกับมัน คุณเพิ่งเห็นมุมมองที่ว่างเปล่า
เรย์ฮันเตอร์

1
@stackex การตรวจสอบจะต้องอยู่ในการเรียกกลับ onCreateView ของ Fragment หรือ onResume of Activity ด้วยวิธีนี้การตรวจสอบจะทำทันทีก่อนที่หน้าจอจะปรากฏต่อผู้ใช้และจะทำงานได้ทั้งการเพิ่มหรือลดจำนวนแถวในชุดข้อมูล
slellis

2
@Zapnologica คุณสามารถทำเช่นนั้นได้: ใช้ Eventbus (เช่น greenrobots Eventbus หรือ Otto) จากนั้นให้ Adaptor Post บน eventbus เมื่อชุดข้อมูลเปลี่ยนไป ในแฟรกเมนต์ทั้งหมดที่คุณทำคือสร้างวิธีที่พารามิเตอร์สอดคล้องกับสิ่งที่ตัวปรับต่อเข้าสู่เมธอด Eventbus.post แล้วเปลี่ยนเลย์เอาต์ตามลำดับ แนวทางที่เรียบง่าย แต่มีความเชื่อมโยงกันมากขึ้นคือการสร้างอินเตอร์เฟสการโทรกลับในอะแด็ปเตอร์และเมื่อสร้างอะแด็ปเตอร์
AgentKnopf

2
ในเลย์เอาต์ฉันได้รับแท็กรูทจำนวนมาก ไฟล์เลย์เอาต์แบบเต็มของคุณมีหน้าตาเป็นอย่างไร
powder366

192

สำหรับโครงการของฉันฉันทำโซลูชันนี้ ( RecyclerViewด้วยsetEmptyViewวิธี):

public class RecyclerViewEmptySupport extends RecyclerView {
    private View emptyView;

    private AdapterDataObserver emptyObserver = new AdapterDataObserver() {


        @Override
        public void onChanged() {
            Adapter<?> adapter =  getAdapter();
            if(adapter != null && emptyView != null) {
                if(adapter.getItemCount() == 0) {
                    emptyView.setVisibility(View.VISIBLE);
                    RecyclerViewEmptySupport.this.setVisibility(View.GONE);
                }
                else {
                    emptyView.setVisibility(View.GONE);
                    RecyclerViewEmptySupport.this.setVisibility(View.VISIBLE);
                }
            }

        }
    };

    public RecyclerViewEmptySupport(Context context) {
        super(context);
    }

    public RecyclerViewEmptySupport(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerViewEmptySupport(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);

        if(adapter != null) {
            adapter.registerAdapterDataObserver(emptyObserver);
        }

        emptyObserver.onChanged();
    }

    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
    }
}

และคุณควรใช้มันแทนRecyclerViewคลาส:

<com.maff.utils.RecyclerViewEmptySupport android:id="@+id/list1"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    />

<TextView android:id="@+id/list_empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Empty"
    />

และ

RecyclerViewEmptySupport list = 
    (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1);
list.setLayoutManager(new LinearLayoutManager(context));
list.setEmptyView(rootView.findViewById(R.id.list_empty));

2
คุณไม่ได้ใช้รหัสนี้จากคำตอบนี้หรือจากที่นี่ ?
Sufian

@Sufian ไม่ฉันไม่เห็นคำตอบนั้นพบวิธีเดียวกัน แต่รหัสที่แน่นอนดูสวยกว่าเหมือง :)
maff91

2
ฉันพยายามรวมสิ่งนี้กับ FirebaseRecyclerAdapter มันใช้งานไม่ได้ นี่เป็นเพราะ FirebaseRecyclerAdapter ไม่เรียกใช้ onChange (ของ AdapterDataObserver) แต่จะเรียกใช้ onItem * -methods แทน เพื่อให้มันทำงานเพิ่ม: `แทนที่โมฆะสาธารณะ onItemRangeRemoved (int positionStart, int itemCount) {// the check} แทนที่โมฆะสาธารณะสาธารณะ onItemRangeMoved (int fromPosition, int toPosition, int itemCount) {// the check} // ฯลฯ .. '
FrankkieNL

1
ทำไมคุณตรวจสอบว่าอะแดปเตอร์เป็นโมฆะในผู้สังเกตการณ์หรือไม่ หากอะแดปเตอร์เป็นโมฆะผู้สังเกตการณ์ไม่ควรถูกเรียก
Stimsoni

16
เอ่อฉันต้องบอกว่านี่เป็นแค่ Google BS แบบคลาสสิก ฉันต้องใช้สิ่งนี้เพียงเพื่อเพิ่มมุมมองที่ว่างเปล่า? ควรรวมอยู่ใน cr @ psh1t SDK! เพื่อประโยชน์!
Neon Warge

62

นี่คือวิธีแก้ไขโดยใช้เฉพาะอะแดปเตอร์ที่กำหนดเองที่มีประเภทมุมมองที่แตกต่างกันสำหรับสถานการณ์ที่ว่างเปล่า

public class EventAdapter extends 
    RecyclerView.Adapter<EventAdapter.ViewHolder> {

    private static final int VIEW_TYPE_EVENT = 0;
    private static final int VIEW_TYPE_DATE = 1;
    private static final int VIEW_TYPE_EMPTY = 2;

    private ArrayList items;

    public EventAdapter(ArrayList items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        if(items.size() == 0){
            return 1;
        }else {
            return items.size();
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (items.size() == 0) {
            return VIEW_TYPE_EMPTY;
        }else{
            Object item = items.get(position);
            if (item instanceof Event) {
                return VIEW_TYPE_EVENT;
            } else {
                return VIEW_TYPE_DATE;
            }
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        ViewHolder vh;
        if (viewType == VIEW_TYPE_EVENT) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event, parent, false);
            vh = new ViewHolderEvent(v);
        } else if (viewType == VIEW_TYPE_DATE) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_date, parent, false);
            vh = new ViewHolderDate(v);
        } else {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_empty, parent, false);
            vh = new ViewHolder(v);
        }

        return vh;
    }

    @Override
    public void onBindViewHolder(EventAdapter.ViewHolder viewHolder, 
                                 final int position) {
        int viewType = getItemViewType(position);
        if (viewType == VIEW_TYPE_EVENT) {
            //...
        } else if (viewType == VIEW_TYPE_DATE) {
            //...
        } else if (viewType == VIEW_TYPE_EMPTY) {
            //...
        }
    }

    public static class ViewHolder extends ParentViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    public static class ViewHolderDate extends ViewHolder {

        public ViewHolderDate(View v) {
            super(v);
        }
    }

    public static class ViewHolderEvent extends ViewHolder {

        public ViewHolderEvent(View v) {
            super(v);
        }
    }

}

3
@ sfk92fksdf เหตุใดจึงเกิดข้อผิดพลาดได้ง่าย นี่คือวิธีที่คุณควรจัดการกับประเภทมุมมองหลายประเภทใน recyclerview
MSpeed

@billynomates เพราะมันgetItemCount()ควรจะเป็น oneliner แต่ที่นี่เราไม่ได้ifมีตรรกะเล็กน้อยที่ซ่อนอยู่ ตรรกะนี้ก็ปรากฏขึ้นอย่างเชื่องช้าgetItemViewTypeและพระเจ้าก็รู้ว่าจะมีใครอยู่อีก
Alexey Andronov

3
ไม่จำเป็นต้องเป็นหนึ่งบรรทัด หากคุณมีหลายประเภทการดูนี่เป็นวิธีที่ทำได้
MSpeed

@ sfk92fksdf คุณหมายถึงอะไรโดยลอจิกที่ซ่อนอยู่? โค้ดอยู่ตรงนั้น▲
radu122

1
ในความเห็นของฉันคำตอบนี้ควรเป็นคำตอบที่ยอมรับได้ เป็นวิธีจัดการกับประเภทมุมมองแบบหลายคู่
Ivo Beckers

45

ฉันใช้ ViewSwitcher

<ViewSwitcher
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/switcher"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <TextView android:id="@+id/text_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/list_empty"
        android:gravity="center"
        />

</ViewSwitcher>

ในรหัสคุณจะตรวจสอบเคอร์เซอร์ / ชุดข้อมูลและสลับมุมมอง

void showItems(Cursor items) {
    if (items.size() > 0) {

        mAdapter.switchCursor(items);

        if (R.id.list == mListSwitcher.getNextView().getId()) {
            mListSwitcher.showNext();
        }
    } else if (R.id.text_empty == mListSwitcher.getNextView().getId()) {
        mListSwitcher.showNext();
    }
}

นอกจากนี้คุณสามารถตั้งค่าภาพเคลื่อนไหวหากคุณต้องการด้วยรหัสสองบรรทัด

mListSwitcher.setInAnimation(slide_in_left);
mListSwitcher.setOutAnimation(slide_out_right);

มันเป็นเรื่องแปลกใหม่จริงๆ
จางเซียง

น้ำยาทำความสะอาด
Ojonugwa Jude Ochalifu

30

เนื่องจากคำตอบของเควินนั้นไม่สมบูรณ์
นี่คือคำตอบที่ถูกต้องมากขึ้นถ้าคุณใช้RecyclerAdapter's notifyItemInsertedและnotifyItemRemovedการปรับปรุงชุด ดูเวอร์ชั่น Kotlin ผู้ใช้อื่นที่เพิ่มด้านล่าง

Java:

mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {

    @Override
    public void onChanged() {
        super.onChanged();
        checkEmpty();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        checkEmpty();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        super.onItemRangeRemoved(positionStart, itemCount);
        checkEmpty();
    }

    void checkEmpty() {
        mEmptyView.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
    }
});

Kotlin

adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    override fun onChanged() {
        super.onChanged()
        checkEmpty()
    }

    override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
        super.onItemRangeInserted(positionStart, itemCount)
        checkEmpty()
    }

    override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
        super.onItemRangeRemoved(positionStart, itemCount)
        checkEmpty()
    }

    fun checkEmpty() {
        empty_view.visibility = (if (adapter.itemCount == 0) View.VISIBLE else View.GONE)
    }
})

2
นี่เป็นทางออกที่ดี ฉันกำลังเสนอการแก้ไขซึ่งจะรวมรุ่น kotlin ด้วย
jungledev

สิ่งนี้ดูดี แต่อาจใช้ไม่ได้ถ้าคุณเรียก swapAdapter () หรือแม้แต่ setAdapter () มากกว่าหนึ่งครั้ง
Glaucus

ฉันคิดว่าเป็นทางออกที่ดีที่สุด!
ผู้ที่ชื่นชอบ android

การทำงานที่ดี. อย่าลืมซ่อน RecyclerView เมื่อแสดงป้ายกำกับ
MrG

18

RVEmptyObserver

แทนที่จะใช้แบบกำหนดเองการRecyclerViewขยายAdapterDataObserverเป็นโซลูชันที่ง่ายกว่าที่อนุญาตให้ตั้งค่าแบบกำหนดเองViewที่แสดงเมื่อไม่มีรายการในรายการ:

ตัวอย่างการใช้งาน:

RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView)
rvAdapter.registerAdapterDataObserver(observer);

ประเภท:

public class RVEmptyObserver extends RecyclerView.AdapterDataObserver {
    private View emptyView;
    private RecyclerView recyclerView;

    public RVEmptyObserver(RecyclerView rv, View ev) {
        this.recyclerView = rv;
        this.emptyView    = ev;
        checkIfEmpty();
    }

    private void checkIfEmpty() {
        if (emptyView != null && recyclerView.getAdapter() != null) {
            boolean emptyViewVisible = recyclerView.getAdapter().getItemCount() == 0;
            emptyView.setVisibility(emptyViewVisible ? View.VISIBLE : View.GONE);
            recyclerView.setVisibility(emptyViewVisible ? View.GONE : View.VISIBLE);
        }
    }

    public void onChanged() { checkIfEmpty(); }
    public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); }
    public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); }
}

ผมเชื่อว่ามันจะทำงานได้เร็วขึ้นถ้าคุณไม่โทรcheckIfEmpty()ในonChanged()และonItemRangeInserted()
Geekarist

ฉันเห็นด้วย (และนั่นคือสิ่งที่ฉันทำเอง) แต่นี่เป็นเพียงจุดเริ่มต้นสำหรับผู้ที่ประสบปัญหานี้
Sheharyar

9

ในการgetItemViewTypeตรวจสอบอะแดปเตอร์ของคุณว่าอะแดปเตอร์มีองค์ประกอบ 0 รายการและส่งคืน viewType อื่นถ้าใช่

จากนั้นในการonCreateViewHolderตรวจสอบของคุณว่า viewType เป็นสิ่งที่คุณกลับมาก่อนหน้านี้และขยายมุมมองที่แตกต่างกัน ในกรณีนี้ไฟล์เค้าโครงที่มี TextView นั้น

แก้ไข

หากนี่ยังใช้งานไม่ได้คุณอาจต้องการกำหนดขนาดของมุมมองแบบเป็นระบบดังนี้:

Point size = new Point();
((WindowManager)itemView.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

และเมื่อคุณขยายการเรียกดูของคุณ:

inflatedView.getLayoutParams().height = size.y;
inflatedView.getLayoutParams().width = size.x;

ฟังดูเข้าท่า. แต่ฉันไม่ได้รับในonCreateViewHolderเมื่อชุดของฉันคือnullหรือเปล่าเพราะผมต้องย้อนกลับไปใน0 getItemCount
JJD

จากนั้นกลับ 1 ถ้ามันเป็นโมฆะ :)
เปโดร Oliveira

ทำงานค่อนข้าง ขอบคุณ. - รายละเอียดเล็กน้อยคือฉันไม่สามารถรับรายการที่ว่างเปล่าที่อยู่กึ่งกลางแนวตั้งในมุมมองรีไซเคิล มุมมองข้อความยังคงอยู่ที่ด้านบนของหน้าจออย่างไรก็ตามฉันตั้งค่าandroid:layout_gravity="center"และandroid:layout_height="match_parent"สำหรับมุมมองผู้รีไซเคิล
JJD

ขึ้นอยู่กับเค้าโครงของแถวที่คุณกำลังใช้ ควรจับคู่กับความสูงของมัน หากคุณยังไม่สามารถใช้งานได้ให้เพิ่มผู้ฟังเลย์เอาต์ทั่วโลกและตั้งค่าพารามิเตอร์ของเลย์เอาต์ให้มีความสูงและความกว้างเท่ากับหน้าจอ
Pedro Oliveira

ภาชนะทุกชนิด (กิจกรรมส่วนรูปแบบเชิงเส้นมุมมองรีไซเคิล, มุมมองข้อความ) android:layout_height="match_parent"มีการกำหนดให้ แม้ว่าแถวจะไม่ขยายไปถึงความสูงของหน้าจอ
JJD

8

นี่คือคลาสของฉันสำหรับแสดงมุมมองที่ว่างเปล่าลองดูอีกครั้ง (เมื่อโหลด api ล้มเหลว) และกำลังโหลดสำหรับ RecyclerView

public class RecyclerViewEmptyRetryGroup extends RelativeLayout {
    private RecyclerView mRecyclerView;
    private LinearLayout mEmptyView;
    private LinearLayout mRetryView;
    private ProgressBar mProgressBar;
    private OnRetryClick mOnRetryClick;

    public RecyclerViewEmptyRetryGroup(Context context) {
        this(context, null);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        if (child.getId() == R.id.recyclerView) {
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            return;
        }
        if (child.getId() == R.id.layout_empty) {
            mEmptyView = (LinearLayout) findViewById(R.id.layout_empty);
            return;
        }
        if (child.getId() == R.id.layout_retry) {
            mRetryView = (LinearLayout) findViewById(R.id.layout_retry);
            mRetryView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRetryView.setVisibility(View.GONE);
                    mOnRetryClick.onRetry();
                }
            });
            return;
        }
        if (child.getId() == R.id.progress_bar) {
            mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        }
    }

    public void loading() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.VISIBLE);
    }

    public void empty() {
        mEmptyView.setVisibility(View.VISIBLE);
        mRetryView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void retry() {
        mRetryView.setVisibility(View.VISIBLE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void success() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    public void setOnRetryClick(OnRetryClick onRetryClick) {
        mOnRetryClick = onRetryClick;
    }

    public interface OnRetryClick {
        void onRetry();
    }
}

activity_xml

<...RecyclerViewEmptyRetryGroup
        android:id="@+id/recyclerViewEmptyRetryGroup">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"/>

        <LinearLayout
            android:id="@+id/layout_empty">
            ...
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_retry">
            ...
        </LinearLayout>

        <ProgressBar
            android:id="@+id/progress_bar"/>

</...RecyclerViewEmptyRetryGroup>

ป้อนคำอธิบายรูปภาพที่นี่

แหล่งที่มาอยู่ที่นี่https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry


7

ใช้ AdapterDataObserver ใน RecyclerView แบบกำหนดเอง

Kotlin:

RecyclerViewEnum.kt

enum class RecyclerViewEnum {
    LOADING,
    NORMAL,
    EMPTY_STATE
}

RecyclerViewEmptyLoadingSupport.kt

class RecyclerViewEmptyLoadingSupport : RecyclerView {

    var stateView: RecyclerViewEnum? = RecyclerViewEnum.LOADING
        set(value) {
            field = value
            setState()
        }
    var emptyStateView: View? = null
    var loadingStateView: View? = null


    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}


    private val dataObserver = object : AdapterDataObserver() {
        override fun onChanged() {
            onChangeState()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            super.onItemRangeRemoved(positionStart, itemCount)
            onChangeState()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            super.onItemRangeInserted(positionStart, itemCount)
            onChangeState()
        }
    }


    override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
        super.setAdapter(adapter)
        adapter?.registerAdapterDataObserver(dataObserver)
        dataObserver.onChanged()
    }


    fun onChangeState() {
        if (adapter?.itemCount == 0) {
            emptyStateView?.visibility = View.VISIBLE
            loadingStateView?.visibility = View.GONE
            this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
        } else {
            emptyStateView?.visibility = View.GONE
            loadingStateView?.visibility = View.GONE
            this@RecyclerViewEmptyLoadingSupport.visibility = View.VISIBLE
        }
    }

    private fun setState() {

        when (this.stateView) {
            RecyclerViewEnum.LOADING -> {
                loadingStateView?.visibility = View.VISIBLE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
                emptyStateView?.visibility = View.GONE
            }

            RecyclerViewEnum.NORMAL -> {
                loadingStateView?.visibility = View.GONE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.VISIBLE
                emptyStateView?.visibility = View.GONE
            }
            RecyclerViewEnum.EMPTY_STATE -> {
                loadingStateView?.visibility = View.GONE
                this@RecyclerViewEmptyLoadingSupport.visibility = View.GONE
                emptyStateView?.visibility = View.VISIBLE
            }
        }
    }
}

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/emptyView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/emptyLabelTv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="empty" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/loadingView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_gravity="center"
            android:indeterminate="true"
            android:theme="@style/progressBarBlue" />
    </LinearLayout>

    <com.peeyade.components.recyclerView.RecyclerViewEmptyLoadingSupport
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

ในกิจกรรมใช้วิธีนี้:

recyclerView?.apply {
        layoutManager = GridLayoutManager(context, 2)
        emptyStateView = emptyView
        loadingStateView = loadingView
        adapter = adapterGrid
    }

    // you can set LoadingView or emptyView manual
    recyclerView.stateView = RecyclerViewEnum.EMPTY_STATE
    recyclerView.stateView = RecyclerViewEnum.LOADING

1
ถ้าคุณใช้โซลูชันนั้นให้ระวังว่าการใช้ enums ไม่เพียงพอเนื่องจากขนาดการจัดสรรหน่วยความจำที่เพิ่มขึ้น ใช้จำนวนเต็มหรือสตริงขึ้นอยู่กับบริบทของคุณด้วยความเหมาะสมStringDefหรือIntDefคำอธิบายประกอบ
Andrii Artamonov

2

ฉันเพิ่มRecyclerViewและทางเลือกImageViewในการRelativeLayout:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/no_active_jobs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_active_jobs" />

    <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

แล้วในAdapter:

@Override
public int getItemCount() {
    if (mOrders.size() == 0) {
        mRecyclerView.setVisibility(View.INVISIBLE);
    } else {
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    return mOrders.size();
}

7
วิธีเข้าถึงmRecyclerViewภายในAdapter
Basheer AL-MOMANI

2
รูปแบบการออกแบบไม่ดีที่จะผูกอะแดปเตอร์กับกิจกรรมเฉพาะ เป็นการดีกว่าที่จะทำผ่านอินเตอร์เฟส
ruX

1
นอกจากนี้ยังทำให้เกิดการถอนเลย์เอาต์ซึ่งไม่แนะนำและอาจทำให้เกิดปัญหาประสิทธิภาพการทำงานบางอย่าง ดูyoutube.com/watch?v=T52v50r-JfEสำหรับรายละเอียดเพิ่มเติม
Felipe Conde

1

อีกวิธีหนึ่งคือการใช้งานaddOnChildAttachStateChangeListenerที่จับปรากฏ / RecyclerViewหายไปมุมมองเด็กใน

recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onChildViewDetachedFromWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.VISIBLE);
            }
        });

0

เพียงแค่กรณีที่คุณกำลังทำงานกับ FirebaseRecyclerAdapter โพสต์นี้ทำงานเป็นเสน่ห์https://stackoverflow.com/a/39058636/6507009


7
ในขณะที่ลิงค์นี้อาจตอบคำถามได้ดีกว่าที่จะรวมส่วนสำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง คำตอบสำหรับลิงค์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง
Anh Pham
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.