Firebase+RecyclerView: RecyclerView not displaying on application start - android-fragments

I'm using the Android Studio provided class for a tabbed activity that uses Action Bar Tabs with ViewPager. Inside this activity, I'm trying to initialize a RecyclerView with data from a Firebase database.
Problem: On the app's first run, the RecyclerView is empty as shown below.
If I close and reopen the application from within the emulator, my RecyclerView gets populated as it should, as shown below.
Any ideas as to why this might be happening? I have a theory but I haven't been able to find a solution. After trying to read the FragmentPagerAdapter page, I got the impression that the fragments must be static (I don't know what the implications of this might be, so if anyone can shed some light on this it would be appreciated). On the app's first run, it initializes the RecyclerView. It then adds the data from the Firebase database but since the RecyclerView has already been initialized it is empty and is never properly updated. I tried calling the notify... methods to no avail.
StudentFragment's onCreateView method:
private View view;
private Context c;
private RecyclerView mRecyclerView;
private LinearLayoutManager manager;
private Firebase mFirebaseRef;
private FirebaseRecyclerAdapter<Student, ViewHolder> firebaseRecyclerAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_students, container, false);
mFirebaseRef = new Firebase("<your Firebase link here>");
c = getContext();
//Initializes Recycler View and Layout Manager.
mRecyclerView = (RecyclerView) view.findViewById(R.id.studentRecyclerView);
manager = new LinearLayoutManager(c);
mRecyclerView.setHasFixedSize(true);
firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Student, ViewHolder>(
Student.class,
R.layout.single_student_recycler,
ViewHolder.class,
mFirebaseRef
) {
#Override
protected void populateViewHolder(ViewHolder viewHolder, Student student, int i) {
viewHolder.vFirst.setText(student.getFirst());
viewHolder.vLast.setText(student.getLast());
viewHolder.vDue.setText(Double.toString(student.getCurrentlyDue()));
viewHolder.vRadio.setButtonTintList(ColorStateList.valueOf(Color.parseColor(student.getColor())));
Log.d(TAG, "populateViewHolder called");
}
};
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
mRecyclerView.setLayoutManager(manager);
return view;
}
ViewHolder:
public static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView vFirst;
public final TextView vLast;
public final TextView vDue;
public final RadioButton vRadio;
public ViewHolder(View itemView) {
super(itemView);
vFirst = (TextView) itemView.findViewById(R.id.recycler_main_text);
vLast = (TextView) itemView.findViewById(R.id.recycler_sub_text);
vRadio = (RadioButton) itemView.findViewById(R.id.recycler_radio_button);
vDue = (TextView) itemView.findViewById(R.id.recycler_due_text);
}
Homescreen's onCreate method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homescreen);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
Firebase context is set on another application that starts as soon as the Homescreen activity starts. Any help will be appreciated.
Edit: I was digging through the FirebaseUI GitHub page, which is where the problem most likely lies, and found another user with the exact same problem. It seems that onBindViewHolder isn't called after notifyItemInserted in the FirebaseRecyclerAdapter class. Now to fix it...

In my case this was caused by mRecyclerView.setHasFixedSize(true); If you comment out this line of code the list loads properly. I got my solution from this discussion: https://github.com/firebase/FirebaseUI-Android/issues/204

Let me try, as you say on the question title,
RecyclerView not displaying on application start
so, the
Initializes Recycler View and Layout Manager.
should be declared on the onStart
#Override
public void onStart() {
super.onStart();
mFirebaseRef = new Firebase("<your Firebase link here>");
firebaseRecyclerAdapter = ...
//and so on
Hope it helps!

firebaseRecyclerAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int friendlyMessageCount = firebaseRecyclerAdapter.getItemCount();
int lastVisiblePosition =
linearLayoutManager.findLastCompletelyVisibleItemPosition();
// If the recycler view is initially being loaded or the
// user is at the bottom of the list, scroll to the bottom
// of the list to show the newly added message.
if (lastVisiblePosition == -1 ||
(positionStart >= (friendlyMessageCount - 1) &&
lastVisiblePosition == (positionStart - 1))) {
linearLayoutManager.scrollToPosition(positionStart);
}
}
});
recyclerListIdeas.setAdapter(firebaseRecyclerAdapter);
** Just add Recyclerview.AdapterDataObserver() . worked for me ! hope it helps :)**

