简体   繁体   English

如何检测 RecyclerView 是否为空?

[英]How to detect if RecyclerView is empty?

I have a RecyclerView getting external JSON data parsed from a server.我有一个 RecyclerView 从服务器解析外部 JSON 数据。 It works fine however the Volley async task on JSON sometimes takes a while and when it does the fragment displays an empty blank view.它工作正常,但是 JSON 上的 Volley 异步任务有时需要一段时间,当它完成时,片段会显示一个空的空白视图。

How can I create a test to check if the view is empty and display a msg if it is?我如何创建一个测试来检查视图是否为空,如果是则显示一条消息? I tried to check:我试图检查:

if (recyclerView == null)
if (jsonList == null)
if (adapter.getItemCount() == 0)
if (bundle == null)

But those tests either dont do anything or they display the error message every single time even if the RecyclerView is not empty.但是这些测试要么不做任何事情,要么每次都显示错误消息,即使 RecyclerView 不为空。

This is the code on the fragment:这是片段上的代码:

public void onViewCreated(View view, Bundle savedInstanceState) {

    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    layoutManager.supportsPredictiveItemAnimations();
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setClickable(true);
    recyclerView.setHasFixedSize(true);
    recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
    NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList);
    if (novaAdapter.getItemCount() != 0) {
        recyclerView.setAdapter(novaAdapter);
    }else{
        pDialog = new ProgressDialog(getActivity());
        pDialog.setMessage("Retrieving data from Server");
        pDialog.show();            
    }
    super.onViewCreated(view, savedInstanceState);

and the method on the Adapter:和适配器上的方法:

@Override
public int getItemCount() {
    return (null != novaList ? novaList.size() : 0);
}

The way it is now the progress dialog always run no matter if the view is empty or not.现在的方式是无论视图是否为空,进度对话框始终运行。

UPDATE: Here's the adapter code:更新:这是适配器代码:

public class NovaAdapter extends RecyclerView.Adapter<NovaListRowHolder> {

ArrayList<HashMap<String, String>> novaList = new ArrayList<HashMap<String, String>>();
public static final String STATUS = "status";
public static final String NAME = "name";
public static final String ID = "id";
private Context mContext;

public NovaAdapter(Context context, ArrayList<HashMap<String, String>> novaList) {
    this.novaList = novaList;
    this.mContext = context;
}

@Override
public NovaListRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.instances_list, null);
    NovaListRowHolder mh = new NovaListRowHolder(v);
    return mh;
}

@Override
public void onBindViewHolder(NovaListRowHolder novaListRowHolder, int i) {

    HashMap<String, String> e = novaList.get(i);
    novaListRowHolder.name.setText(e.get(NAME));
    novaListRowHolder.status.setText(e.get(STATUS));
    novaListRowHolder.setId(e.get(ID));


}


@Override
public int getItemCount() {
    return (null != novaList ? novaList.size() : 0);
}class NovaListRowHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
protected TextView name;
protected TextView status;
protected String id;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public NovaListRowHolder(View view) {
    super(view);
    view.setOnClickListener(this);
    this.name = (TextView) view.findViewById(R.id.nameInstance);
    this.status = (TextView) view.findViewById(R.id.statusInstance);

}

public void onClick(View view){
    Dialog dialog = new Dialog(view.getContext());
    dialog.setContentView(R.layout.instances_listdetail);
    dialog.setTitle("Details " + name.getText() + " " + getPosition());
    dialog.show();
}

UPDATE2:更新2:

I updated another class which is pretty much the same as the one above with a callback interface however now the recyclerView displays for 1 second and then goes blank.我更新了另一个类,它与上面的类几乎相同,带有回调接口,但是现在 recyclerView 显示 1 秒,然后变为空白。 The dialog doesn't even show.该对话框甚至不显示。 Here's the code:这是代码:

public class SubnetsFragment extends Fragment implements OnJSONLoaded{
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";
    private OnFragmentInteractionListener mListener;
    public ArrayList<HashMap<String, String>> jsonList;
    public RecyclerView recyclerView;
    public ProgressDialog pDialog;
        /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static SubnetsFragment newInstance(int sectionNumber) {
        SubnetsFragment fragment = new SubnetsFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }


