Getting current fragment variables values - android

I am using FragmentViewPagerAdapter to create a calendar Activity. Each fragment represents a day, and I have a Switch on my main activity that allows the user to filter results on each fragment.
I am using ViewModel to request data, so when the switch is checked I need to do a new request to my server to get new data for each fragment, My problem is that when I do my request, the day sent is next fragment's date, not the current's one.
I already tried to limit of screen pages to 0 but nothing changed.
public class PoiPlanningFragment extends Fragment implements InitApplicationListener{
MutableLiveData<Boolean> filtered = new MutableLiveData<>();
private Context mContext;
DateTime startTime;
DateTime endTime;
switcher = getActivity().findViewById(R.id.app_switcher);
public PoiPlanningFragment() {
// Required empty public constructor
}
public static Fragment getInstance(int position, String date, String resourceId, String resourceType,boolean filter) {
Bundle bundle = new Bundle();
bundle.putInt("pos", position );
bundle.putString("date", date);
bundle.putString(ARG_RESOURCE_ID, resourceId);
bundle.putString(ARG_RESOURCE_TYPE, resourceType);
PoiPlanningFragment tabFragment = new PoiPlanningFragment();
tabFragment.setArguments(bundle);
return tabFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
DateTimeFormatter horaire = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
startTime = horaire.parseDateTime(date.concat(" " + STARTHOUR));
endTime = horaire.parseDateTime(date.concat(" " + ENDHOUR));
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_poi_planning, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
//Switch my meetings
switcher.setOnCheckedChangeListener(new SwitchClickedListener(this.filtered));
//Observes whether the user has a filter on own meetings or not
filtered.observe(this, filtered -> {
//here startTime always shows next tab startTime and endTime
BookingRepository.getUserPlanning(planning,resourceId,Utility.toTimeStamp(startTime),Utility.toTimeStamp(endTime), filtered);
loader.setVisibility(View.VISIBLE);
});
}
}
private class SwitchClickedListener implements CompoundButton.OnCheckedChangeListener{
MutableLiveData<Boolean> listener;
SwitchClickedListener(MutableLiveData<Boolean> listener){
this.listener = listener;
}
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
//Set filter to true
this.listener.setValue(b);
}
}
public class PoiPlanningViewPagerAdapter extends FragmentStatePagerAdapter {
private final static String TAG = PoiPlanningViewPagerAdapter.class.getName();
private List<Date> dateList;
private Context context;
private String poiId;
private String resouceType;
public PoiPlanningViewPagerAdapter(FragmentManager fm, List<Date> dates, String poidId,String resourceType, Context context) {
super(fm);
this.dateList = dates;
this.context = context;
this.poiId = poidId;
this.resouceType = resourceType;
}
#Override
public Fragment getItem(int position) {
Date datetime = dateList.get(position);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(datetime);
PoiPlanningFragment planningFragment = (PoiPlanningFragment) PoiPlanningFragment.getInstance(position, date, poiId,resouceType,false);
return planningFragment;
}
#Override
public int getCount() {
return dateList.size();
}
#Override
public CharSequence getPageTitle(int position) {
Date datetime = dateList.get(position);
DateFormat df = new SimpleDateFormat(context.getString(R.string.date_format));
String date = df.format(datetime);
return date;
}
}
In the previous code, if I check the switch and I am on Monday 02 11th It will load data of Tuesday.

You can use supportFragmentManager for getting current fragment.
below code will help you.
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
val frag = getSupportFragmentManager().findFragmentById(R.id.container);
if (frag is HomeFragment) {
Fragment currentFragment = (HomeFragment) frag;
//do your code
return
}
}
I hope this will help you.

Related

Recylerview blank when using ViewModel in fragment

