首页 > 解决方案 > 在 recyclerview 中使用 ViewModel 和 Retrofit 实现无限滚动

问题描述

在添加 viewmodel 和 livedata 之前,我通过改造成功实现了无限滚动。但是在使用 Retrofit 添加 viewmodel 和 livedata 之后,我无法使用新的数据调用更新 recyclerview 或 viewmodel 观察者不更新列表。

我只是想像以前的代码一样无限滚动。我添加了一个全局变量来重用下一页令牌。我是否遗漏了任何东西或任何样本来实现具有 viewmodel 和改造的无限 recyclerview 会很棒。

public static String NEXT_PAGE_URL = null;

我就是这样编码的。

我的活动 - > PlaceListActivity

placeRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            LogMe.d(tag, "onScrollStateChanged:: " + "called");
            // check scrolling started or not
            if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                isScrolling = true;
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            LogMe.d(tag, "onScrolled:: " + "called");
            super.onScrolled(recyclerView, dx, dy);

            currentItem = layoutManager.getChildCount();
            totalItems = layoutManager.getItemCount();
            scrolledOutItems = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();

            LogMe.d(tag, "currentItem:: " + currentItem);
            LogMe.d(tag, "totalItems:: " + totalItems);
            LogMe.d(tag, "scrolledOutItems:: " + scrolledOutItems);

            if (isScrolling && (currentItem + scrolledOutItems == totalItems)) {
                LogMe.d(tag, "view:: " + "finished");
                isScrolling = false;
                if (ApplicationData.NEXT_PAGE_URL != null) {
                    LogMe.d(tag, "place adding:: " + " onScrolled called");
                    ll_loading_more.setVisibility(View.VISIBLE);

                    // todo: call web api here
                    callDataFromLocationAPi(type, ApplicationData.NEXT_PAGE_URL, currentLatLng);

                } else {
                    LogMe.d(tag, "next_page_url:: " + " is null");
                }


            }
        }
    });

private void callDataFromLocationAPi(String type, String next_page_url, LatLng latLng) {

    if (Connectivity.isConnected(activity)) {
        showProgressDialog();
        model.getNearestPlaces(type, next_page_url, latLng).
                observe(activity, new Observer<List<PlaceDetails>>() {
                    @Override
                    public void onChanged(@Nullable List<PlaceDetails> placeDetails) {

                        ll_loading_more.setVisibility(View.GONE);

                        LogMe.i(tag, "callDataFromLocationAPi: onChanged called !");

                        hideProgressDialog();
                        if (placeDetails != null) {
                            placeDetailsList = placeDetails;
                            placeListAdapter.setPlaceList(placeDetails);
                        }

                    }
                });
    } else {
        showAlertForInternet(activity);
    }

}

就地视图模型

    public class PlaceViewModel extends AndroidViewModel {

    //this is the data that we will fetch asynchronously
    private MutableLiveData<List<PlaceDetails>> placeList;
    private PlaceRepository placeRepository;
    private String tag = getClass().getName();


    public PlaceViewModel(Application application) {

        super(application);
        placeRepository = new PlaceRepository(application);
    }


    //we will call this method to get the data
    public MutableLiveData<List<PlaceDetails>> getNearestPlaces(String type,
                                                         String next_page_token,
                                                         LatLng latLng) {
        //if the list is null
        if (placeList == null) {
            placeList = new MutableLiveData<>();

            //we will load it asynchronously from server in this method
            //loadPlaces(type, next_page_token, latLng);

            placeList = placeRepository.getNearestPlacesFromAPI(type, next_page_token, latLng);
        }

        //finally we will return the list
        return placeList;
    }
}

