I have a RecyclerView
in a fragment which is repeating in TabLayout. I am having the problem of unchanged view in RecyclerView. I have a spinner on each tab. I want to change the data when spinner items get selected.
My cases:
- when switching between tabs - items changed
when selecting another value in the spinner in the first tab -items not changed. (but data is changing in adapter class.Ie. First, it is not null then null during selection. But the first not nulled data is not appearing, its replacing with null. Found it using breakpoints).
Note: In this case, when switching the tab, the items get changed to the spinner selected items in the previous tab. And then it disappears and displaying the current items in the tab.
when selecting another value in the spinner in the last tab -items changed.
My view pager adapter class
public class StudentViewPagerAdapter extends FragmentStatePagerAdapter { private final List<StudentList> mFragmentList = new ArrayList<>(); private final List<Clazz> mFragmentTitleList = new ArrayList<>(); public StudentViewPagerAdapter(FragmentManager fm) { super(fm); } public void addFragment(StudentList fragment,Clazz clazz){ mFragmentList.add(fragment); mFragmentTitleList.add(clazz); } @Override public Fragment getItem(int position) { return StudentList.newInstance(mFragmentTitleList.get(position)); } @Override public int getCount() { return mFragmentList.size(); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position).getName(); } }
my RecyclerView
adapter class
public class PeopleAdapter extends RecyclerView.Adapter<PeopleAdapter.MyViewHolder> implements View.OnClickListener { private List<Student> dataList; private Context context; private Clicker clicker; public PeopleAdapter(List<Student> data, Context context, Clicker clicker) { this.dataList = data; this.context = context; this.clicker = clicker; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.people_list_item, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { Student data=dataList.get(position); holder.email.setText(data.getEmail()); holder.name.setText(data.getName()); holder.phone.setText(data.getPhone()); Glide.with(context).load(Method.getImageUrl(MyConfiguration.STUDENT_IMAGE_URL, data.getStudentId())).asBitmap().into(holder.profilePic); holder.edit.setOnClickListener(this); holder.edit.setTag(position); } @Override public int getItemCount() { return dataList.size(); } @Override public void onClick(View v) { clicker.OnItemClicked((int) v.getTag(),null); } static class MyViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.name) TextView name; @BindView(R.id.email) TextView email; @BindView(R.id.phone) TextView phone; @BindView(R.id.image) ImageView profilePic; @BindView(R.id.imageedit) ImageView edit; MyViewHolder(View view) { super(view); ButterKnife.bind(this,view); } } }
tab fragments
public class StudentList extends Fragment implements SectionChanger { @BindView(R.id.studentlist) RecyclerView mRecyclerview; CompositeDisposable disposable; private Unbinder unbinder; private Clazz clazz; private Requester requester; public StudentList() { StudentInformation.bindSectionChangeListener(this); } public static StudentList newInstance(Clazz clazz) { StudentList fragment=new StudentList(); Bundle args = new Bundle(); args.putSerializable(MyConfiguration.SECTIONS, clazz); fragment.setArguments(args); return fragment; } @Override public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); disposable.clear(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_student_list, container, false); unbinder = ButterKnife.bind(this, view); LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); mRecyclerview.setLayoutManager(mLayoutManager); initializeRetrofit(); if (getArguments() != null) { clazz = (Clazz) getArguments().getSerializable(MyConfiguration.SECTIONS); loadStudentJson(clazz != null ? clazz.getClassId() : null, clazz != null ? clazz.getSections().get(0).getSectionId() : null); } return view; } /** * Load students list */ public void loadStudentJson(String class_id,String section_id) { disposable = new CompositeDisposable(requester.getStudentsInSection(class_id,section_id) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( this::handleResponse, this::handleError ) ); } private void handleResponse(List<Student> list) { PeopleAdapter adapter=new PeopleAdapter(list, getActivity(), (position, name) -> Toast.makeText(getActivity(), position, Toast.LENGTH_LONG).show()); mRecyclerview.setAdapter(adapter); adapter.notifyDataSetChanged(); } private void handleError(Throwable error) { Toast.makeText(getActivity(), "Error " + error.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); } @Override public void ChangeData(Section section) { loadStudentJson(section.getClassId(),section.getSectionId()); } public void initializeRetrofit(){ HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); CookieHandler handler=new Cookies(getActivity()); //ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getActivity())); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(interceptor) .cookieJar(new JavaNetCookieJar(handler)) .build(); requester = new Retrofit.Builder() .baseUrl(MyConfiguration.BASE_URL) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .client(client) .build().create(Requester.class); } }
Viewpager setup fragment
public class StudentInformation extends Fragment implements TabLayout.OnTabSelectedListener, AdapterView.OnItemSelectedListener { // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private Unbinder unbinder; // TODO: Rename and change types of parameters private String mParam1; private String mParam2; CompositeDisposable disposable; List<Section> sectionsList=new ArrayList<>(); private OnConnectingFragments mListener; @BindView(R.id.tabs) TabLayout mTabLayout; @BindView(R.id.viewpager) ViewPager viewPager; @BindView(R.id.secSelector) Spinner spinner; @BindView(R.id.className) TextView className; private static SectionChanger sectionChanger; public StudentInformation() { // Required empty public constructor } /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 Parameter 1. * @param param2 Parameter 2. * @return A new instance of fragment StudentInformation. */ // TODO: Rename and change types and number of parameters public static StudentInformation newInstance(String param1, String param2) { StudentInformation fragment = new StudentInformation(); Bundle args = new Bundle(); args.putString(ARG_PARAM1, param1); args.putString(ARG_PARAM2, param2); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } @Override public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); disposable.clear(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.fragment_student_information, container, false); unbinder = ButterKnife.bind(this, view); LoadDataAndSetupViewPager(); //setupViewPager(viewPager); mTabLayout.setupWithViewPager(viewPager); mTabLayout.addOnTabSelectedListener(this); spinner.setOnItemSelectedListener(this); // ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, MyConfiguration.CLASS_SECTIONS); // spinner.setAdapter(arrayAdapter); return view; } // TODO: Rename method, update argument and hook method into UI event public void onButtonPressed(Fragment fragment,String tag) { if (mListener != null) { mListener.onClickedMenu(fragment,tag); } } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnConnectingFragments) { mListener = (OnConnectingFragments) context; } else { throw new RuntimeException(context.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onDetach() { super.onDetach(); mListener = null; } /** * This interface must be implemented by activities that contain this * fragment to allow an interaction in this fragment to be communicated * to the activity and potentially other fragments contained in that * activity. * <p> * See the Android Training lesson <a href= * "http://developer.android.com/training/basics/fragments/communicating.html" * >Communicating with Other Fragments</a> for more information. */ /*private void setupViewPager(ViewPager viewPager) { StudentViewPagerAdapter adapter = new StudentViewPagerAdapter(getFragmentManager()); //adapter.addFragment(new StudentList(),"exam"); for (String claz: MyConfiguration.CLASS) adapter.addFragment(new StudentList(), claz); viewPager.setAdapter(adapter); }*/ @OnClick(R.id.add) public void OnClicked(LinearLayout view){ onButtonPressed(new AddStudent(),"addStudent"); } @Override public void onTabSelected(TabLayout.Tab tab) { className.setText(String.format(getString(R.string.class_name), tab.getPosition()+1)); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } public void LoadDataAndSetupViewPager() { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getActivity())); OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); Requester requester=new Retrofit.Builder() .baseUrl(MyConfiguration.BASE_URL) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .client(client) .build().create(Requester.class); disposable=new CompositeDisposable(requester.getClasses() ////GETTING CLASSES//// .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .flatMapIterable(clazzs -> clazzs) .flatMap(clazz -> requester.getDivision(clazz.getClassId()) ////GETTING SECTIONS//// .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .flatMapIterable(sections -> sections) .doOnNext(section -> {sectionsList.add(section); Log.v("section_id",section.getSectionId());}) .takeLast(1) .map(section -> clazz) ) .doOnNext(clazz -> {clazz.setSections(sectionsList); Log.v("List Size",sectionsList.size()+""); sectionsList=new ArrayList<>(); }) .toList() .subscribe(this::SetupViewPager, throwable -> Log.e("retroerror",throwable.toString()))); } public void SetupViewPager(List<Clazz> classList){ StudentViewPagerAdapter adapter = new StudentViewPagerAdapter(getFragmentManager()); //adapter.addFragment(new StudentList(),"exam"); for (Clazz claz: classList){ adapter.addFragment(new StudentList(), claz); } viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(3); viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { super.onPageSelected(position); List<Section>sections=classList.get(position).getSections(); ArrayAdapter<Section> arrayAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, sections); spinner.setAdapter(arrayAdapter); } }); } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { Section section= (Section) parent.getItemAtPosition(position); sectionChanger.ChangeData(section); } @Override public void onNothingSelected(AdapterView<?> parent) { } public static void bindSectionChangeListener(SectionChanger changer){ sectionChanger=changer; } }
The question is: Why the data get unchanged when selecting options in the spinner sometimes? (look my cases)
6 Answers
Answers 1
try using getChildFragmentManager() instead of getFragmentManager() will solve your issue
Answers 2
I think you should implement onNothingSelected() as below
Section mCurrentSection; @Override public void onNothingSelected(AdapterView<?> parent) { int position = parent.getSelectedItemPosition(); Section section= (Section) parent.getItemAtPosition(position); if (mCurrentSection == null || !mCurrentSection.equals(section)) { mCurrentSection = section; sectionChanger.ChangeData(section); } }
Because, onNothingSelected is called instead of onItemSelected when you re-select an item of spinner.
Answers 3
The problem is mainly here:
public StudentList() { StudentInformation.bindSectionChangeListener(this); }
This doesn't assure that the fragment visible to the user is the last bound. You set viewPager.setOffscreenPageLimit(3);
meaning that three fragments per time can be instantiated, so when you are on a fragment also the right and the left one are created.
This explain why the last one works well, because there isn't a right fragment, and probably is the last one to be instantiated and bound.
Personally I would change the implementation handling the spinner inside the page fragment, since it acts only on the page fragment.
Solution with a setUserVisibleHint
Bind the page fragment to the main one when the fragment become visble to the user. Pay attention to memory leak and release the static reference
@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if(isVisibleToUser){ StudentInformation.bindSectionChangeListener(this); } }
Solution with a bus
Another quick solution could be to use a bus like this: http://square.github.io/otto/ In this way every fragment will subscribe to an event SelectedItemChanged
and refresh them-self. The main fragment will post the updates every time the spinner selection is changed.
However the example is pretty big, so I'm not super sure that there aren't other problems. Try to share a complete project to receive more specific help.
Answers 4
When you work with recycleview rely only on your model class filed values. Do all the changes to model class and create row views based on this model class fields.
i.e, you have to create or change values or view state based on the value of the model class fields.
For ex: if you want to make a row (position 30) invisible, then i will set a flag in model class called visibility and set it to false.
in this case whenever view redraws in UI, we will have to check for the flag and set the visibility based on that model class field value.
Like this you can create flags, content values, serialized params from API etc in model class. This method will make your reclycleview more consistent through out user interactions and API changes.
Answers 5
Check your StudentViewPagerAdapter
@Override public Fragment getItem(int position) { return mFragmentList.get(position).newInstance(mFragmentTitleList.get(position)); }
why do you call newInstance
again?
Replace it with the following, return mFragmentList.get(position);
Answers 6
Firstly, check that you add this line after
setadapter : adapter.notifyDataSetChanged();
Another way is make final the MyViewHolder
holder and int position
example.
public void onBindViewHolder(final MyViewHolder holder, final int position) {
0 comments:
Post a Comment