    public SubnetsFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Bundle extras = getArguments();
        Serializable parsedList = extras.getSerializable("SubnetsParsed");
        jsonList = (ArrayList<HashMap<String, String>>)parsedList;
        if (extras == null){
            AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
            alert.setTitle("Token Expired");
            alert.setMessage("Authentication Token expired! Please login again.")
                    .setNeutralButton("Connect", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            Intent intent = new Intent(getActivity(), Login.class);
                            startActivity(intent);
                            getActivity().finish();
                            getFragmentManager().beginTransaction().remove(SubnetsFragment.this).commit();
                        }
                    });
            AlertDialog alertDialog = alert.create();
            alertDialog.show();
        }
        View rootView = inflater.inflate(R.layout.fragment_subnets, container, false);
        recyclerView = (RecyclerView)rootView.findViewById(R.id.subnetsRV);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        layoutManager.supportsPredictiveItemAnimations();
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setClickable(true);
        recyclerView.setHasFixedSize(true);
        recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));

        onJsonLoaded(jsonList);

    }

    @Override
    public void onJsonLoaded(ArrayList<HashMap<String, String>> list) {

        SubnetsParser.setOnJSONLoadedListener(new OnJSONLoaded() {
            @Override
            public void onJsonLoaded(ArrayList<HashMap<String, String>> list) {
                if (list.size() != 0){
                    SubnetsAdapter subnetsAdapter = new SubnetsAdapter(getActivity(),jsonList);
                    recyclerView.setAdapter(subnetsAdapter);
                }else {
                    pDialog = new ProgressDialog(getActivity());
                    pDialog.setMessage("Retrieving data from Server");
                    pDialog.show();
                }
            }
        });

    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((Stackerz) activity).onSectionAttached(
                getArguments().getInt(ARG_SECTION_NUMBER));
        //try {
        //    mListener = (OnFragmentInteractionListener) activity;
        //} catch (ClassCastException e) {
        //    throw new ClassCastException(activity.toString()
        //            + " must implement OnFragmentInteractionListener");
        //}
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }




    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        public void onFragmentInteraction(Uri uri);
    }
}

And this is the JSON Parser class:这是 JSON Parser 类:

public class SubnetsParser extends Activity{
    public static final String NAME = "name";
    public static final String GW = "gw";
    public static final String CIDR = "cidr";
    public static final String ID = "id";

    public String authToken;
    public String neutronURL;

    public static SubnetsParser parser = null;

    public static OnJSONLoaded mListener;

    public static void setOnJSONLoadedListener(OnJSONLoaded listener) {
        mListener = listener;
    }

    public interface OnJSONLoaded {
        void onJsonLoaded(ArrayList<HashMap<String, String>> list);
    }

    public static SubnetsParser shared(){
        if (parser  == null){
            parser  = new SubnetsParser();
        }
        return parser ;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }





    public static ArrayList<HashMap<String, String>> parseJSON(String subnetsJSON){
        ArrayList<HashMap<String, String>> jsonList = new ArrayList<HashMap<String, String>>();
        try {
            Subnets subnets = new Subnets();
            JSONObject subnet = new JSONObject(subnetsJSON);
            JSONArray subnetobj = subnet.getJSONArray("subnets");
            for (int i = 0; i < subnetobj.length(); i++) {
                JSONObject objsrv = subnetobj.getJSONObject(i);
                subnets.setName(objsrv.getString("name"));
                subnets.setGw(objsrv.getString("gateway_ip"));
                subnets.setCidr(objsrv.getString("cidr"));
                subnets.setId(objsrv.getString("id"));
                HashMap<String, String> map = new HashMap<String, String>();
                map.put(NAME, subnets.getName());
                map.put(GW, subnets.getGw());
                map.put(CIDR, subnets.getCidr());
                map.put(ID, subnets.getId());
                jsonList.add(map);
            }
        } catch (JSONException e) {
            Log.d("ErrorInitJSON", e.toString());
            e.printStackTrace();
        }

        Collections.sort(jsonList, new Comparator<HashMap<String, String>>() {
            @Override
            public int compare(HashMap<String, String> lhs, HashMap<String, String> rhs) {
                return (lhs.get("name")).compareToIgnoreCase(rhs.get("name"));
            }
        });

        if (mListener != null) {
            mListener.onJsonLoaded(jsonList);
        }

        return jsonList;

    }


}