在我的 PlaceRepository.java 看起来

    public class PlaceRepository {

    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            // Since we didn't alter the table, there's nothing else to do here.
        }
    };
    private PlaceDatabase placeDatabase;
    private CurrentLocation currentLocation = null;
    private String tag = getClass().getName();
    //this is the data that we will fetch asynchronously
    private MutableLiveData<List<PlaceDetails>> placeList;


    public PlaceRepository(Context context) {
        placeDatabase = PlaceDatabase.getDatabase(context);
        //addMigrations(MIGRATION_1_2)
        placeList =
                new MutableLiveData<>();
    }


    public MutableLiveData<List<PlaceDetails>> getNearestPlacesFromAPI(String type, final String next_page_token, LatLng latLng) {

        List<PlaceDetails> placeDetailsList = new ArrayList<>();

        try {


            ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
            Call<Example> call = apiService.getNearbyPlaces(type,
                    latLng.latitude + "," +
                            latLng.longitude, ApplicationData.PROXIMITY_RADIUS,
                    ApplicationData.PLACE_API_KEY, next_page_token);

            call.enqueue(new Callback<Example>() {
                @Override
                public void onResponse(Call<Example> call, Response<Example> response) {

                    try {

                        Example example = response.body();

                        ApplicationData.NEXT_PAGE_URL = example.getNextPageToken();
                        //     next_page_url = example.getNextPageToken();
                        LogMe.i(tag, "next_page_url:" + ApplicationData.NEXT_PAGE_URL);

                        if (example.getStatus().equals("OK")) {

                            LogMe.i("getNearbyPlaces::", " --- " + response.toString() +
                                    response.message() + response.body().toString());

                            // This loop will go through all the results and add marker on each location.
                            for (int i = 0; i < example.getResults().size(); i++) {
                                Double lat = example.getResults().get(i).getGeometry().getLocation().getLat();
                                Double lng = example.getResults().get(i).getGeometry().getLocation().getLng();

                                String placeName = example.getResults().get(i).getName();
                                String vicinity = example.getResults().get(i).getVicinity();
                                String icon = example.getResults().get(i).getIcon();
                                String place_id = example.getResults().get(i).getPlaceId();

                                PlaceDetails placeDetails = new PlaceDetails();

                                if (example.getResults().get(i).getRating() != null) {
                                    Double rating = example.getResults().get(i).getRating();
                                    placeDetails.setRating(rating);
                                }

                                //List<Photo> photoReference = example.getResults().
                                //       get(i).getPhotos();
                                placeDetails.setName(placeName);
                                placeDetails.setAddress(vicinity);
                                placeDetails.setLatitude(lat);
                                placeDetails.setLongitude(lng);
                                placeDetails.setIcon(icon);
                                placeDetails.setPlace_id(place_id);
                                //placeDetails.setPlace_type(place_type_title);

                                double value = ApplicationData.
                                        DISTANCE_OF_TWO_LOCATION_IN_KM(latLng.latitude, latLng.longitude, lat, lng);

                                //new DecimalFormat("##.##").format(value);

                                placeDetails.setDistance(new DecimalFormat("##.##").format(value));

                                String ph = "";
                                if (example.getResults().
                                        get(i).getPhotos() != null) {

                                    try {
                                        List<Photo> photos = example.getResults().
                                                get(i).getPhotos();

                                        //JSONArray array = new JSONArray(example.getResults().
                                        //get(i).getPhotos());
                                        //JSONObject jsonObj = new JSONObject(array.toString());

                                        //ph = jsonObj.getString("photo_reference");
                                        ph = photos.get(0).getPhotoReference();
                                        //LogMe.i(tag, "\n" + ph);

                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        //placeDetails.setPicture_reference(ph);
                                        //PLACE_DETAILS_LIST.add(placeDetails);

                                        //LogMe.i(tag, "@@@@ Exception Occureed @@@@");

                                        ph = "";
                                        //continue;
                                    }
                                }

                                placeDetails.setPicture_reference(ph);
                                placeDetailsList.add(placeDetails);
                                placeList.postValue(placeDetailsList);

                            }


                        } else {


                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }


                }

                @Override
                public void onFailure(Call<Example> call, Throwable t) {

                    Log.e("onFailure", t.toString());

                }
            });


        } catch (RuntimeException e) {
            //hideProgressDialog();
            Log.d("onResponse", "RuntimeException is an error");
            e.printStackTrace();

        } catch (Exception e) {

            Log.d("onResponse", "Exception is an error");
        }

        return placeList;

    }
}

由于问题简单,我精确编码。

标签: android-recyclerviewretrofit2infinite-scrollandroid-jetpackandroid-viewmodel

解决方案


尽管您已经使用android-jetpack过,但请看一下Paging 库。它专为使用RecyclerView.

根据您的源代码,我会说您需要PageKeyedDataSource,这里有一些示例,其中包括有关如何实现的信息PageKeyedDataSource- 在 Android 中实现分页库的 7 个步骤

如果谈论这种方法的缺点:

  • 您不再需要观察列表滚动(图书馆为您完成),您只需要通过以下方式指定页面大小:

    PagedList.Config myPagingConfig = new PagedList.Config.Builder()
            .setPageSize(50)
            .build(); 
    

    从文档:

    页面大小:每页中的项目数。

  • 你的代码会更清晰,你会摆脱你的RecyclerView.OnScrollListener

  • ViewModel 代码会更短,它只会提供PagedList

    @NonNull
    LiveData<PagedList<ReviewSection>> getReviewsLiveData() {
        return reviewsLiveData;
    }
    

推荐阅读