RecyclerView无法顺畅滚动

时间:2016-09-08 07:15:57

标签: android listview android-asynctask android-recyclerview

我有一个应用程序,我在RecyclerView中有几个视图(不同类型的列表项)。从数据库中获取数据后,每个项目都会更新。在滚动我的RecyclerView中的项目时,我遇到了粘性滚动的问题。我确信这是由于数据库访问操作而发生的,我试图解决这个问题。我在StackOverflow上提到了几个答案,并通过官方的android帖子(Making ListView Scrolling Smooth)阅读。正如在android帖子中所建议的,我使用AsyncTask将所有数据库访问操作移动到后台线程,但仍然没有找到运气。 以下是我在onBindViewHolder内写的代码,用于更新其中一项。

new AsyncTask<RecyclerView.ViewHolder, Void, TaskPhysicalActivity>(){
                private RecyclerView.ViewHolder v;
                PhysicalActivityItemHolder pAItemHolder;
                DBHelper mDbHelper;
                User mUser;
                @Override
                protected TaskPhysicalActivity doInBackground(RecyclerView.ViewHolder... params) {
                    pAItemHolder = (PhysicalActivityItemHolder)params[0];
                    Calendar cal = Calendar.getInstance();
                    Calendar today = Calendar.getInstance();
                    Calendar earlier = Calendar.getInstance();
                    earlier.add(Calendar.DATE, -6);
                    mDbHelper = DBHelper.getInstance(mContext);
                    mUser = User.getDefaultUser(mContext);
                    try {
                        List<PhysicalActivity> physicalActivities = mDbHelper.getPhysicalActivityInRange(mUser, cal, cal);
                        PhysicalActivityReport physicalActivityReport = new PhysicalActivityReport(earlier.getTime(), today.getTime());
                        physicalActivityReport.makeReport(mContext, mUser);
                        int userStepGoal = PhysicalActivity.getDailyStepsGoal(mContext);
                        return new TaskPhysicalActivity(userStepGoal, physicalActivityReport.getPhysicalActivityList(), physicalActivities,physicalActivityReport);
                    } catch (ParseException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    return null;
                }


                @Override
                protected void onPostExecute(TaskPhysicalActivity taskPhysicalActivity) {
                    int stepsCount = 0;
                    Calendar cal = Calendar.getInstance();
                    TaskPhysicalActivity taskPhysicalActivityObject = taskPhysicalActivity;
                    List<PhysicalActivity> physicalActivities = taskPhysicalActivityObject.getPhysicalActivities();
                    List<PhysicalActivity> activityList = taskPhysicalActivityObject.getActivityList();
                    PhysicalActivityReport physicalActivityReport = taskPhysicalActivityObject.getPhysicalActivityReport();

                    pAItemHolder.tvActivityEmptyText.setVisibility(View.GONE);

                    if (physicalActivities.size() > 0) {
                        for (int i = 0; i < physicalActivities.size(); i++) {
                            stepsCount = stepsCount
                                    + physicalActivities.get(i).getValue();
                        }

                        final int finalStepsCount = stepsCount > 0 ? stepsCount : 0;
                        pAItemHolder.tvActivityValue.setText(String.valueOf(finalStepsCount));
                        pAItemHolder.tvActivityValueText.setText(mContext.getResources().getString(R.string.steps_unit));
                        pAItemHolder.tvActivityEmptyText.setText("");
                        pAItemHolder.vActivityCardEmptyView.setVisibility(View.GONE);
                        pAItemHolder.vActivityCardView.setVisibility(View.VISIBLE);

                    } else {

                        pAItemHolder.tvActivityValue.setText("--");
                        pAItemHolder.tvActivityValueText.setText(mContext.getResources().getString(R.string.steps_unit));
                        pAItemHolder.tvActivityEmptyText.setText("");
                        pAItemHolder.vActivityCardEmptyView.setVisibility(View.VISIBLE);
                        pAItemHolder.vActivityCardView.setVisibility(View.GONE);

                    }

                    pAItemHolder.tvActivityGoal.setText(String.format(mContext.getResources().getString(R.string.activity_goal_display_string),
                            taskPhysicalActivityObject.getUserStepsGoal(), mContext.getResources().getString(R.string.steps_unit), mContext.getResources().getString(R.string.per_day)));
                    pAItemHolder.vActivityGraph.setActivityValues(activityList);

                    if (physicalActivityReport.getPhysicalActivityAverage() > 0) {
                        pAItemHolder.tvPercentInRange.setText("Average steps " + physicalActivityReport.getPhysicalActivityAverage());
                    }
                    if (activityList.size() == 0) {
                        pAItemHolder.vActivityCardEmptyView.setVisibility(View.VISIBLE);
                        pAItemHolder.vActivityCardView.setVisibility(View.GONE);
                    } else {
                        pAItemHolder.vActivityCardEmptyView.setVisibility(View.GONE);
                        pAItemHolder.vActivityCardView.setVisibility(View.VISIBLE);
                    }
                }
            }.execute(holder);

在上面的代码中,doInBackground内的以下四行执行数据库访问:

List<PhysicalActivity> physicalActivities = mDbHelper.getPhysicalActivityInRange(mUser, cal, cal);
PhysicalActivityReport physicalActivityReport = new PhysicalActivityReport(earlier.getTime(), today.getTime());
physicalActivityReport.makeReport(mContext, mUser);
int userStepGoal = PhysicalActivity.getDailyStepsGoal(mContext);

我使用这些操作的结果来更新onPostExecute方法中的用户界面 我使用AsyncTask的方式有问题吗?我错过了什么吗?

2 个答案:

答案 0 :(得分:2)

问题是,您在onBindViewHolder内调用此代码。这意味着,为RecyclerView内的每个新显示的行执行此代码(并在滚动时对同一行执行多次!!)。

如何将此代码移至ActivityFragment(无论持有该列表)?这样,代码将执行一次。然后将您在此处解析的数据包装到自定义POJO对象中:

public class POJO {
    String activityValue;
    String activityValueText;
    String activityEmptyText;
    boolean activityCardEmptyViewVisibility;
    boolean activityCardViewVisibility;
}

将此类作为传递给RecyclerView的对象。然后,在onBindViewHolder内使用getItem(position),然后将数据从POJO传递到Holder

执行Activity内的所有逻辑(或任何控制器,pressenter,用于业务逻辑的任何内容)。这就是它注定的目标。 Adapter仅用于显示结果,而不用于为应用程序创建业务逻辑。

答案 1 :(得分:0)

感谢@Rafal和@ r-zagórski提出的宝贵建议。我尝试了保持数据准备并将其传递给onBindViewHolder的方法,但由于某种原因没有帮助。 然后我将onBindViewHolder中的所有代码移动到onCreateViewHolder,并将一个对象传递给适配器构造函数,其中包含已计算并打包在此对象中的所有必需参数。列出以下步骤:

  1. 创建一个类(比如DataClass)来保存所有必需的值,并在更新期间稍后在recyclerView适配器中使用它们的getter。
  2. 由于数据库访问是内存密集型的,因此使用AsyncTask在doInBackground方法中执行所有数据库访问操作,并使其返回DataClass的对象。

     protected DataClass doInBackground(Params... params) {
     //perform database or other operations here
     // Let's say DataClass constructor takes in 3 parameters which are later used to update recycler view 
        return new DataClass(data1, data2, data3);
     }
    
  3. 现在更新onPostExecute完成执行后调用的doInBackground方法内的recyclerView。我确信这不是最佳实践,但每次我想更新时都必须重新初始化适配器,因为我需要将DataClass对象传递给它。

    protected void onPostExecute(DataClass dataObject) {
     mRecyclerViewAdapter = new RecyclerViewAdapter(dataObject, .....);
     mRecylerView.setAdapter(mRecyclerViewAdapter);
     //Notify Data set or Item changed if required
    }
    
  4. 不要忘记为recyclerViewAdapter编写自定义构造函数,以便将dataObject传递给它。

  5. 我这样解决了我的问题。滚动是平滑的,除了第一次创建片段。