Here, the flow first in a screen there are three buttons which are also created using recycler view. View Model is passing the data to fill the button content ie image and text. This is working fine. On click of button a new screen/fragment loads which should show list. Using below method to fetch API data:
public class TrackRepository {
MutableLiveData<TrackData> trackData;
RestClient restClient;
SharedPreferences sharedPreferences;
public TrackRepository() {
trackData = new MutableLiveData<>();
this.restClient = new RestClient();
sharedPreferences = SessionManager.getPreferences();
}
public void getTrackList(String startDate,String endDate,String searchTest){
int centerId = ClientInfo.getCenterId(sharedPreferences);
String auth = ClientInfo.getAuthToken(sharedPreferences);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("CenterId",centerId);
jsonObject.addProperty("SearchText",searchTest);
jsonObject.addProperty("StartDate",startDate);
jsonObject.addProperty("EndDate",endDate);
restClient.getEndpoints().getTrackReports(auth,jsonObject).enqueue(new Callback<TrackData>() {
#Override
public void onResponse(Call<TrackData> call, Response<TrackData> response) {
if (response.isSuccessful()){
boolean success = response.body().isSuccess();
String message = response.body().getMessage();
try {
if (success) {
trackData.postValue(response.body());
}else {
trackData.postValue(response.body());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<TrackData> call, Throwable t) {
trackData.postValue(null);
}
});
}
public LiveData<TrackData> getTrackDataMutableLiveData() {
return trackData;
}
}
After this, the TrackHomeViewModel is created which is used by both fragment TrackHome and TrackScreen like below
public class TrackHomeViewModel extends ViewModel {
ArrayList<String> btName;//button text
ArrayList<Integer> btImage; //button image
int FLAG;
SimpleDateFormat inFormat;
private TrackRepository trackRepository;
List<TrackData> trackList;
LiveData<TrackData> track;
public TrackHomeViewModel() {
btName = new ArrayList<>();
this.btName.add("Previous Day");
this.btName.add("Previous Three Days");
btImage = new ArrayList<>();
this.btImage.add(R.drawable.ic_baseline_calendar_today_24);
this.btImage.add(R.drawable.ic_baseline_calendar_view_day_24);
trackRepository = new TrackRepository();
track = trackRepository.getTrackDataMutableLiveData();
}
public void captureCardClick(int adapterPosition) {
String selectedChoice;
String searchTest,startDate,endDate;
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
inFormat = new SimpleDateFormat("HH:mm:ss", Locale.US);
Calendar cal = Calendar.getInstance();
selectedChoice = btName.get(adapterPosition);
if (selectedChoice.equals("Previous Day")) {
FLAG = 1;
searchTest = "";
endDate = dateFormatter.format(cal.getTime()); // get current date
cal.add(Calendar.DATE, -1); //ONE day before of current date
startDate = dateFormatter.format(cal.getTime()); // get PREVIOUS date
// int centerId = ClientInfo.getCenterId(preferences);
getTrackList(startDate,endDate,searchTest);
}else if(selectedChoice.equals("Previous Three Days")){
FLAG = 2;
searchTest = "";
endDate = dateFormatter.format(cal.getTime()); // get current date
cal.add(Calendar.DATE, -3); //Three day before of current date
startDate = dateFormatter.format(cal.getTime()); // get PREVIOUS date
// int centerId = ClientInfo.getCenterId(preferences);
getTrackList(startDate,endDate,searchTest);
}
}
public void getTrackList(String startDate,String endDate,String searchTest) {
trackRepository.getTrackList(startDate, endDate, searchTest);
}
public LiveData<TrackData> getTrackLiveData() {
return track;
}
}
in the TrackScreen fragment, loading the RecyclerView like below:
public class TrackScreen extends Fragment {
private TrackHomeViewModel trackHomeViewModel;
RecyclerView recyclerView;
TrackAdapter trackAdapter;
LinearLayoutManager linearLayoutManager;
Fragment trackHome;
SearchView searchView;
List<TrackData> trackDataList;
TextView currentFragment;
MaterialToolbar toolbar;
ImageButton back;
TrackInterface trackInterface;
TrackData trackD;
public static TrackScreen newInstance() {
return new TrackScreen();
}
#Override
public void onCreate(#Nullable Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
trackHomeViewModel = new ViewModelProvider(this).get(TrackHomeViewModel.class);
trackHomeViewModel.getTrackLiveData().observe(this, new Observer<TrackData>() {
#Override
public void onChanged(TrackData trackData) {
trackD = trackData;
}
});
trackInterface = new TrackInterface() {
#Override
public void captureRowClick(TrackData trackData, int adapterPosition) {
String patId = trackData.getPatientId();
Toast.makeText(getContext(), "Patient ID is " + patId, Toast.LENGTH_SHORT).show();
}
};
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_track_screen,container,false);
toolbar = view.findViewById(R.id.toolbar);
searchView = view.findViewById(R.id.search_report);
recyclerView = view.findViewById(R.id.track_recycler);
currentFragment = view.findViewById(R.id.current_fragment);
currentFragment.setText("Reports");
back = view.findViewById(R.id.back_screen);
trackHome = getParentFragmentManager().findFragmentByTag("TrackHome");
linearLayoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(linearLayoutManager);
if(trackAdapter==null) {
trackHomeViewModel.getTrackLiveData().observe(getViewLifecycleOwner(), new Observer<TrackData>() {
#Override
public void onChanged(TrackData trackData) {
trackAdapter = new TrackAdapter(getContext(),trackData.getTrackData(),trackInterface);
}
});
recyclerView.setAdapter(trackAdapter);
}else{
Toast.makeText(getContext(), "No Data Found", Toast.LENGTH_SHORT).show();
}
// other code
}
}
Data is loaded from API, but blank screen displayed by TrackScreen with
E/RecyclerView: No adapter attached; skipping layout message, i tested all other things still same message. Issue is when the TrackScreen fragment loads the list is empty and after executing the getTrackLiveData() method it loads the API data therefore at load list is empty so this message coming. I am not getting how to workaround this.
When the button is clicked it should load the TrackScreen fragment with list. Tried as above.
You need to move
recyclerView.setAdapter(trackAdapter);
inside
public void onChanged(TrackData trackData) {
...
}
Now the trackAdapter field is null when you set it to the recyclerView because onChanged is called asynchronously.
Resolved
The issue was using same viewmodel for two fragments. When using single model for two fragments it should be
viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);
not getlifecyclerowner when initializing the viewmodel.
After, wasting hours turned to Android official doc and found this here Communicating with fragments
Since, i was using trackHomeViewModel = new ViewModelProvider(this).get(TrackHomeViewModel.class);the code for retrieving the list from viewmodel was not executing(repository code running passing data to viewmodel but not to next fragment here TrackScreen) therefore empty list. Used Log.d and found trackData is empty. Changed to requireActivity() and everything resolved.

Listview replaces first item instead of creating new items (From Fragment-to-Fragment)

I have an Alarm app which contain 3 tabs (3 Fragments) , First one is "Edit" , And second one is "Alarm" which display a list-view of all alarms , Finally last tab is "Add Alarm" i have time-picker and button , i want when i pick a time and click on add button take that values to tab "alarm" and add it to list-view.
But in my code ,It just make 1 item , And when i add one , it replace old with new one , So it don't add it just update .
After day of searching i am really tired :(
Can anyone help me please .
Here is my full code.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
String getHourFromAddPage = intent.getStringExtra("HOURS_ADD_PAGE");
String getFormatFromAddPage = intent.getStringExtra("FORMAT_ADD_PAGE");
String getDaysFromAddPage = intent.getStringExtra("DAYS_ADD_PAGE");
String getLabelFromAddPage = intent.getStringExtra("LABEL_ADD_PAGE");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager() , getHourFromAddPage , getFormatFromAddPage , getDaysFromAddPage ,getLabelFromAddPage);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
viewPager.setCurrentItem(1);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentPagerAdapter {
#StringRes
private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2 , R.string.tab_text_3};
private final Context mContext;
private String time;
private String format;
private String days;
private String label;
public SectionsPagerAdapter(Context context, FragmentManager fm , String time ,String format , String days , String label ) {
super(fm);
mContext = context;
this.time = time;
this.format = format;
this.days = days;
this.label = label;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
Fragment fragment = new Fragment();
switch (position)
{
case 0:
fragment = new Edit();
break;
case 1:
fragment = new Alarm();
Bundle bundle = new Bundle();
bundle.putString("HOUR", time);
bundle.putString("FORMAT", format);
bundle.putString("DAYS", days);
bundle.putString("LABEL", label);
fragment.setArguments(bundle);
break;
case 2:
fragment = new Add();
break;
}
return fragment;
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
#Override
public int getCount() {
// Show 2 total pages.
return 3;
}
}
And here is Add.class (fragment1)
public class Add extends Fragment {
public Add() {
// Required empty public constructor
}
private TimePicker timePicker ;
private TextView tv ;
private Button add ;
private EditText label;
private String format = "";
public String subject = "";
static final String ADD_TIME_ALARM = "add_alarm";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_add, container, false);
tv = (TextView) v.findViewById(R.id.textView5);
timePicker = (TimePicker) v.findViewById(R.id.datePicker1);
label = (EditText) v.findViewById(R.id.subject) ;
add = (Button) v.findViewById(R.id.addAlarm);
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
addNewAlarm();
}
});
return v;
}
public void addNewAlarm()
{
subject = label.getText().toString();
int hour = timePicker.getHour();
//int minute = timePicker.getCurrentMinute();
if (hour == 0) {
hour += 12;
format = "AM";
} else if (hour == 12) {
format = "PM";
} else if (hour > 12) {
hour -= 12;
format = "PM";
} else {
format = "AM";
}
Intent intent = new Intent(getActivity().getBaseContext(), MainActivity.class);
intent.putExtra("HOURS_ADD_PAGE" , String.valueOf(hour));
intent.putExtra("FORMAT_ADD_PAGE" , format);
intent.putExtra("DAYS_ADD_PAGE" , "wed , mon");
intent.putExtra("LABEL_ADD_PAGE" , subject);
startActivity(intent);
}
}
Finaly here is Alarm.class(Fragment2)
public class Alarm extends Fragment {
public ArrayList<Times> Alarms = new ArrayList<>();
public Alarm() {
// Required empty public constructor
}
ListView list;
Button goAddAlarm ;
String format = "";
String subject = "";
myAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_alarm, container, false);
list = v.findViewById(R.id.listview);
Alarms.add(new Times( "07:09" , "AM" , "Wed , Mon" , "-Home Time"));
//This Func to add new item from Add.class to Alarm(List View)
adapter = new myAdapter(getContext(), R.layout.custom_list_alarm , Alarms);
list.setAdapter(adapter);
return v;
}
public void addNewAlarm(){
Bundle bundle = getArguments();
String hour = bundle.getString("HOUR");
String format = bundle.getString("FORMAT");
String days = bundle.getString("DAYS");
String label = bundle.getString("LABEL");
Alarms.add(new Times( hour , format , days , label));
}
}
List view Adapter
public class myAdapter extends BaseAdapter {
private Context c;
Fragment fr ;
private int res ;
private ArrayList<Times> time;
public myAdapter() {
}
public myAdapter(Context c , int res , ArrayList<Times> time)
{
this.c = c;
this.res = res;
this.time = time;
}
public void addAlarm(Times times)
{
this.time.add(times);
}
#Override
public int getCount() {
return time.size();
}
#Override
public Times getItem(int position) {
return time.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if(v == null)
{
v = LayoutInflater.from(c).inflate(res , null , false);
}
TextView tv_name = v.findViewById(R.id.textView);
TextView tv_name2 = v.findViewById(R.id.textView2);
TextView tv_name3 = v.findViewById(R.id.textView3);
TextView tv_name4 = v.findViewById(R.id.textView4);
Times t = getItem(position);
tv_name.setText(t.getTime());
tv_name3.setText(t.getDay());
tv_name2.setText(t.getDays());
tv_name4.setText(t.getSubject());
return v;
}
}
You should notify your adapter with adapter.notifyDataSetChanged() whenever you're adding new items to the list. Also consider using RecyclerView instead of ListView. It has bigger advantages and improvements from a ListView.

