我正在开发一个简单的Android应用程序(待办事项列表),并且我一直试图使用ViewPager
来实现FragmentStatePagerAdapter
来导航任务。为简单起见,我在当天,前一天和后一天添加了一些任务(不处理没有任务的日子,只是为了保持简单)。
我无法在滚动屏幕时加载正确的日期。当我第一次向左滑动到前一天时,我得到了正确的结果 - 显示前一天。但是后来我不能向左滑动以获得更多的日子(我认为我的代码将设置前一个"左边"日期为当前"中间"日期并让我滑动再次离开)。在尝试滚动到最右边的日期然后再向左滚动之后,我最终得到NullPointerException
,但我无法弄清楚如何更改代码以正确刷新日期。滑动序列(左右 - 右 - 左)后的堆栈跟踪:
04-05 22:38:24.966 12441-12441/ptmprojects.com.quicktickcalendar2 E/AndroidRuntime: FATAL EXCEPTION: main
Process: ptmprojects.com.quicktickcalendar2, PID: 12441
java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.FragmentTransaction android.support.v4.app.FragmentTransaction.add(int, android.support.v4.app.Fragment)' on a null object reference
at android.support.v4.app.FragmentStatePagerAdapter.instantiateItem(FragmentStatePagerAdapter.java:123)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1004)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1186)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1086)
at android.support.v4.view.ViewPager$3.run(ViewPager.java:267)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:933)
at android.view.Choreographer.doCallbacks(Choreographer.java:742)
at android.view.Choreographer.doFrame(Choreographer.java:671)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:919)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6605)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:999)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:889)
我创建了一个TasksBank
,其中所有任务都存储在List<SingleTask> mAllTasksList
中,以便当屏幕上显示正确的日期时,从该银行获取任务以将其加载到ViewPager
。正如我在Activity
点击Button
时在其他ActionBar
中看到的那样,每天都会正确加载任务。这是一个屏幕截图,用于确认任务是从TasksBank
正确加载的不同日期:
任务由班级SimpleTask
表示,每个mDate
都有一个字段SimpleTask
,用于存储分配给每项任务的日期(我知道Map<LocalDate, SimpleTask>
可能会更好选择,但为了简单起见,我选择从ArrayList<SimpleTask>
开始 - 如果这是一个非常糟糕的主意,请纠正我。)
为了让您更好地了解它的外观,下面是SingleDayFragment
的外观截图(它是SingleTask
中存储的RecyclerView
列表:
我想解决这个问题的方法是在List<LocalDate> mListOfDates
中创建一个DailyTaskPagerActivity
来存储3个日期 - 任务的日期显示在左侧滑动,日期显示在中间屏幕上,日期显示在显示在右侧滑动。滚动到另一天之后,我应该将前一个左或右片段设置为当前中间片段,并在需要时将另一天添加到天数列表中。我还创建了字段:currentPosition
和currentDate
来存储当前显示的屏幕的详细信息。
我包含我认为与我面临的问题相关的.java
个文件,但我认为第一个 - DailyTaskPagerActivity.java
是我遇到问题的文件。如果它在这种情况下太多或不够,请告诉我 - 我通过适当的编辑包含所需的一切或删除不应该存在的内容。
DailyTaskPagerActivity.java
(负责在几天之间滚动的主要班级 - 我很确定这里有问题)。我对在评论中包含每个代码元素的目的做了一些解释:
public class DailyTaskPagerActivity extends AppCompatActivity {
private ViewPager mViewPager;
private List<LocalDate> mListsOfDates;
private TasksDaysStatePagerAdapter mAdapter;
int currentPosition;
LocalDate currentDate;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_daily_task_pager);
mViewPager = (ViewPager) findViewById(R.id.daily_task_view_pager);
mListsOfDates = new ArrayList<>();
mListsOfDates.add(new LocalDate().minusDays(1));
mListsOfDates.add(new LocalDate());
mListsOfDates.add(new LocalDate().plusDays(1));
currentPosition = 1; // current position on middle screen / today's date
currentDate = new LocalDate();
mAdapter = new TasksDaysStatePagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(1); // to start mViewPager from the middle date (to allow swipe to the left and
// to the right (not only to right if it were 0
}
private class TasksDaysStatePagerAdapter extends FragmentStatePagerAdapter {
public TasksDaysStatePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position > currentPosition) { // if position after swipe is higher than currentPosition(equal to 1)
// it means that the screen has been swiped to the right
currentPosition = position; // update current position field
currentDate = currentDate.plusDays(1); // update currentDate field
mListsOfDates.set(2, currentDate); // set updated date to mListsOfDates last element
notifyDataSetChanged(); // notify that change to mListsOfDates occured
return SingleDayFragment.newInstance(currentDate); // create new Fragment with updated date
} else if (position < currentPosition) {// if position after swipe is lower than currentPosition(equal to 1)
// it means that the screen has been swiped to the left
currentPosition = position;
currentDate = currentDate.minusDays(1);
mListsOfDates.set(0, currentDate);
notifyDataSetChanged();
return SingleDayFragment.newInstance(currentDate);
} else { // I'm not sure if I should include anything here ??
// currentPosition = position;
// mViewPager.setAdapter(new TasksDaysStatePagerAdapter(getSupportFragmentManager()));
return SingleDayFragment.newInstance(currentDate);
}
}
@Override
public int getCount() {
return mListsOfDates.size();
}
}
}
SingleDayFragment.java
(片段为一天,包含TextView
日期和RecyclerView
,其中包含单日任务)
public class SingleDayFragment extends Fragment {
private static final String ARG_DATE = "ptmprojects.com.quicktickcalendar.argDate";
private RecyclerView mTaskRecyclerView;
private TaskAdapter mAdapter;
private TextView mDateAboveRecyclerView;
DateTimeFormatter mDateTimeFormatter = DateTimeFormat.forPattern("dd MMMM yyyy");
private LocalDate date;
public static SingleDayFragment newInstance(LocalDate date) {
Bundle args = new Bundle();
args.putSerializable(ARG_DATE, date);
SingleDayFragment fragment = new SingleDayFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
date = getArguments().getSerializable(ARG_DATE) != null ? (LocalDate) getArguments().getSerializable(ARG_DATE) : new LocalDate();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_single_day, container, false);
mTaskRecyclerView = (RecyclerView) view.findViewById(R.id.single_day_recycler_view);
mTaskRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mDateAboveRecyclerView = view.findViewById(R.id.date_above_recycler_view);
mDateAboveRecyclerView.setText(getString(R.string.date_above_recycler_view, date.toString(mDateTimeFormatter)));
updateUI();
return view;
}
private void updateUI() { // updating RecyclerView containing tasks for one day
TasksBank tasksBank = TasksBank.get(getActivity());
List<SingleTask> tasksForSingleDay = tasksBank.getTasksForDate(date);
if (mAdapter == null) {
mAdapter = new TaskAdapter(tasksForSingleDay);
mTaskRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.setTasks(tasksForSingleDay);
mAdapter.notifyDataSetChanged();
}
}
// private class TaskAdapter...
// private class TaskHolder...
}
AndroidManifest.xml
(删除了一些我认为不需要的行):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ptmprojects.com.quicktickcalendar">
<application
android:allowBackup="true"
<activity android:name=".DailyTaskPagerActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SingleDayActivity">
</activity>
</application>
</manifest>
TasksBank.java
(提供每天任务列表的课程):
public class TasksBank {
private Context mContext;
private static TasksBank sTasksBank;
private List<SingleTask> mAllTasksList;
public static TasksBank get(Context context) {
if (sTasksBank == null) {
sTasksBank = new TasksBank(context);
}
return sTasksBank;
}
private TasksBank(Context context) {
mAllTasksList = new ArrayList<>();
// Just a sample, to check if code is working
mContext = context.getApplicationContext();
mAllTasksList.add(new SingleTask(new LocalDate().minusDays(2), "Title2DaysBefore", "Description2DaysBefore", false));
mAllTasksList.add(new SingleTask(new LocalDate().minusDays(1), "Title1DayBefore", "Description1DayBefore", false));
mAllTasksList.add(new SingleTask(new LocalDate(), "Title2", "Description2", false));
mAllTasksList.add(new SingleTask(new LocalDate().plusDays(1), "Title1DayAfter", "Description1DayAfter", false));
mAllTasksList.add(new SingleTask(new LocalDate().plusDays(2), "Title2DaysAfter", "Description2DaysAfter", false));
mAllTasksList.add(new SingleTask(new LocalDate(2018, 03, 31), "Title3", "Description3", false));
}
public ArrayList<SingleTask> getTasksForDate(LocalDate date) {
ArrayList<SingleTask> tasksForDate = new ArrayList<>();
for(SingleTask task : getAllTasksList()) {
if (task.getDate().equals(date)) {
tasksForDate.add(task);
}
}
return tasksForDate;
}
public List<SingleTask> getAllTasksList() {
return mAllTasksList;
}
public void addTask(SingleTask task) {
mAllTasksList.add(task);
}
}
SingleTask.java
(代表任务的类,有更多字段,getter和setter,但我不会包含它,因为我认为它无关紧要):
public class SingleTask {
private String mTitle;
private LocalDate mDate;
public SingleTask(LocalDate date, String title) {
mDate = date;
mTitle = title;
}
public LocalDate getDate() {
return mDate;
}
public void setDate(LocalDate date) {
mDate = date;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
}
只是为了让你知道我认为我进行了大量的研究,我在Stack Overflow中包含了一些我在这里找到的问题的链接,除了看看Stack Overflow之外:
“infinite” scrolling viewpager with days incrementing/decrementing
在许多教程中,我已经读过,只需要实现ViewPager
和getCount()
方法,getItem()
正常工作是微不足道的。我怎样才能让它发挥作用,或者任何人都可以理解为什么它现在不起作用?