i had the same issue, check the documentation:
https://codelabs.developers.google.com/codelabs/firebase-android/#6
fixed it by adding a data observer:
mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int friendlyMessageCount = mFirebaseAdapter.getItemCount();
int lastVisiblePosition =
mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
// If the recycler view is initially being loaded or the
// user is at the bottom of the list, scroll to the bottom
// of the list to show the newly added message.
if (lastVisiblePosition == -1 ||
(positionStart >= (friendlyMessageCount - 1) &&
lastVisiblePosition == (positionStart - 1))) {
mMessageRecyclerView.scrollToPosition(positionStart);
}
}
});

Related

Change fragment activity through fragment directly from button Xamarin.Android

I'm using navigation drawer template from Xamarin and it working perfectly. So I can change layout dynamically. Now, I need to change fragment directly from single button (like shortcut). Changing between fragments works differently not like simple activity. In activity I can do simple code to change activity through a button:
Intent nextActivity = new Intent(this, typeof(ItemAddFormActivity));
StartActivity(nextActivity);
But, how to change fragment layout from single button? I'm still searching how to change fragment between fragment with button.
Maybe someone here can help me.
Thanks in advance.
This is how i have implemented in my solution, might help you
this is my fragment
public class Fragment3 : Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public static Fragment3 NewInstance()
{
var frag1 = new Fragment3 { Arguments = new Bundle() };
return frag1;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
return inflater.Inflate(Resource.Layout.fragment3, null);
}
}
To Display Fragment on button event use this
Android.Support.V4.App.Fragment fragment = null;
fragment = Fragment3.NewInstance();
if (fragment == null)
return;
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.Commit();

Android TabLayout Fragment with MVP

I'm implementing an MVP app in which the Views are fragments loaded in Activities. Each Activity had 1 fragment to display. I have to change my implementation and add the TabLayout which will now display the fragments. I've tried different ways of passing the fragment to the adapter but all makes my app crash and I can't understand the error. My last try, I'm passing an arraylist of fragments(1 for now) to the adapter. At the base, I'm following google samples MVP todo app, but I need to implement this tab layout. Please, this is for my major project, I looked everywhere and this is my last resort.
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar mHomeToolbar = (Toolbar) findViewById(R.id.toolbar); // Set to the corresponding Toolbar in the UI.
setSupportActionBar(mHomeToolbar);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); // Set to the corresponding Drawer Layout in the UI.
ActionBarDrawerToggle mToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mHomeToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
mDrawerLayout.addDrawerListener(mToggle); // Set mToggle as Drawer's toggle button and listen to actions.
mToggle.syncState();
NavigationView mDrawerNavigationView = (NavigationView) findViewById(R.id.nav_view); // Set the corresponding Navigation View in the UI.
mDrawerNavigationView.setNavigationItemSelectedListener(this); // Add listener on Navigation's items.
HomeFragment homeFragment = (HomeFragment) getSupportFragmentManager().findFragmentById(R.id.Quests_Frame); // Set to corresponding Fragment View in the UI.
if (homeFragment == null) {
homeFragment = HomeFragment.newInstance();
FragmentLoader.loadFragmentInActivity(getSupportFragmentManager(), homeFragment, R.id.Quests_Frame); // Display fragment in Activity.
}
repo = QuestsRepository.getInstance(QuestsDataSource.getINSTANCE());
mHomePresenter = new HomePresenter(repo , homeFragment);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
TabPagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager());
adapter.addFragment(homeFragment);
viewPager.setAdapter(adapter);
}
The adapter class:
public class TabPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final int tabCount = 3;
public TabPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
mFragmentList.get(position);
default:
return null;
}
}
public void addFragment(Fragment fragment) {
mFragmentList.add(fragment);
}
#Override
public int getCount() {
return tabCount;
}
}
For what you want to achieve, you won't be using the FragmentLoader Class. Remove it (just for the tabs Activities). And the getSupportFragmentManager line.
In Home Activity, how you set up the tabLayout and Viewpager, it's fine.
Remove addFragment line.
Add the following after setAdapter:
mTabLayout.setupWithViewPager(mViewPager);
In the tabPagerAdapter, just create the object presenter and fragment there.
In the getItem method, case 0, you can have:
HomeFragment homeFragment = HomeFragment.newInstance();
homePresenter = new HomePresenter(repo, homeFragment);
return homeFragment;
Oh and in the TabPagerAdapter, you can pass your repo argument there for creating your presenter.
I hope I was clear. Let me know if you have any issues.

