I'm using Firebase3 in Ionic2 App. The requirement is to load data of approx 2000 objects and every time when page switched(using navigation) the data needs to be loaded. But loading every time data does not looks well.
So, is there any solution to fetch data once - like contacts list.
Then the loaded data will be displayed from local.
Thanks!
Try this code :-
private List<Contact> contactList;
private SharedPreferences shared ;
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
shared = getSharedPreferences("AppPref", MODE_PRIVATE);
//**Get the data from local storage**
List<Contacts> contactList = new Gson().fromJson((shared.getString("contacts", "")),new TypeToke<List<Contact>>(){}.getType());
if(contactList ==null) { // if no data found then call firebase service
contactList=new ArrayList();
//Use addListenerForSingleValueEvent instead of ValueEventListener.So this will listen only once.
mDataBaseRef.child("Your_Node").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot postSnapshot : dataSnapshot.getChildren()){
contactList.add(postSnapshot.getValue(Contact.class);
}
//Save the data in Shared Preferences
SharedPreferences.Editor editor = shared.edit();
editor.putString("contacts", new Gson().toJson(contactList));
editor.commit();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
contactList = null ;
}
});
}
return view ;
}
Related
I have a fragment, with only a list view. I am loading the favourites from my database like below. How do I load them async? Currently I am forced to make the async method synchronised while loading the view. I would like it so the view loads, then when the results are ready they are populated so that it is faster. Its not just micro optimisation, I would like to learn how to do this properly.
public class FavoriteFragment : Fragment
{
public event EventHandler<Favorite> FavoriteSelected;
private FavoriteAdapter _lstAdapter;
private List<Favorite> _favorites;
private Repository _repo;
public override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
_repo = TinyIoCContainer.Current.Resolve<Repository>();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
var view = inflater.Inflate(Resource.Layout.fragment_favorite, container, false);
var lstFavorites = view.FindViewById<ListView>(Resource.Id.lstFavorites);
_favorites = Task.Run(async () => await _repo.GetFavoritesAsync()).Result;
_lstAdapter = new FavoriteAdapter(Activity, _favorites);
lstFavorites.Adapter = _lstAdapter;
lstFavorites.ItemClick += (s, e) => FavoriteSelected?.Invoke(this, _favorites[e.Position]);
return view;
}
From this thread onActivityCreated(), you can use OnActivityCreated() to load your data.
public override async void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
await LoadData();
}
private async Task LoadData()
{
//get data
}
I am trying to implement tabs through MvvmCross in Xamarin. I came across MvxTabActivity in Android and MvxTabBarViewController in IOS. Both are working well. The problem is MvxTabActivity is obselete. Are there any alternatives for MvxTabActivity?
I found another way to implement this, which uses TabLayout and a ViewPager.
The solution asks to use fragments within a fragment. I have pasted the code for this approach. The problem here is on swiping the tabs, all the data in previous tabs is lost.
I tried using RetainInstance = true, that gave following exception : "Can't retain fragements that are nested in other fragments."
Product Detail Activity :
[Activity(Label = "ProductDetailView")]
public class ProductDetailView : MvxAppCompatActivity<ProductDetailViewModel>
{
private FrameLayout _mainFrame;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.product_detail_view);
if (FindViewById<FrameLayout>(Resource.Id.frame_Detail) != null)
{
var frag = new NutritionCategoryView();
frag.ViewModel = ViewModel.NutritionCategoryModel;
var trans = SupportFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.frame_Detail, frag);
trans.AddToBackStack(null);
trans.Commit();
}
}
}
Nutrition Category View Fragment :
public class NutritionCategoryView : MvxFragment
{
public NutritionCategoryViewModel vm
{
get { return (NutritionCategoryViewModel) ViewModel; }
}
private TabLayout _tablayout;
private ViewPager _viewPager;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.nutrition_category_view, container, false);
SetViewPager(view);
return view;
}
private void SetViewPager(View view)
{
_viewPager = view.FindViewById<Android.Support.V4.View.ViewPager>(Resource.Id.viewpager);
if (_viewPager != null)
{
var fragments = new List<CategoryTabsAdapter.FragmentInfo>
{
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Proximates",
ViewModel = vm.Category1
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Minerals",
ViewModel = vm.Category2
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Fats",
ViewModel = vm.Category3
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Vitamins",
ViewModel = vm.Category4
}
};
_viewPager.Adapter = new CategoryTabsAdapter(Activity, ChildFragmentManager, fragments);
}
_tablayout = view.FindViewById<TabLayout>(Resource.Id.sliding_tabs);
_tablayout.SetBackgroundColor(Android.Graphics.Color.Black);
_tablayout.SetupWithViewPager(_viewPager);
}
}
Category Tabs Adapter :
public class CategoryTabsAdapter : FragmentStatePagerAdapter
{
private readonly Context _context;
public IEnumerable<FragmentInfo> Fragments { get; private set; }
public CategoryTabsAdapter(Context context, FragmentManager fragmentManager, IEnumerable<FragmentInfo> fragments) : base(fragmentManager)
{
_context = context;
Fragments = fragments;
}
public override int Count
{
get { return Fragments.Count(); }
}
public override Fragment GetItem(int position)
{
var fragmentInfo = Fragments.ElementAt(position);
var fragment = Fragment.Instantiate(_context, Java.Lang.Class.FromType(fragmentInfo.FragmentType).Name);
((MvxFragment)fragment).ViewModel = fragmentInfo.ViewModel;
return fragment;
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String(Fragments.ElementAt(position).Title);
}
public class FragmentInfo
{
public string Title { get; set; }
public Type FragmentType { get; set; }
public IMvxViewModel ViewModel { get; set; }
}
}
Category View Fragment
public class CategoryView : MvxFragment<CategoryViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.category_view, container, false);
//Exception caused here :
//RetainInstance = true;
return view;
}
}
I am new to Xamarin and MvvmCross, so could come up with this much research only. Any solution for either approaches would be of great help.
P.S. This is my first question on Stackoverflow.
I think you need to use the MvxCachingFragmentStatePagerAdapter which is in the MvvmCross.Droid.Support.V4 Nuget package. Then hook it up to your TabLayout with SetupWithViewPager().
var viewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
if (viewPager != null)
{
var fragments = new List<MvxCachingFragmentStatePagerAdapter.FragmentInfo>
{
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleA",
typeof (YourFragmentA),
typeof (YourViewModelA)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleB",
typeof (YourFragmentB),
typeof (YourViewModelB)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleC",
typeof (YourFragmentC),
typeof (YourViewModelC))
};
viewPager.Adapter = new MvxCachingFragmentStatePagerAdapter(Activity, ChildFragmentManager, fragments);
viewPager.OffscreenPageLimit = fragments.Count;
var tabLayout = view.FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewPager);
}
Your tabs are being recreated each time because you are using FragmentStatePagerAdapter instead of FragmentPagerAdapter. From the docs, FragmentPagerAdapter:
This version of the pager is best for use when there are a handful of
typically more static fragments to be paged through, such as a set of
tabs. The fragment of each page the user visits will be kept in
memory, though its view hierarchy may be destroyed when not visible.
This can result in using a significant amount of memory since fragment
instances can hold on to an arbitrary amount of state. For larger sets
of pages, consider FragmentStatePagerAdapter.
FragmentStatePagerAdapter:
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the
saved state of that fragment. This allows the pager to hold on to much
less memory associated with each visited page as compared to
FragmentPagerAdapter at the cost of potentially more overhead when
switching between pages.
So use FragmentPagerAdapter, but I think it will have to be at the Activity level, and not a fragment within a fragment.
I am using Retrofit 2.0 to retrieve data from my api and using a recyclerView to display it.
My main activity has a tab layout and one of those tabs has the recyclerView and the fragment class for that tab is being used to retrieve the data and update the layout.
In my main layout I have a fab which makes a post (all posts are being retrieved in fragment class) and this fab has it's function of making the post in main activity.
So how can I refresh the layout when the fab's function is over and the post is successfully saved in my database?
Basically
User clicks fab > Makes his post > Alert dialog closes > recyclerView should be refreshed with new data added.
My Fragment Class :
public class PostsRecentTab extends Fragment {
private static final String TAG = MainActivity.class.getSimpleName();
private RecyclerView feedView;
private ProgressDialog pDialog = MainActivity.pDialog;
LinearLayoutManager layoutManager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.tab_recent_posts, container, false);
pDialog.setCancelable(false);
layoutManager = new LinearLayoutManager(this.getContext());
feedView = (RecyclerView) v.findViewById(R.id.feedView);
requestData();
return v;
}
public void requestData() {
SocialHubAPI apiService = ApiClient.getClient().create(SocialHubAPI.class);
pDialog.setMessage("Refreshing...");
showDialog();
Call<StatusResponse> call = apiService.getStatuses();
call.enqueue(new Callback<StatusResponse>() {
#Override
public void onResponse(Call<StatusResponse> call, Response<StatusResponse> response) {
int statusCode = response.code();
List<Status> statuses = response.body().getStatuses();
Log.d(TAG, "Status Code: " + statusCode);
hideDialog();
updateView(statuses);
}
#Override
public void onFailure(Call<StatusResponse> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
private void updateView(List<Status> statuses) {
StatusesAdapter adapter = new StatusesAdapter(statuses, R.layout.feed_item, getContext());
feedView.setLayoutManager(layoutManager);
feedView.setAdapter(adapter);
}
private void showDialog() {
if (!pDialog.isShowing())
pDialog.show();
}
private void hideDialog() {
if (pDialog.isShowing())
pDialog.dismiss();
}
}
My Fab On Click :
FloatingActionButton postStatus = (FloatingActionButton) findViewById(R.id.postStatus);
postStatus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Post Status");
// Set up the input
final EditText input = new EditText(MainActivity.this);
// Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
// Set up the buttons
builder.setPositiveButton("Post", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
postText = input.getText().toString();
processPost(postText, sessionManager.getToken());
Snackbar.make(view, "Status posted!", Snackbar.LENGTH_LONG).show();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
});
Fab onClick calls this method :
protected void processPost(String postText, String token) {
SocialHubAPI apiService = ApiClient.getClient().create(SocialHubAPI.class);
pDialog.setMessage("Posting...");
showDialog();
final PostRequest postRequest = new PostRequest();
postRequest.setStatus(postText);
Call<PostResponse> call = apiService.postStatus(postRequest, token);
call.enqueue(new Callback<PostResponse>() {
#Override
public void onResponse(Call<PostResponse> call, Response<PostResponse> response) {
hideDialog();
Toast.makeText(getApplicationContext(), "Status Posted Successfully!", Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(Call<PostResponse> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
You should invalidate your list in updateView(List<Status> statuses) instead of setting the adapter again. Instantiate adapter only in onCreate().
This function should be like this:
adapter.addNewStatutes(statuses)
in Adapter class
public void addNewStatutes(List<Status> statuses)
{
this.statuses.addAll(statuses);
notifyDataSetChanged();
}
Also in onResponse use EventBus or Rx, because your view can be destroyed and this method can crash your app.
Added notifyDataSetChanged as per docs.
I have two fragments which are VolleyFragment and AnotherFragment, setting VolleyFragment as the initial fragment. In this fragment, the bundle data is displaying ok. However, when I go to AnotherFragment, the app crashes. It says this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] android.os.Bundle.getStringArray(java.lang.String)' on a null object reference
at com.example.rendell.volleyfragment.AnotherFragment.onCreateView(AnotherFragment.java:28)
MainActivity
public class MainActivity extends AppCompatActivity {
public static final String JSON_URL = "http://192.168.0.102/musicmania/music/getMF";
TextView id,name,email;
String[] ids, names, emails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
id = (TextView) findViewById(R.id.id);
name = (TextView) findViewById(R.id.name);
email = (TextView) findViewById(R.id.email);
sendRequest();
}
private void sendRequest(){
StringRequest stringRequest = new StringRequest(JSON_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
showJSON(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
private void showJSON(String json){
ParseJSON pj = new ParseJSON(json);
pj.parseJSON();
id.setText(ParseJSON.ids[0]);
name.setText(ParseJSON.titles[0]);
email.setText(ParseJSON.artists[0]);
FragmentManager fragmentTransaction = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentTransaction.beginTransaction();
VolleyFragment squadFragment = new VolleyFragment();
AnotherFragment anotherFragment = new AnotherFragment();
Bundle bundle = new Bundle();
bundle.putStringArray("id", ParseJSON.ids);
bundle.putStringArray("name", ParseJSON.titles);
bundle.putStringArray("email", ParseJSON.artists);
squadFragment.setArguments(bundle);
anotherFragment.setArguments(bundle);
transaction.replace(R.id.containerView, squadFragment);
transaction.commit();
}
public void changeFragment(View view) {
FragmentManager fragmentTransaction = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentTransaction.beginTransaction();
AnotherFragment anotherFragment = new AnotherFragment();
transaction.replace(R.id.containerView, anotherFragment);
transaction.commit();
}
}
VolleyFragment
public class VolleyFragment extends Fragment {
TextView id,name,email;
String[] ids,names,emails;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.volley_fragment, container, false);
id = (TextView) view.findViewById(R.id.id);
name = (TextView) view.findViewById(R.id.name);
email = (TextView) view.findViewById(R.id.email);
ids = getArguments().getStringArray("id");
names = getArguments().getStringArray("name");
emails = getArguments().getStringArray("email");
//jsonArray.setIds(id);
id.setText(/*jsonArray.getIds()*/ids[0]);
name.setText(names[0]);
email.setText(emails[0]);
return view;
}
}
AnotherFragment
public class AnotherFragment extends Fragment {
TextView id,name,email;
String[] ids,names,emails;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.another_fragment, container, false);
id = (TextView) view.findViewById(R.id.id);
name = (TextView) view.findViewById(R.id.name);
email = (TextView) view.findViewById(R.id.email);
ids = getArguments().getStringArray("id");
names = getArguments().getStringArray("name");
emails = getArguments().getStringArray("email");
//jsonArray.setIds(id);
id.setText(/*jsonArray.getIds()*/ids[0]);
name.setText(names[0]);
email.setText(emails[0]);
return view;
}
}
I'm having trouble following exactly what you're doing here, but I see a number of problems:
In showJSON, you new up a ParseJSON object and assign it to pj, but never make use of pj. Instead you reach statically into ParseJSON for the values to populate a Bundle. That seems really odd. I'd expect the pj instance to contain the data.
Typically one does not "new" up a fragment. The usual pattern is to use a static builder in the class of the fragment itself.
You're passing the same Bundle to two different fragments. Unless you know for a fact that's OK, try instead using a different Bundle for each one.
in showJSON, your AnotherFragment instance doesn't seem to be used at all
I don't see any place where changeFragment is called
You're passing string arrays in the Bundle, but you only ever use the first element in the array in the fragment. Consider just passing a single string instead?
My Xamarin Android app uses fragments for each view rather than separate activities, and one of the fragments needs to contain a ViewPager for a photo slider.
From everything I've seen, the ViewPager has to bind to an adapter that uses fragments. This is problematic because this means I have a fragment within a fragment, which has to use the ChildFragmentManager, which isn't supported in versions prior to Android 4.2. My app needs to work on Android 4.0+, so I need guidance on how to work around this issue.
Some lines of thought I had...
Is there a way to bind some other kind of adapter to a ViewPager that doesn't use fragments? In my case all I need is to show an ImageView as each item, so a fragment seems overkill and obviously has the 4.2+ issue.
Is there some other control that I could use besides ViewPager that would give me the desired UX (list of photos sliding left/right) without relying on child fragments?
Is there a way to make ChildFragmentManager work with Android 4.0 and 4.1?
Current code to setup the ViewPager with only Android 4.2+ support:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="200dip"
android:id="#+id/property_details_image_gallery" />
private ViewPager gallery;
gallery = view.FindViewById<ViewPager> (Resource.Id.property_details_image_gallery);
gallery.Adapter = new ImagePagerAdapter (ChildFragmentManager, 0, facts.PhotoUris);
gallery.SetOnPageChangeListener (this);
public class ImagePagerAdapter : FragmentPagerAdapter
{
private int resourceId;
private List<string> photos;
public ImagePagerAdapter (FragmentManager fragmentManager, int resourceId, List<string> photos) : base(fragmentManager)
{
this.resourceId = resourceId;
this.photos = photos;
if (photos.Count == 0)
photos.Add (string.Empty); // If there are no photos, we add a single photo that will result in a 'noimage' photo
}
public override Fragment GetItem (int position)
{
// Instantiate a new fragment
ImageGalleryFragment fragment = new ImageGalleryFragment (photos[position], position);
return fragment;
}
public override long GetItemId (int position)
{
return position;
}
public override int Count {
get {
return photos.Count;
}
}
}
I just started using Xamarin 3 days ago, so please be gentle. :)
I was able to get this working by having ImagePagerAdapter inherit directly from PagerAdapter.
public class ImagePagerAdapter : PagerAdapter {
private List<string> data;
private Context context;
public ImagePagerAdapter(Context context, List<string> data) {
this.context = context;
this.data = data;
}
public override Java.Lang.Object InstantiateItem(View collection, int position) {
// Create new image view
var uri = data [position];
var imageView = new ImageView(context);
var defaultImage = context.Resources.GetDrawable (Resource.Drawable.noimage);
imageView.SetUrlDrawable (uri, defaultImage);
// Add image view to pager
var viewPager = collection as ViewPager;
viewPager.AddView(imageView);
return imageView;
}
public override void DestroyItem(View collection, int position, Java.Lang.Object view) {
var viewPager = collection as ViewPager;
viewPager.RemoveView(view as View);
}
public override bool IsViewFromObject(View view, Java.Lang.Object obj) {
return view == obj;
}
public override IParcelable SaveState() {
return null;
}
public override void StartUpdate(View arg0) {
}
public override void FinishUpdate(View arg0) {
}
public override int Count {
get {
return data != null ? data.Count : 0;
}
}
}