RecyclerView无法平滑滚动

3
我有一个应用程序,其中有几个视图(不同类型的列表项)位于RecyclerView中。每个项目在从数据库获取数据后进行更新。在浏览RecyclerView中的项目时,我遇到了粘性滚动的问题。我确定这是由于数据库访问操作导致的,并尝试解决该问题。我参考了StackOverflow上的几个答案,并阅读了官方Android文章(使ListView滚动平滑)。如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 方法中更新UI。
我使用 AsyncTask 的方式有什么问题吗?我有遗漏吗?


1
onbindToViewHolder方法旨在快速获取对象,加载其内容并将此内容加载到视图中,而不是执行逻辑。AsyncTask或Thread应该在片段或活动中使用。只有结果应该传递给适配器,因此当绑定到视图持有者时,您的数据已准备就绪。 - Rafal
@Rafal,你的回答听起来很棒!你有任何示例代码片段可以演示一下吗?我也遇到了FirebaseRecyclerAdapter中滚动卡顿的问题。非常感谢您的帮助。谢谢! - Arjun
2个回答

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(或任何控制器、Presenter 或用于业务逻辑的其他内容)中执行所有逻辑。这就是它的目的所在。 Adapter 只是用于显示结果,而不是为您的应用程序创建业务逻辑。

0

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

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

     protected DataClass doInBackground(Params... params) {
     //在这里执行数据库或其他操作
     //假设DataClass构造函数需要传入3个参数,稍后用于更新recycler view 
        return new DataClass(data1, data2, data3);
     }
    
  3. 现在,在doInBackground执行完毕后调用的onPostExecute方法中更新你的recyclerView。我相信这不是最佳实践,但我不得不每次想要更新时重新初始化适配器,因为我需要将DataClass对象传递给它。

    protected void onPostExecute(DataClass dataObject) {
     mRecyclerViewAdapter = new RecyclerViewAdapter(dataObject, .....);
     mRecylerView.setAdapter(mRecyclerViewAdapter);
     //如果需要,通知数据集或项目已更改
    }
    
  4. 不要忘记为你的recyclerViewAdapter编写自定义构造函数,以便将dataObject传递给它。

我是这样解决我的问题的。滚动除了在创建片段的第一次之外都是平滑的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接