繁体   English   中英

ViewPager片段都引用相同的RecyclerView和/或适配器

[英]ViewPager fragments are all referencing the same RecyclerView and/or Adapter

我有一个片段,其中包含一个RecyclerView来显示给定日期的事件。 我正在使用ViewPager将片段分成多天; 星期六活动的片段和星期日活动的片段。

但是,似乎两个片段都引用相同的RecyclerView和/或适配器,因为这只是显示事件的最后一个选项卡(在本例中为Sunday)。

在我的特定情况下,星期六有两个事件,而星期日没有任何事件。 两个片段都有空的RecyclerViews。 为了确认我的理论是由最后一个选项卡引起的,我更改了日期。 这导致两个RecyclerViews都发生两个事件(星期六起)。

以下是各个片段的相关代码:

public class EventListFragment extends Fragment{
    private EventAdapter mEventAdapter;

    private static final String DATE_ARG = "eventDate";

    public static EventListFragment newInstance(LocalDate date){
        EventListFragment eventListFragment = new EventListFragment();
        Bundle args = new Bundle();
        args.putSerializable(DATE_ARG, date);
        eventListFragment.setArguments(args);
        return eventListFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_event_list, container, false);

        // Setup recyclerview
        RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        eventRecyclerView.setLayoutManager(layoutManager);

        // Get date
        LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG);

        // Set adapter
        mEventAdapter = new EventAdapter(getActivity(), getEvents(eventDate));
        eventRecyclerView.setAdapter(mEventAdapter);

        return view;
    }
}

getEvents()只是一个私有函数,用于返回给定日期的事件。 我已经使用调试器以及单元测试来验证它是否可以正常工作。 调试器显示它为每个Fragment提取了正确的列表,但是正如我所解释的,它们无法正确显示。

这是父片段的相关代码:

public class EventFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_event, container, false);

        // Get and set up viewpager
        final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager);
        EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getFragmentManager(), getEventDates());
        viewPager.setAdapter(eventFragmentAdapter);

        // Get and set up tablayout
        final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs);
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        tabLayout.post(new Runnable() {
            @Override
            public void run() {
                tabLayout.setupWithViewPager(viewPager);
            }
        });

        return view;
    }
}

与最后一个类似, getEventDates()只是提取事件发生的日期。 目前出于测试目的,由于我们尚未设置数据库,因此我正在对返回的日期列表进行硬编码。 我取消此方法是因为我希望该应用程序能够在2016年再次运行,而日期可能有所不同:

private List<LocalDate> getEventDates(){
    List<LocalDate> eventDates = new ArrayList<>();

    eventDates.add(new LocalDate(2015, 10, 17));
    eventDates.add(new LocalDate(2015, 10, 18));

    return eventDates;
}

相关代码的最后一部分是我正在为ViewPager使用的FragmentStatePagerAdapter:

public class EventFragmentAdapter extends FragmentStatePagerAdapter {
    private List<LocalDate> mEventDates;

    public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){
        super(fragmentManager);
        this.mEventDates = eventDates;
    }
    @Override
    public Fragment getItem(int i) {
        return EventListFragment.newInstance(mEventDates.get(i));
    }

    @Override
    public int getCount() {
        return mEventDates.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mEventDates.get(position).dayOfWeek().getAsText();
    }
}

为什么两个列表总是相同并且基于ViewPager的最后一个选项卡的想法? 我假设他们以某种方式引用了相同的RecyclerView或相同的RecyclerViewAdapter,但是我没有针对这些的任何静态字段,因此我不确定它是如何发生的。

一个漫长的寻找和一种反气候的解决方案(与大多数困难的bug一样)。 由于该错误不在上面的代码中,因此也有点不公平,我不得不寻找您的git项目来解决它。 该错误在EventAdapter

public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> {
    private static final List<Event> mEvents;
    private final Context mContext;

    public EventAdapter(Context context, List<Event> events){
        this.mContext = context;
        mEvents = events;
    }
    ...
}

mEvents是静态的!...因此它在mEvents所有实例之间mEvents 这可以完美地解释该错误,因为最后一次更新将为所有EventAdapters设置值。

看起来您mEvents静态,以便可以在ViewHolders访问它。 相反,您可以仅将单个事件存储在ViewHolder然后删除危险的static修饰符。 另一方面,为开源项目万岁!

我已经看过您的代码,并且完全同意Travor的观点-您使用的是静态成员,每次创建新的Adapter时该成员都会被替换(因此它仅获取最后的页面数据)。 为了使它正常工作,我对您的项目做了一些修改。 看一看,希望它会有用。

EventFragment:将getFragmentManager替换为getChildFragmentManager,因为您需要EventListFragment由EventFragment片段管理器而不是活动片段管理器处理

public class EventFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_event, container, false);

        // Get and set up viewpager
        final ViewPager viewPager = (ViewPager) view.findViewById(R.id.event_view_pager);
        EventFragmentAdapter eventFragmentAdapter = new EventFragmentAdapter(getChildFragmentManager(), getEventDates());
        viewPager.setAdapter(eventFragmentAdapter);

        // Get and set up tablayout
        final TabLayout tabLayout = (TabLayout) view.findViewById(R.id.event_tabs);
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        tabLayout.post(new Runnable() {
            @Override
            public void run() {
                tabLayout.setupWithViewPager(viewPager);
            }
        });

        return view;
    }

    /**
     * Retrieves the event dates for the hackathon so that the proper events can be displayed.
     * @return
     */
    private List<LocalDate> getEventDates(){
        List<LocalDate> eventDates = new ArrayList<>();

        eventDates.add(new LocalDate(2015, 10, 17));
        eventDates.add(new LocalDate(2015, 10, 18));

        return eventDates;
    }
}