You can check if it's empty by running:您可以通过运行来检查它是否为空:

if (adapter.getItemCount() == 0)

If it's not working it means you haven't Override the getItemCount on your adapter!如果它不起作用,则意味着您尚未覆盖适配器上的 getItemCount! so make sure it's overrided:所以确保它被覆盖:

@Override
public int getItemCount() {
    return mDataSet.size(); // Where mDataSet is the list of your items
}

Update: So based on your update this is how you could proceed.更新:所以根据您的更新,这是您可以继续的方式。 In my opinion you just need a callback.在我看来,你只需要一个回调。 You are checking if the list is empty on your onViewCreated.您正在检查 onViewCreated 上的列表是否为空。 You should, instead, use a callback.相反,您应该使用回调。 Do something like that:做这样的事情:

public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    layoutManager.supportsPredictiveItemAnimations();
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setClickable(true);
    recyclerView.setHasFixedSize(true);
    recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));

    pDialog = new ProgressDialog(getActivity());
    pDialog.setMessage("Retrieving data from Server");
    pDialog.show();   
}

In the class you are using to populate your jsonList, I assume an asynctask or a separate class add this:在您用来填充 jsonList 的类中,我假设一个 asynctask 或一个单独的类添加以下内容:

private OnJsonLoaded mListener;

public void setOnJsonLoadedListener(OnJsonLoaded listener) {
    mListener = listener;
}

public interface OnJsonLoaded {
    void onJsonLoaded(ArrayList<HashMap<String, String>> list);
}

now, in the asynctask that populate ur jsonLise or when the json parser finish his job, call the listener:现在,在填充 jsonLise 的异步任务中或当 json 解析器完成他的工作时,调用侦听器:

if (mListener != null) {
    mListener.onJsonLoaded(jsonList);
}

In your fragment (the one with NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); and your recyclerview) add the interface implementation:在您的片段(带有 NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); 和您的 recyclerview 的片段)中添加接口实现:

classThatParseJson.setOnJsonLoadedListener(new OnJsonLoaded() {
        @Override
        public void onJsonLoaded(ArrayList<HashMap<String, String>> list) {
            if (list.size() != 0) {
                NovaAdapter novaAdapter = new NovaAdapter(getActivity(),jsonList); 
                recyclerView.setAdapter(novaAdapter);
            } else {
                // Show something like a dialog that the json list is 0 or do whatever you want... here the jsonlist have a count of 0 so it's empty!
            }  
        }
});

the code may containts errors, i written it by hand without using IDE so maybe you have to fix small things but the logic is quite clear!代码可能包含错误,我在没有使用 IDE 的情况下手工编写,所以也许您必须修复一些小问题,但逻辑很清楚!

Update based on your Update 2:根据您的更新 2 进行更新:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_subnets, container, false);
    recyclerView = (RecyclerView)rootView.findViewById(R.id.subnetsRV);
    return rootView;
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    layoutManager.supportsPredictiveItemAnimations();
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setClickable(true);
    recyclerView.setHasFixedSize(true);
    recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));

    // start json parser here instead of passing to fragment as a bundle
    SubnetsParser.parseJSON(yourparams);
}

@Override
public void onJsonLoaded(ArrayList<HashMap<String, String>> list) {

    SubnetsParser.setOnJSONLoadedListener(new OnJSONLoaded() {
        @Override
        public void onJsonLoaded(ArrayList<HashMap<String, String>> list) {
            if (list.size() != 0){
                SubnetsAdapter subnetsAdapter = new SubnetsAdapter(getActivity(),jsonList);
                recyclerView.setAdapter(subnetsAdapter);
            }else {
                //pDialog = new ProgressDialog(getActivity());
                //pDialog.setMessage("Retrieving data from Server");
                //pDialog.show();
                //Instead of a progressdialog, put here a dialog informing that the list is empty!
            }
        }
    });

}