setOnItemSelectedListener seems to work only the first time the app is launched

I'm using a spinner in the menu of a fragment, load its data in the onCreateView. It works fine when the app is launched,however, the spinner disappears when the user navigates to a different fragment and comes back or when the app is opened the next time.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
setHasOptionsMenu(true);
getSpinnerValues(); //string request to add values to TrailList
}
#Override // ...
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.map_menu, menu);
super.onCreateOptionsMenu(menu, inflater); //temp
//setData();
final MenuItem item = menu.findItem(R.id.trailfiller);
mySpinner = (Spinner) MenuItemCompat.getActionView(item);
ArrayAdapter<Trail> adapter = new ArrayAdapter<Trail>(getContext(), android.R.layout.simple_spinner_dropdown_item, TrailList);
mySpinner.setAdapter(adapter);
mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Trail country = (Trail) parent.getSelectedItem();
Toast.makeText(getContext(), ""+country.getId()+""+country.getName(), Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
The moment it goes blank, the onItemSelected isn't getting triggered either.Strange thing to note is the spinner is consistent when the values are hardcoded. What am I missing here?
The onItemSelected would get triggered.Problem was with the ArrayList of size 0. Populating it inside the onCreateOptionsMenu will help. Alternatively,you can add an item to the list inside the onCreateOptionsMenu, which will help you get rid of this issue
Spinner spinner= new Spinner();
spinner.setName("select a country");
TrailList.add(spinner);

Child fragment from onListItemClick within viewPager behaves unexpectedly

I have 3 ListFragments being handled by a viewPager (managed by a FragmentAdapter) - they work perfectly. Now when the user clicks an item in ListFragment #1, a new Fragment should open with the details. It's behaving strangely in the following manner:
Only clicking a list item twice opens the DetailFragment, yet debugging shows the first click indeed goes into the DetailFragment, but doesn't show the view (the view still shows the current ListFragment).
After clicking the 2nd time, the DetailFragment does show it's layout, but not the elements within it (like TextView, etc).
If the user 'accidently' swipes the screen when DetailFragment is showing, the viewPager sets it in place of the 2nd ListFragment! Only when pressing back on the DetailFragment view will 'reset' the viewPager to it's correct ListFragment. Of course if the user swipes when in a DetailFragment, the next ListFragment of the viewPager should appear, and the DetailFragment should be removed.
Thanks for any tips muddling through Android's odd world of fragments and views :)
public class PlanetFragment extends ListFragment{
LayoutInflater inflater;
ListView list;
ArrayList<HashMap<String, String>> planetListArray;
HashMap<String, String> planetMap;
Activity activity;
Context context;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.planets_tab_layout, container, false);
inflater=(LayoutInflater)getLayoutInflater(savedInstanceState);
activity = getActivity();
context = PlanetFragment.this.getActivity();
String dbTableName = "Table_Planets";
SQLiteHelper info = new SQLiteHelper(getActivity().getBaseContext());
info.open();
ArrayList<HashMap<String, String>> datafromSQL = info.getData(dbTableName);
if(!datafromSQL.isEmpty()){
planetListArray = new ArrayList<HashMap<String, String>>();
for (int i = 0; i<datafromSQL.size(); i++){
planetMap = new HashMap<String, String>();
planetMap.put(PLANET_ID, datafromSQL.get(i).get(KEY_PLANET_ID));
planetMap.put(ZODIAC_ID, datafromSQL.get(i).get(KEY_ZODIAC_ID));
planetMap.put(DEGREES, datafromSQL.get(i).get(KEY_DEGREES));
planetMap.put(CONTENT, datafromSQL.get(i).get(KEY_CONTENT));
planetListArray.add(planetMap);
}
info.close();
}
list = (ListView) v.findViewById(android.R.id.list);
PlanetAdapter adapter=new PlanetAdapter(getActivity(), R.layout.planets_row, planetListArray);
list.setAdapter(adapter);
return v;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//the dividers
getListView().setDivider(getResources().getDrawable(R.drawable.purplebartop));
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
HashMap<String, String> item = planetListArray.get(position);
Bundle bundle = new Bundle();
bundle.putSerializable("itemMap", item);
bundle.putInt("position", position);
Fragment frag = DetailFragment.newInstance();
frag.setArguments(bundle);
if (frag != null) {
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.pager, frag, "frag")
.addToBackStack("frag")
.commit();
}
}
}
public class DetailFragment extends Fragment{
Context context;
Activity activity;
TextView planetName;
public static android.support.v4.app.Fragment newInstance() {
DetailFragment f = new DetailFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
inflater=(LayoutInflater)getLayoutInflater(savedInstanceState);
View v = inflater.inflate(R.layout.dialog_details, container, false);
activity = getActivity();
context = DetailFragment.this.getActivity();
planetName = (TextView)v.findViewById(R.id.planetNameExpanded);
planetName.setText("planetX");
return v;
}
}
EDIT:
Instead of getActivity().getSupportFragmentManager() I have also tried getChildFragmentManager() but it always gives the error: The method getChildFragmentManager() is undefined for the type PlanetFragment.
When you click on a list item, you are indeed constructing a new details fragment and telling the fragment manager to replace the tag "frag" with that fragment. However, you are not telling the view pager to switch over to that fragment.
Since you already have a back-pointer to your activity, you could use findViewById to find your view pager, and then call viewPager.setCurrentItem.
I think you might be asking for trouble by constructing a new details fragment inside of the list fragment. When you use a FragmentPagerAdapter, the adapter usually constructs the fragments. I would have implemented this by letting the adapter make the fragments, and then in your onListItemClick find the existing details fragment and call a method on it to configure it with the new data. But maybe just the setCurrentItem will fix your problem.
EDIT
First, I would write your FragmentPagerAdapter so you can use getItem to fetch the existing fragment, without creating a new one each time.
public class PlanetFragmentAdapter extends FragmentPagerAdapter {
private Fragment [] fragments = new Fragments[3];
public PlanetFragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = fragments[position];
if (fragment == null) {
switch (position) {
case 0:
fragment = new PlanetFragment();
break;
case 1:
fragment = new DetailFragment();
break;
case 2:
fragment = new MysteryFragment();
break;
}
fragments[position] = fragment;
}
return fragment;
}
}
Also add functions in your activity to work with your fragments:
public void setPage(int position) {
viewPager.setCurrentItem(position);
}
public DetailFragment getDetailFragment() {
return (DetailFragment) viewPager.getItem(1); // now it doesn't create a new instance
// you could also use getSupportFragmentManager().findFragmentById() here
}
Now when you click on an item in your list fragment, you can get the existing detail fragment, configure it, and set the ViewPager to show the detail fragment.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
HashMap<String, String> item = planetListArray.get(position);
Bundle bundle = new Bundle();
bundle.putSerializable("itemMap", item);
bundle.putInt("position", position);
PlanetActivity pa = (PlanetActivity) activity;
DetailFragment frag = pa.getDetailFragment();
frag.setArguments(bundle);
pa.setCurrentItem(1);
}

Update fragment from activity

I have a activity which have two fragments.
Activity receives broadcast events for the two fragments.
One fragment has a image button and text view. When the image button is clicked an event is send to the server and server responds back with live broadcast event.
We receive the response in activity and I need to update the UI of the fragment(the image button needs to be changed with another image)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.single_window_lock, container, false);
updateUI(view);
return view;
}
public void updateUI(View view){
String lockName;
final String lockState;
final boolean state;
final ImageButton singleLockImage = (ImageButton)view.findViewById(R.id.single_lock_image);
final TextView lockNameText = (TextView)view.findViewById(R.id.single_lock_name);
final TextView lockStateText = (TextView)view.findViewById(R.id.single_lock_state);
final ProgressBar progress = (ProgressBar)view.findViewById(R.id.singleLockProgress);
doorLock = LockState.getValue();
lockName = doorLock.getName();
if (doorLock.isLocked()) {
lockState = getActivity().getString(R.string.door_locks_locked);
singleLockImage.setImageResource(R.drawable.doorlocks_single_locked);
state = true;
} else {
lockState = getActivity().getString(R.string.door_locks_unlocked);
singleLockImage.setImageResource(R.drawable.doorlocks_single_unlocked);
state = false;
}
lockNameText.setText(lockName);
lockStateText.setText(lockState);
singleLockImage.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().changeState(state);
}
}
);
}
I thought to call updateUI, which will get the new state from the cache saved after the broadcast event received in Activity, but I am not sure how to pass (view)
Use FragmentActivity instead.
in FragmentActivity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Create an instance of ExampleFragment
TestFragment0 firstFragment = new TestFragment0();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
}
in FragmentActivity for fragments :
TestFragment0 firstFragment0 = new TestFragment0();
firstFragment0.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,firstFragment0).commit();

Resources