我有一个应用程序,我在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
的方式有问题吗?我错过了什么吗?
答案 0 :(得分:2)
问题是,您在onBindViewHolder
内调用此代码。这意味着,为RecyclerView
内的每个新显示的行执行此代码(并在滚动时对同一行执行多次!!)。
如何将此代码移至Activity
或Fragment
(无论持有该列表)?这样,代码将执行一次。然后将您在此处解析的数据包装到自定义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,并将一个对象传递给适配器构造函数,其中包含已计算并打包在此对象中的所有必需参数。列出以下步骤:
由于数据库访问是内存密集型的,因此使用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);
}
现在更新onPostExecute
完成执行后调用的doInBackground
方法内的recyclerView。我确信这不是最佳实践,但每次我想更新时都必须重新初始化适配器,因为我需要将DataClass对象传递给它。
protected void onPostExecute(DataClass dataObject) {
mRecyclerViewAdapter = new RecyclerViewAdapter(dataObject, .....);
mRecylerView.setAdapter(mRecyclerViewAdapter);
//Notify Data set or Item changed if required
}
不要忘记为recyclerViewAdapter编写自定义构造函数,以便将dataObject传递给它。
我这样解决了我的问题。滚动是平滑的,除了第一次创建片段。