EventListFragment-我修改了查询,因为sqlite查询不适用于我的语言环境(意大利语)

public class EventListFragment extends Fragment{

    private EventAdapter mEventAdapter;

    private static final String TAG = EventListFragment.class.getSimpleName();

    private static final String DATE_ARG = "eventDate";

    public static EventListFragment newInstance(LocalDate date){
        EventListFragment eventListFragment = new EventListFragment();
        Bundle args = new Bundle();
        args.putSerializable(DATE_ARG, date);
        eventListFragment.setArguments(args);
        return eventListFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_event_list, container, false);

        // Setup recyclerview
        RecyclerView eventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        eventRecyclerView.setLayoutManager(layoutManager);

        // Get date
        LocalDate eventDate = (LocalDate) getArguments().getSerializable(DATE_ARG);

        // Set adapter
        mEventAdapter = new EventAdapter(getEvents(eventDate));
        eventRecyclerView.setAdapter(mEventAdapter);

        Log.v(TAG, eventRecyclerView.toString());

        return view;
    }

    /**
     * Retrieves the events for the given date for the fragment.
     */
    private List<Event> getEvents(LocalDate date){
        List<Event> returnList = new ArrayList<>();

        String dateString = Utility.getDBDateString(date);
        List<String> dateList = new ArrayList<>();

        Cursor cursor = getActivity().getContentResolver().query(
                GHContract.EventEntry.CONTENT_URI,
                new String[]{ "*", "substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11)" },
                "substr(" + GHContract.EventEntry.COLUMN_TIME + ",0,11) = ? ",
                new String[]{dateString},
                GHContract.EventEntry.COLUMN_TIME
        );

        while(cursor.moveToNext()){
            returnList.add(new Event(cursor));
            dateList.add(cursor.getString(cursor.getColumnIndex(GHContract.EventEntry.COLUMN_TIME)));
        }

        cursor.close();

        return returnList;
    }
}

EventAdapter-删除了static关键字和对活动上下文的引用(您需要将上下文获得其他位置)

public class EventAdapter extends RecyclerView.Adapter<EventAdapter.ViewHolder> {
    private List<Event> mEvents;

    public EventAdapter(List<Event> events){
        mEvents = events;
    }

    /**
     * Inflates the view for Event items.
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_event, parent, false);
        return new ViewHolder(view);
    }

    /**
     * Binds the data for an event to its view.
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Event event = mEvents.get(position);
        holder.timeView.setText(Utility.getTimeString(event.getTime()));
        holder.titleView.setText(event.getTitle());
        holder.locationView.setText(event.getLocation());

        // If reminder time is not null, show check mark. If it is, show plus.
        if(event.getReminderTime() != null){
            holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_alarm_on));
        } else{
            holder.alarmView.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.ic_add_alarm));
        }
    }

    /**
     * Returns the size of the adapter.
     */
    @Override
    public int getItemCount() {
        return mEvents.size();
    }

    /**
     * Retains a reference to the view so `findViewById` calls are only made once for the adapter.
     */
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
        public final TextView timeView;
        public final TextView titleView;
        public final TextView locationView;
        public final ImageView alarmView;

        public ViewHolder(View view){
            super(view);
            timeView = (TextView) view.findViewById(R.id.event_time);
            titleView = (TextView) view.findViewById(R.id.event_title);
            locationView = (TextView) view.findViewById(R.id.event_location);
            alarmView = (ImageView) view.findViewById(R.id.event_add_reminder);
            alarmView.setOnClickListener(this);
        }

        /**
         * Handles the click a user makes on the alarm image view.
         */
        @Override
        public void onClick(View v) {
            OnEventReminderClickListener activity = (OnEventReminderClickListener) v.getContext();
            activity.onEventReminderClicked(mEvents.get(getPosition()));
        }
    }

    /**
     * Interface to call back to the activity when an alarm is clicked for an event item.
     */
    public interface OnEventReminderClickListener{
        void onEventReminderClicked(Event event);
    }
}

最后是app / build.gradle,因为您需要为所有支持库(回收站,存储卡等)获得相同的版本

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.adammcneilly.grizzhacks"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'joda-time:joda-time:2.7'
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:cardview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
}

你可以试试这个吗?

public class EventFragmentAdapter extends FragmentStatePagerAdapter {
    private List<LocalDate> mEventDates;
    private List<EventListFragment> mFragments;


    public EventFragmentAdapter(FragmentManager fragmentManager, List<LocalDate> eventDates){
        super(fragmentManager);
        this.mEventDates = eventDates;
        this.mFragments = new ArrayList<>;
        for (LocalDate date : this.mEventDates) {
            this.mFragments.add(EventListFragment.newInstance(date));
        }
    }

    @Override
    public Fragment getItem(int i) {
        return mFragments.get(i);
    }

    @Override
    public int getCount() {
        return mEventDates.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mEventDates.get(position).dayOfWeek().getAsText();
    }
}

然后,您检查mFragent的每个项目是否具有预期的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM