I use MvxRecyclerView in Mvvmcross Support RecyclerView library
The problem is that MvxRecyclerView cannot be inflated in an Activity (but Fragment works well). It seems that MvxAndroidBindingContextHelpers.Current() return null in MvxRecyclerAdapter constructor.
Could you please tell me how to resolve this problem ?
Maybe OnCreate is too early in the lifecycle to get a context. You could try to do this in the OnCreateView method. Otherwise i would suggest to set your own instance of the MvxRecyclerAdapter where you put in the context in the constructor.
var recyclerView = view.FindViewById<MvxRecyclerView>(Resource.Id.my_recycler_view);
if (recyclerView != null)
{
recyclerView.Adapter = new MvxRecyclerAdapter((IMvxAndroidBindingContext)BindingContext);
}
Related
I would like to go from Fragment to an Activity by clicking on an item of Fragment's RecyclerView. RecyclerView adapter is made in separate class, so in that class in the part of setOnClickListener{} I have to write the Intent part.
holder.itemView.setOnClickListener{
val intent = Intent(MainActivity.getActivity(),DetailsActivtiy::class.java)
intent.putExtra("ImageUrl", uri)
MainActivity.getActivity().startActivity(intent)
}
The problem as you might already guessed is MainActivty.getActivity() .
I tried the approach from Fragment via onButtonClick instead of MainActivty.getActivity() I wrote only activity and it worked perfectly but it does not work in RecyclerView adapter because it is non activity class. How to solve my problem?
Best, Armen.
You don't actually need an Activity to launch an Intent, you need a Context. You can get the Context of the viewholder's itemView for example, like this:
holder.itemView.setOnClickListener {
val context = holder.itemView.context
val intent = Intent(context,DetailsActivtiy::class.java)
intent.putExtra("ImageUrl", uri)
context.startActivity(intent)
}
Alternatively, you could introduce a listener mechanism and notify the Fragment that contains the adapter that an item has been clicked, and have the Fragment launch the appropriate Intent.
I have a ListFragment which would show list of items via an ArrayAdapter, I'm trying to handle configuration change (Device Rotation) I feel passing activity context to Array Adapter might cause Memory Leak when Activity is restarted on rotation and ListFragment adapter is retained because i'm using setRetainInstance(true), can someone tell me if my understanding is true? If so what is the best way to handle this. And yes I don't want to null my adapter onDetach and reuse it once Fragment view is re-created.
public class DummyXListFragment extends RoboSherlockListFragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (adapter == null)
adapter = new DummyItemAdapter(getActivity(),
android.R.layout.simple_list_item_1, list);
}
The Fragment will be retained (and thus won't be garbage collected). The Fragment will hold a reference to the adapter, and the adapter holds a reference to the activity Context, so yes, I believe this will cause a memory leak.
A very simple solution would be to pass getActivity().getApplicationContext() to the adapter constructor instead.
Depending on what you are using the activity context for it may be possible to use the application context instead, but there are some circumstances where you may still require the activity context. You cannot, for instance, do a findViewById or display a toast/dialog with an application context.
If you must use the activity context, then I would add a method to your adapter for setting the context so you can set it (the context) to null on detach, then set it again when your fragment/activity is recreated.
Here's a good summary of the different context types and their capabilities:
http://www.doubleencore.com/2013/06/context/
In my OncreateView() set the adapter which is working when i am first loading the page. When i go to another page and make changes then come back to this fragment it is not working adapter.notifyDatasetchanged().
#Override
public void onStart() {
super.onStart();
groupItem.clear();
childItem.clear();
List<String> child_Category;child_Category=new ArrayList<String>();
groupItem = obj_Listdatabase.fetchcategory();
childItem.clear();
ListIterator<String> iterator = groupItem
.listIterator();
while (iterator.hasNext()) {
String categoryname = iterator.next();
child_Category = new ArrayList<String>();
child_Category = obj_Listdatabase
.fetchchildlist(categoryname);
childItem.add(child_Category);
}
adapter.notifyDataSetChanged();
}
Hai I got output for this one:
groupItem.addAll(obj_Listdatabase.fetchcategory());
Instead of This
//groupItem = obj_Listdatabase.fetchcategory();
Because i change the reference in assignment statement(=) for that adapter.So i use addAll() method to store the values only instead of reference.
You should reaaaally indent your code more cleanly, never use several ";" on the same line for example.
And you can definitely use BaseExpandableListAdapter the issu is somewhere else..
You should also notice that onCreate view is more suitable for "creating view" so all the adapter stuff should be somewhere else like onViewCreated, or onActivityCreated as you need (it's just some advices)
I assume your adapter is in a Fragment since you use GetActivity() and in the constructor of your adapter you pass a reference to the activity (context), the group list and the child list.. ok
we ll assume that you are using the fragment onStart. From the official doc :
Called when the Fragment is visible to the user. This is generally tied to Activity.onStart of the containing Activity's lifecycle.
So normally this method is called after onViewCreate, onViewCreated .. etc at least the first time you code is running. So this is ok
Did you try using adapter.notifyDataSetInvalidate() and then do adapter.notidyDataSetChanged ?
One last thing, since you actually pass the data to your adapter by the constructor, when you update your lists how can the adapter be aware of the changes ? Are your lists global (static) ?
If not, before doing adapter.notifyDataSetChanged() you should pass the lists (group and child) to the adapter with some setter..
good luck
I have a ListFragment that uses a header view. Both the header's contents and the list's are fetched from a background task. In order to not re-fetch the data on configuration changes, I am calling setRetainInstance and keeping the data on the fragment.
When the the configuration changes, the view is recreated, so it removes the header view that I previously populated. Since now I already have the data, I should just re-add the header view to the list.
Unfortunately when I try doing this... boom!
java.lang.IllegalStateException: Cannot add header view to list -- setAdapter
has already been called.
Apparently, even tho the view is destroyed and onCreateView is called again, the list's adapter is already set (or the state is retained), making it impossible to add the header view again.
How can I keep the ListView's header or redraw it without recreating the fragment on orientation changes?
This is intended behaviour, take a look at the Android source code here for guidance on API 17, but really any will do. The relevant part is:
Add a fixed view to appear at the top of the list. If addHeaderView is
called more than once, the views will appear in the order they were
added. Views added using this call can take focus if they want. NOTE:
Call this before calling setAdapter. This is so ListView can wrap the
supplied cursor with one that will also account for header and footer
views.
public void addHeaderView(View v, Object data, boolean isSelectable) {
if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
throw new IllegalStateException(
"Cannot add header view to list -- setAdapter has already been" +
"called."); // Edit: SK9 wrapped this.
}
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
// in the case of re-adding a header view, or adding one later on,
// we need to notify the observer
if (mAdapter != null && mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
The adapter is not null when you come to add the header again and an exception is being raised. To resolve your issue, something along the following lines will do just fine:
setListAdapter(null);
getListView().addHeaderView(mHeader);
setListAdapter(new MyAdapter(getActivity(), items));
I wouldn't even classify this as a workaround. I encountered the same problem and this worked for me.
Apparently footers are treated very differently, see here:
public void addFooterView(View v, Object data, boolean isSelectable) {
// NOTE: do not enforce the adapter being null here, since unlike in
// addHeaderView, it was never enforced here, and so existing apps are
// relying on being able to add a footer and then calling setAdapter to
// force creation of the HeaderViewListAdapter wrapper
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
info.isSelectable = isSelectable;
mFooterViewInfos.add(info);
// in the case of re-adding a footer view, or adding one later on,
// we need to notify the observer
if (mAdapter != null && mDataSetObserver != null) {
mDataSetObserver.onChanged();
}
}
it's a know issue, but you can resolve it like this:
add header before the set adapter and remove him
Yes, it's a known issue, but can be avoided with the proper approach.
It seems that a solution similar to your problem exists.
These guys found a workaround: setSelected in OnItemClick in ListView
Hope it helps ;)
In a class extending android.support.v4.app.FragmentPagerAdapter, is there any way to get access to the Context.getString(..) method without the extending class being an inner class of an activity or passing in some context from the outside?
Thanks for any hint!
From a fragment use :
getActivity().getString(...)
From an adapter use :
getContext().getResources().getString(...)
Yes, you need a context to access the resources.
From an Activity, use:
this.getString(R.string.string_name);
From a Fragment, use:
getActivity.getString(R.string.string_name);
From an adapter, use:
getContext().getResources().getString(R.string.string_name);
I had a similar issue. From a drawer layout, I wanted to decide which fragment to use in a method called from a helper class.
So in onCreateView...
String form = getResources().getStringArray(R.array.drawer_array)[i];
Context context = getActivity().getApplicationContext();
FragmentHelper fh = new FragmentHelper();
int myFragment = fh.getCurrentFragment(form,context);
And in public FragmentHelper()...
public int getCurrentFragment(String form, Context context){
...
context.getResources().getString(R.string.label_item1);
...
}
The trick being to add context in front of getResources(). Otherwise, my stack showed that the fragment was not attached to an activity.
Hope this helps someone.