Realm and RecyclerView Item sorting and automatic ViewPager Fragment Communication

I'm facing a few problems for a while now which I'm having trouble to solve. So I refer to the Realm and RecyclerView geniuses among the community.
I'm woking on a ToDo-List that sets completed tasks back to the ToDo-List after 2 days. The app uses a ViewPager with two tabs: "TODO" & "DONE".
1. RecyclerView
1.1. I want the completed Tasks from fragment 1 to be sent back to fragment 0 automatically after 2 days.
The Problem: If the counter is at 0 (or below) the item gets sent to fragment 0.
If I delte the item in the next line I get an exception error: "java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling"
So I put the delete function into a handler. Then it's working BUT only if ONE gets sent back. If many items get sent back simultaneously the app crashes. When I reopen the app everything is working because it was successfully saved in realm but one item is always saved twice.
Where's the Problem (in DoneAdapter.java)?
2. Realm
2.1. When I add an Item to the RecyclerView (and simultaneously to Realm), the item gets added at the bottom. But I want to add every new item at position 0.
(I know how to achieve this wih an ArrayList, but I want the items to be stored and displayed when I reopen the app, so I'm using Realm DB.)
Do you have any suggestions to achieve this?
2.2. Is it possible to implement later on the onLongClickListener for dragging and droping items and rearranging the position with Realm?
(I want to use this https://www.youtube.com/watch?v=tNgevYpyA9E)
2.3. I want to add some nice animations when I add and check an item. Realm doesn't support mRecyclerView.setItemAnimator(...); but I heard it is possible by adding mAdapter.setHasStableIds(true);. Unfortunately it throws an Exception: java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers. (You can see this in my code below)
Do you have any solutions for that?
(optionally 1.4. Can you recommend me any Online DBs (e.g. Firebase) which I can sync with Realm or more generally: is it possible to sync an Online DB with Realm? Do you know any Tutorials (Udemy, YouTube) for setting up this sync process?)
Lastly: I want to update the Database with a background service every Midnight, so the counter in the completed section updates automatically. Does anyone know how to do this as well? Maybe with protected void onHandleIntent(Intent intent)?
Do you also know if there's an option in debugging mode to simulate passing time?
Here is the code:
MainActivity.java
public class MainActivity extends AppCompatActivity implements ToOtherFragmentCommunicator {
private ViewPagerAdapter mViewPagerAdapter;
private ViewPager mViewPager;
private static final int DONE = 1;
private static final int TODO = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mViewPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
RealmConfiguration configuration = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(configuration);
}
#Override
public void itemToOtherFragment(String data, int fragment) {
if (DONE == fragment) {
Done done = (Done) mViewPagerAdapter.getItem(fragment);
done.createDoneItem(data);
} else if (TODO == fragment) {
ToDo toDo = (ToDo) mViewPagerAdapter.getItem(fragment);
toDo.createToDoItem(data);
}
}
}
ToDo.java
public class ToDo extends Fragment {
private RecyclerView mRecyclerView;
private ToDoAdapter mAdapter;
private EditText taskInput;
private String taskName;
private Realm mRealm;
private RealmResults<ListItems> mResults;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View toDoView = inflater.inflate(R.layout.todo_layout, container, false);
mRecyclerView = (RecyclerView) toDoView.findViewById(R.id.todo_rv);
mRealm = Realm.getDefaultInstance();
mResults = mRealm.where(ListItems.class).equalTo("fragment", 0).findAllAsync();
setRecyclerView();
mRecyclerView.setItemAnimator(null);
//TODO add product to shopping list
final Handler handler = new Handler();
taskInput = (EditText) toDoView.findViewById(R.id.task_input);
taskInput.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (taskInput.getText().length() > 0 && (event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
taskName = taskInput.getText().toString();
//Problem 2.1
//Code for adding item at the top with mRealm?
mRealm.beginTransaction();
createToDoItem(taskName);
mRealm.commitTransaction();
// mRecyclerView.scrollToPosition(0);
taskInput.setText(null);
handler.postDelayed(new Runnable() {
#Override
public void run() {
taskInput.setFocusableInTouchMode(true);
taskInput.setFocusable(true);
taskInput.requestFocus();
}
}, 200);
return true;
} else if (taskInput.length() == 0 && (event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
taskInput.clearFocus();
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(taskInput.getWindowToken(), 0);
return true;
}
return false;
}
});
return toDoView;
}
//TODO creates the shopping list item in DB
public void createToDoItem(String taskName) {
ListItems item = mRealm.createObject(ListItems.class);
long now = System.currentTimeMillis();
item.setAddedTime(now);
item.setFragment(0);
item.setTaskName(taskName);
mRealm.copyToRealmOrUpdate(item);
}
public void setRecyclerView() {
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new ToDoAdapter(getActivity(), mRealm, mResults);
mRecyclerView.setAdapter(mAdapter);
//Problem 2.3.
//Produces "java.lang.IllegalStateException: Cannot change whether this adapter has stable IDs while the adapter has registered observers."
// mAdapter.setHasStableIds(true);
}
private RealmChangeListener mChangeListener = new RealmChangeListener() {
#Override
public void onChange() {
mAdapter.updateItems(mResults);
}
};
#Override
public void onStart() {
super.onStart();
mResults.addChangeListener(mChangeListener);
}
#Override
public void onStop() {
super.onStop();
mResults.removeChangeListener(mChangeListener);
}
}
ToDoAdapter.java
public class ToDoAdapter extends RecyclerView.Adapter<ListItemsViewHolder> {
private Context mContext;
private Realm mRealm;
private RealmResults<ListItems> mResults;
private int focusedItem = 0;
ToOtherFragmentCommunicator comm;
ToDoAdapter(Context context, Realm realm, RealmResults<ListItems> mResults) {
this.mContext = context;
this.mRealm = realm;
updateItems(mResults);
}
public void updateItems(RealmResults<ListItems> mResults) {
this.mResults = mResults;
notifyDataSetChanged();
}
//Problem 2.3.
//needed for mAdapter.setHasStableIds(true); in ToDo.java
// #Override
// public long getItemId(int position) {
// if (position < mResults.size()) {
// return mResults.get(position).getAddedTime();
// } else {
// return RecyclerView.NO_ID;
// }
// }
#Override
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.todo_item, parent, false);
comm = (ToOtherFragmentCommunicator) mContext;
return new ListItemsViewHolder(v);
}
#Override
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) {
final ListItems items = mResults.get(position);
holder.taskName.setText(items.getTaskName());
holder.itemView.setSelected(focusedItem == position);
holder.getLayoutPosition();
holder.itemCheckbox.setOnCheckedChangeListener(null);
holder.itemCheckbox.setChecked(items.isSelected());
holder.itemCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mRealm.beginTransaction();
items.setSelected(isChecked);
//send item to Done
comm.itemToOtherFragment(items.getTaskName(), 1);
removeItem(position);
mRealm.commitTransaction();
}
});
}
#Override
public int getItemCount() {
return (mResults != null ? mResults.size() : 0);
}
private void removeItem(int position) {
mResults.get(position).removeFromRealm();
notifyDataSetChanged();
}
}
Done.java
public class Done extends Fragment {
private RecyclerView mRecyclerView;
private DoneAdapter mAdapter;
private Calendar calendar = Calendar.getInstance();
private Date date = new Date();
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy");
private Realm mRealm;
private RealmResults<ListItems> mResults;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View doneView = inflater.inflate(R.layout.done_layout, container, false);
mRecyclerView = (RecyclerView) doneView.findViewById(R.id.done_rv);
mRealm = Realm.getDefaultInstance();
mResults = mRealm.where(ListItems.class).equalTo("fragment", 1).findAllAsync();
setRecyclerView();
mRecyclerView.setItemAnimator(null);
return doneView;
}
//TODO creates the fridge item in DB
public void createDoneItem(String taskName) {
TimeZone.getDefault();
ListItems item = mRealm.createObject(ListItems.class);
long now = System.currentTimeMillis();
item.setAddedTime(now);
item.setFragment(1);
item.setTaskName(taskName);
item.setInputDate(simpleDateFormat.format(calendar.getTime()));
calendar.add(Calendar.DATE, 2);
item.setRenewDate(simpleDateFormat.format(calendar.getTime()));
//reset time to current date after adding days
calendar.setTime(date);
item.getRenewDate();
mRealm.copyToRealmOrUpdate(item);
}
public void setRecyclerView() {
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new DoneAdapter(getActivity(), mRealm, mResults, Done.this);
mRecyclerView.setAdapter(mAdapter);
}
private RealmChangeListener mChangeListener = new RealmChangeListener() {
#Override
public void onChange() {
mAdapter.updateItems(mResults);
}
};
#Override
public void onStart() {
super.onStart();
mResults.addChangeListener(mChangeListener);
}
#Override
public void onStop() {
super.onStop();
mResults.removeChangeListener(mChangeListener);
}
}
DoneAdapter.java
public class DoneAdapter extends RecyclerView.Adapter<ListItemsViewHolder> {
private Context mContext;
private Done done;
private Realm mRealm;
private RealmResults<ListItems> mResults;
private int focusedItem = 0;
protected ToOtherFragmentCommunicator comm;
DoneAdapter(Context context, Realm realm, RealmResults<ListItems> results, Done done) {
this.mContext = context;
this.mRealm = realm;
this.done = done;
updateItems(results);
}
public void updateItems(RealmResults<ListItems> mResults) {
this.mResults = mResults;
notifyDataSetChanged();
}
#Override
public ListItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.done_item, parent, false);
comm = (ToOtherFragmentCommunicator) mContext;
return new ListItemsViewHolder(v);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onBindViewHolder(final ListItemsViewHolder holder, final int position) {
final ListItems items = mResults.get(position);
holder.taskName.setText(items.getTaskName());
try {
if (items.getRenewCounter() == 1) {
holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.day)));
} else {
holder.renewCounter.setText(mContext.getString(R.string.show_days_till_renew, items.getRenewCounter(), mContext.getString(R.string.days)));
}
holder.renewCounter.setTextColor(ContextCompat.getColor(mContext, R.color.colorAccent));
if (items.getRenewCounter() <= 0) {
mRealm.beginTransaction();
//Problem 1.1.
//send item back to todo list
comm.itemToOtherFragment(items.getTaskName(), 0);
// Produces "java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling" if there is no Handler
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
mRealm.beginTransaction();
removeItem(position);
mRealm.commitTransaction();
}
};
handler.post(r);
mRealm.commitTransaction();
}
} catch (ParseException e) {
e.printStackTrace();
}
holder.itemView.setSelected(focusedItem == position);
holder.getLayoutPosition();
}
#Override
public int getItemCount() {
return (mResults != null ? mResults.size() : 0);
}
private void removeItem(int position) {
mResults.get(position).removeFromRealm();
notifyDataSetChanged();
}
}
ListItems.java
public class ListItems extends RealmObject {
public ListItems(long addedTime, String taskName, String inputDate, String renewDate, int fragment) {
this.addedTime = addedTime;
this.taskName = taskName;
this.inputDate = inputDate;
this.renewDate = renewDate;
this.fragment = fragment;
}
#PrimaryKey
private long addedTime;
private int fragment;
#Ignore
private long renewCounter;
private String taskName, inputDate, renewDate;
private boolean selected;
public ListItems() {
}
public long getAddedTime() {
return addedTime;
}
public void setAddedTime(long addedTime) {
this.addedTime = addedTime;
}
public int getFragment() {
return fragment;
}
public void setFragment(int fragment) {
this.fragment = fragment;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getInputDate() {
return inputDate;
}
public void setInputDate(String inputDate) {
this.inputDate = inputDate;
}
public String getRenewDate() {
return renewDate;
}
public void setRenewDate(String renewDate) {
this.renewDate = renewDate;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public long getRenewCounter() throws ParseException {
TimeZone.getDefault();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
Date todayDate = new Date();
Date exDate = dateFormat.parse(renewDate);
this.renewCounter = daysBetween(todayDate, exDate);
return renewCounter;
}
private static long daysBetween(Date startDate, Date endDate) {
Calendar sDate = getDatePart(startDate);
Calendar eDate = getDatePart(endDate);
long daysBetween = 0;
while (sDate.before(eDate)) {
sDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
while (eDate.before(sDate)) {
eDate.add(Calendar.DAY_OF_MONTH, 1);
daysBetween--;
}
return daysBetween;
}
private static Calendar getDatePart(Date date) {
Calendar cal = Calendar.getInstance(); // get calendar instance
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0); // set hour to midnight
cal.set(Calendar.MINUTE, 0); // set minute in hour
cal.set(Calendar.SECOND, 0); // set second in minute
cal.set(Calendar.MILLISECOND, 0); // set millisecond in second
return cal; // return the date part
}
}
Here's a Screenshot on how the app looks like:
DailyTaskRepeater
That's it! It would mean the world to me if someone could help me with all that (especially Problem 1.1!).
Thank you!
The current practice Realm supports is to add an index (e.g. timestamp) and to reverse sort your list for having the latest item at the top and achiving the rearranging effect you are seeking for.
Please consider taking a reference from an adapter example provided in the official repository.

Updating ViewPager Adapter?

I am using tabLayout which is attached with the viewPager. I have implemented viewPager Adapter using FragmentStatePager adapter, because the tabLayout list is big.
ViewPager creates a new instance of each fragment, and inside the fragment, i am using recyclerView to the data.
Now, before initiating the viewPager i am making a call to the server, and in success response, i am initiating viewPager for the first time. Then viewPager calls its getItem() callback, where it gets the data for the first position initialises the fragment and it's recyclerView.
It also call getItem() for the next position as i have set the offScreenLimit() to 2, which is fine and working well.
The problem is when it calls getItem() and creates the fragment, at that time, i am checking if i have the data, if not, then i am making a callback to get data from server.
And if user visits that position, i want to updated the fragment recyclerView.
Here, i have passed the reference of the main object in the fragment, so on success of request, it updates the main object, and the updated object will be present inside the fragment list.
Since, it's an asynchronous call, i want to show a loader and then update the fragment.
Here is where my problem lies. I am not able to update the fragment recyclerView. It's updating but taking time.
I had tried using return POSITION_NONE which worked fine, but it's not a good approach as it creates a new fragment every time.
This is my viewPagerAdapter.
public class ShowTimeViewPageAdapter extends FragmentStatePagerAdapter {
private static final String TAG = ShowTimeViewPageAdapter.class.getSimpleName();
private Context mContext;
private List<String> mDateStringList;
private FragmentManager fragmentManager;
private IShowTimeActivity mIShowTimeActivity;
private List<String> mDateCodes;
private LinkedHashMap<String, List<Venue>> mVenueHashMap;
public ShowTimeViewPageAdapter(FragmentManager fm, Context context, IShowTimeActivity showTimeActivity, List<String> createdDates, List<String> dateCodes, LinkedHashMap<String, List<Venue>> venueHashMap) {
super(fm);
this.mContext = context;
this.mIShowTimeActivity = showTimeActivity;
this.fragmentManager = fm;
this.mDateCodes = dateCodes;
this.mVenueHashMap = venueHashMap;
this.mDateStringList = createdDates;
}
private String getDateCode(int pos) {
String dateCode = null;
try {
dateCode = mDateCodes.get(pos);
} catch (IndexOutOfBoundsException e) {
Logger.d(TAG, "Index out of bound");
}
return dateCode;
}
private List<Venue> getVenuesList(String dateCode) {
return mVenueHashMap.get(dateCode);
}
#Override
public Fragment getItem(int position) {
if (getDateCode(position) != null) {
if (getVenuesList(getDateCode(position)) == null) {
if(position!=0){
mIShowTimeActivity.requestForTheVenueListByDateCode(getDateCode(position), position);
}
}
}
ShowTimeFragment showTimeFragment = ShowTimeFragment.newInstance(mDateStringList.get(position).split(";")[2]);
fragmentManager.beginTransaction().add(showTimeFragment,""+position);
return showTimeFragment;
}
#Override
public int getCount() {
return mDateStringList.size();
}
public View getTabView(int position) {
String[] dateStr = mDateStringList.get(position).split(";");
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View tabView = layoutInflater.inflate(R.layout.show_time_tab_layout, null, false);
TextViewRoboto day = (TextViewRoboto) tabView.findViewById(R.id.show_time_tab_day);
TextViewRoboto date = (TextViewRoboto) tabView.findViewById(R.id.show_time_tab_date);
day.setText(dateStr[0]);
date.setText(dateStr[1]);
return tabView;
}
}
And this is the Fragment.
public class ShowTimeFragment extends Fragment {
#Bind(R.id.show_time_fragment_recycler_view) RecyclerView mShowTimeRecyclerView;
#Bind(R.id.show_time_fragment_no_data_text_view) TextViewRoboto mShowTimeNoDataTextView;
private static LinkedHashMap<String, List<Venue>> mVenueList = ShowTimeActivity.mVenueHashMap;
private ShowTimeRecyclerViewAdapter mShowTimeRecyclerViewAdapter;
private static final String KEY_CODE = "date_key";
public ShowTimeFragment() {
}
public static ShowTimeFragment newInstance(String dateCode) {
Bundle bundle = new Bundle();
bundle.putString(KEY_CODE, dateCode);
ShowTimeFragment showTimeFragment = new ShowTimeFragment();
showTimeFragment.setArguments(bundle);
return showTimeFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.show_time_fragment, container, false);
ButterKnife.bind(this, view);
String code = getArguments().getString(KEY_CODE);
List<Venue> venueList = mVenueList.get(code);
if (venueList != null && venueList.size() > 0) {
mShowTimeRecyclerView.setVisibility(View.VISIBLE);
mShowTimeNoDataTextView.setVisibility(View.GONE);
mShowTimeRecyclerViewAdapter = new ShowTimeRecyclerViewAdapter(venueList, getContext());
mShowTimeRecyclerView.setAdapter(mShowTimeRecyclerViewAdapter);
CustomLinearLayoutManager cl = new CustomLinearLayoutManager(getActivity(), 1, false);
mShowTimeRecyclerView.setLayoutManager(cl);
RecyclerView.OnItemTouchListener disabler = new RecyclerViewDisabler();
mShowTimeRecyclerView.addOnItemTouchListener(disabler); // disables scrolling
} else {
mShowTimeNoDataTextView.setVisibility(View.VISIBLE);
mShowTimeRecyclerView.setVisibility(View.GONE);
mShowTimeNoDataTextView.setText("NO Data Available");
}
return view;
}
public class RecyclerViewDisabler implements RecyclerView.OnItemTouchListener {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
return true;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
protected void addFragment(int containerViewId, Fragment fragment, String fragmentTag) {
getActivity().getSupportFragmentManager().beginTransaction().add(containerViewId, fragment, fragmentTag).disallowAddToBackStack().commit();
}
public void onDestroyView() {
super.onDestroyView();
}
}
In the above fragment
private static LinkedHashMap<String, List<Venue>> mVenueList = ShowTimeActivity.mVenueHashMap;
this hashMap is the reference to the original hashMap, which is updated on each request.
Any kind of help or suggestions would be appreciated.

ListView in ListFragment keeps accumulating data

I am unable to refresh my ListView in ListFragment with new data. Instead the new data is added to the previous.
The time period is from 6AM to 5PM for each entity. Then new data is appended to the list restarting at 6AM for another entity. The data for the first entity should be cleaned before the second is added to the ListView.
Here is the code:
public class FragmentStatePagerSupport extends FragmentActivity {
static final int NUM_ITEMS = 4; //control number of fragments
MyAdapter mAdapter;
ViewPager mPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(null);
mPager.setAdapter(mAdapter);
}
//===============================================================================================
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
fragNumber = position;
return ArrayListFragment.newInstance(position);
}
}
//===============================================================================================
public static class ArrayListFragment extends ListFragment {
Integer mNum;
String FORMAT_LINE = "%s%7s%7s%10s%16s";
static ArrayListFragment newInstance(int num) {
ArrayListFragment f = new ArrayListFragment();
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_pager_list, container, false);
View tv = v.findViewById(R.id.text);
View tvd = v.findViewById(R.id.tv_description);
String title = "";
switch (mNum){
case 0:title = MyGlobals.getInstance().getToday();break;
case 1:title = MyGlobals.getInstance().getTomorrow();break;
case 2:title = MyGlobals.getInstance().getDayAfter();break;
case 3:title = MyGlobals.getInstance().getDayDayAfter();break;
}
((TextView) tv).setText(MyGlobals.getInstance().getName() + " on " + title);
((TextView) tvd).setText(String.format(FORMAT_LINE, "time", "temp", "rain", "wind", "weather"));
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayList<Data> row = Data.getRows(mNum);
ListViewAdapter adapter = new ListViewAdapter(getActivity(), row);
getListView().setAdapter(null);
getListView().setAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
v.setBackgroundColor(getResources().getColor(R.color.blue));
}
}
//==============================================================================================
public static class ListViewAdapter extends ArrayAdapter<Data> {
public static class ViewHolder{
TextView time;
TextView temp;
TextView rain;
TextView wind_speed;
TextView weather;
}
public ListViewAdapter(Context context, ArrayList<Data> list) {super(context, R.layout.text_listview, list); }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Data data = getItem(position);
ViewHolder holder;
if(convertView == null){
holder=new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView=inflater.inflate(R.layout.text_listview,parent,false);
holder.time=(TextView) convertView.findViewById(R.id.time);
holder.temp=(TextView) convertView.findViewById(R.id.temp);
holder.rain=(TextView) convertView.findViewById(R.id.rain);
holder.wind_speed=(TextView) convertView.findViewById(R.id.wind);
holder.weather=(TextView) convertView.findViewById(R.id.weather);
convertView.setTag(holder);
}else{
holder=(ViewHolder) convertView.getTag();
}
holder.time.setText(data.time);
holder.temp.setText(data.temp);
holder.rain.setText(data.rain);
holder.wind_speed.setText(data.wind_speed);
holder.weather.setText(data.weather);
return convertView;
}
}
}
This last piece populates the ListView. The calls adapter.clear() and adapter.notifyDataSetChanged() does not resolve the problem.
There is a similar question from 3 years ago How update ListView in ListFragment from FragmentActivity? still without accepted response despite the 6836 views.
Thanks a Lot.
The GetRows(mNum) piece as requested:
public class Data {
public String time;
public String temp;
public String rain;
public String wind_speed;
public String weather;
public Data(String time, String temp, String rain, String wind_speed, String weather) {
this.time = time;
this.temp = temp;
this.rain = rain;
this.wind_speed = wind_speed;
this.weather = weather;
}
public static ArrayList<Data> getRows(int fragNumber) {
int mNum = fragNumber;
int size;
String myList[] = null;
ArrayList<Data> list = new ArrayList<Data>();
switch (mNum) {
case 0://Today
size = MyGlobals.getInstance().getToday("time").size();
myList = new String[size];
for (int i = 0; i < size; i++) {
String time = (String) MyGlobals.getInstance().getToday("time").get(i);
String temp = (String) MyGlobals.getInstance().getToday("temperature").get(i);
String wind_speed = (String) MyGlobals.getInstance().getToday("wind_speed").get(i);
String pop = (String) MyGlobals.getInstance().getToday("pop").get(i);
//String wind_gust = (String) MyGlobals.getInstance().getToday("wind_gust").get(i); //when null breaks code
String weather = (String) MyGlobals.getInstance().getToday("weather").get(i);
if (time.length() == 4) time = "0" + time;
if (wind_speed.length() == 1) wind_speed = "0" + wind_speed;
if (pop.length() == 1) pop = "0" + pop;
list.add(new Data(time,temp,pop,wind_speed,weather));
}
return list;
case 1://Tomorrow
...snip... same as above with pertinent variables,...
case 2://DayAfter
...snip...
case 3://DayDayAfter
...snip....
case 4:// is an error
Log.d("***error***", "list got to case 5");
}
return list;
}
}
Try this;
Before adding new list of Data to the existing list, use list.clear();
Add new Data to the list and you should have only newly added data in the list..
pass the list to your adapter class.
Notify adapter of the new data i.e. adapter.notifyDataSetChanged()
You are returning list twice in your getRows()
remove the "return list" before the case structure
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
// Clear old list here before adding new one.
if (list! = null)
{
list.clear();
}
// Add New list
list = Data.getRows(mNum);
ListViewAdapter adapter = new
ListViewAdapter(getActivity(), list);
getListView().setAdapter(adapter);
adapter.notifyDataSetChange();
}
It's because everytime you set an Adapter, it will really be added., remove all its data first before setting again so it will be like
mPager.setAdapter(null);
before doing
mPager.setAdapter(mAdapter);

Categories

Resources