How is described in https://developer.android.com/training/material/lists-cards.html如何在https://developer.android.com/training/material/lists-cards.html 中描述

The overriden method getItemCount() is invoked by the layout manager.覆盖方法getItemCount()由布局管理器调用。 This is the snippet:这是片段:

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.length;
}

So to detect if the recyclerView is empty you must request it to your LayoutManager .因此,要检测recyclerView是否为空,您必须向LayoutManager请求它。 Example:例子:

if( mLayoutManager.getItemCount() == 0 ){
    //Do something
}

I try to getItemCount() of my Adapter but this returns 0, I don't know why it is...我尝试获取我的Adapter getItemCount()但这返回 0,我不知道为什么它是...

if (adapter.getItemCount() == 0)如果(适配器.getItemCount()== 0)

doing this worked for me...这样做对我有用...

You can do it using interface callback:您可以使用接口回调来做到这一点:

  1. Create interface创建接口
public interface OnAdapterCountListener {
    void onAdapterCountListener(int count);
}
  1. Add below variables and methods in adapter在适配器中添加以下变量和方法
private OnAdapterCountListener onAdapterCountListener; 
    public void setOnAdapterCountListener(OnAdapterCountListener l) { 
        onAdapterCountListener = l; 
    }
  1. Add this line in onCreateViewHolder of your adapter在适配器的 onCreateViewHolder 中添加此行
onAdapterCountListener.onAdapterCountListener(getItemCount());
  1. Finally, call interface in your activity最后,在您的活动中调用接口
listAdapter.setOnAdapterCountListener(new OnAdapterCountListener() {
    @Override 
    public void onAdapterCountListener(int count) { 
        if (count > 0) 
            adapterEmptyText.setVisibility(View.GONE); 
    }
});

Create a Class and inherit Recyclerview - Follow the bellowing code创建一个类并继承 Recyclerview - 遵循以下代码

public class RecyclerviewEmpty extends RecyclerView {

private View emptyView;

private final AdapterDataObserver observer = new AdapterDataObserver() {
    @Override
    public void onChanged() {
        checkIfEmpty();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        checkIfEmpty();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        checkIfEmpty();
    }
};

public RecyclerviewEmpty(@NonNull Context context) {
    super(context);
}

public RecyclerviewEmpty(@NonNull Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public RecyclerviewEmpty(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


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

@Override
public void setAdapter(@Nullable Adapter adapter) {
    final Adapter oldAdapter = getAdapter();
    if (oldAdapter != null) {
        oldAdapter.unregisterAdapterDataObserver(observer);
    }
    super.setAdapter(adapter);
    if (adapter != null) {
        adapter.registerAdapterDataObserver(observer);
    }

    checkIfEmpty();
}

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

activity_xml - define class name in a View activity_xml - 在视图中定义类名

    <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_main"
    tools:context=".Ui.FavouriteActivity">


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <view
            android:id="@+id/rv_fav_activity"
            class="com.kumar.random.quotes.Views.RecyclerviewEmpty"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/list_empty1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="Empty Favourite"
            android:textSize="14sp" />
    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Activity.java活动.java

    private RecyclerviewEmpty rvFavourite;
private void setUpRv() {
        rvFavourite.setEmptyView(emptyView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        MainQuotesAdapter adapter = new MainQuotesAdapter(this, favouriteItem);
        rvFavourite.setLayoutManager(layoutManager);
        rvFavourite.setAdapter(adapter);
    }

Please notes_01:请注意_01:

 rvFavourite.setEmptyView(emptyView);

Please Notes_02:请注意_02:

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

Please Notes_03请注意_03

 <view
        android:id="@+id/rv_fav_activity"
        class="com.kumar.random.quotes.Views.RecyclerviewEmpty"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM