Handling orientation change with loaders and array adapters - android

I am using loaders to load data into a gridview . First let me show you some code:-
The Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle icic){
View v=inflater.inflate(R.layout.thegrid, container, false);
final GridView gv = (GridView)v.findViewById(
R.id.thegridview);
gv.setAdapter(upAd);//upAd is the array adapter initialized in oncreate of the fragment
gv.setOnItemClickListener(this);
Bundle args=new Bundle();
args.putString("page", String.valueOf(page));
getLoaderManager().initLoader(1, args, this);
return v;
}
#Override
public Loader<ArrayList<HashMap<String, String>>> onCreateLoader(int loaderId,Bundle thebundle) {
if(loaderId==1){
String page=thebundle.getString("page");
return new MyLoader(getActivity().getApplicationContext(),page);
}
return null;
}
#Override
public void onLoadFinished(Loader<ArrayList<HashMap<String, String>>> theLoader,ArrayList<HashMap<String, String>> data) {
upAd.addAll(data);
}
Now when the fragment is displayed , it shows a grid of 10 elements downloaded.Now when the screen orientation is changed, the loader behaves well and does not download the data again but it adds the same data again to the grid.. so i end up having a grid of 20 elements the same 10 repeated twice. Well i can call :-
upAd.clear()
in onLoadFinished but i want to avoid that.Could someone tell me what would I be doing wrong here.

You have 20 items because ArrayList holding data in upAd already has 10 items from past. Then you are calling upAd.addAll(data);. That means you are adding another 10 identical items to that array list. So the solution is to clear adapter.

use this line in your Activity declaration in menifiest file.
android:configChanges="orientation|keyboardHidden"

Related

How to populate a listview inside a fragment with data from another fragment?

I am trying to populate my Listview using the data from another fragment.
I am able to get the data from the other fragment, but when I try to make my listview object, it is returning null.
As a result, the app is crashing.
I am getting data from the user from one fragment and then calling a method from another fragment to pass the data. I am making my listview object and array adapter in the poplist() method of the second method. However, the app is crashing due to null pointer exception.
Please help.
public class WishListFragment extends Fragment {
ArrayList<String> wishListTitles = new ArrayList<String>();
View rootView;
ListView wishListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Context context = getActivity();
rootView = inflater.inflate(R.layout.fragment_wish_list, container, false);
return rootView;
}
// Another fragment gets the data from user as strings and calls this method
public void popList(String str1, String str2, String str3, Context context)
{
// LibModel is a pojo, I am using its constructor to set the values.
LibModel lm = new LibModel(str1,str2,str3);
Log.w("Title:",lm.getTitle());
Log.w("Author:",lm.getAuthor());
Log.w("Language:",lm.getLang());
wishListTitles.add(lm.getTitle());
// I get this on the log, so the ArrayList is made correctly
Log.w("ArrayList:",wishListTitles.get(0));
// The listView gives Null Pointer exception and crashes the app
wishListView = (ListView) rootView.findViewById(R.id.wl_lv);
ArrayAdapter<String> wishListAdapter = new ArrayAdapter<String>(context,
android.R.layout.simple_list_item_1,wishListTitles);
wishListView.setAdapter(wishListAdapter);
}
}
I have tried the following, but it does not work
Used getView method instead of rootView while making the Listview.
Tried to make the listview inside the onCreateView() method but then the listview object is null, I get null pointer.
I am unable to find a way to put the set the adapter for the listview as it is returning Null.
I would recommend having interface callback in the fragment with the data. Also keep a reference of the list view in the activity containing the fragment.
Upon callback, the interface function will be executed and you can update list view from there.

Recyclerview inside fragment inside viewpager notifydatasetchanged not working

I have a recyclerview inside a fragment and that fragment is inside a view pager.
notifydatasetchanged() is not working even though the same code works in other projects of mine. The only difference I can see is that it is inside a view pager now?.
This is how notify the change:
public void updateRecyler(){
// Clear the current array
alarmsFromSP.clear();
// Update the array (This is working the array size is changing)
alarmsFromSP.addAll(AlarmCollection.getAlarms(getActivity()));
// Notify the change
adapter.notifyDataSetChanged();
}
I create the recylerview in oncreate like this:
public class FragmentAllAlarms extends Fragment {
// Arraylist of the alarm data from the strings
ArrayList<Alarm> alarmsFromSP;
tabRefreshReceiver r;
RecyclerView rv;
AlarmsAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_all_alarms, container, false);
// Recycler
rv = (RecyclerView) v.findViewById(R.id.mainAlarmRecyclerView);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
// Get saved data for alarms from the shared preferences string
alarmsFromSP = AlarmCollection.getAlarms(getActivity());
System.out.println("StringIs"+alarmsFromSP.size());
// Adapter
adapter = new AlarmsAdapter(getActivity(), alarmsFromSP);
rv.setAdapter(adapter);
Is it possible I am losing reference to the array or is it somthing to do with the viewpager?
I have been stuck with this for two days now any help is greatly appreciated.
Thanks in advance
make sure that updateRecyler() is called,
and try to change adapter.notifyDataSetChanged(); to rv.getAdapter().notifyDataSetChanged();
this worked for me once.
I have faced the same problem, then I found a temporary solution, instead of using notifyDataSetChanged() use
recyclerView.setAdapter(adapter);
recyclerView.scrollToPosition(postion);

Android spinner acting weird on rotate

I have a fragement with various Spinners in it. To have these spinners display an initial (non selectable) value I'm using a custom arrayAdapter (SpinnerHintAdapter). The only important thing this class does is override the getCount() so the last item of the selection-array isn't displayed, this is where the initial value is stored.
This all works fine untill you rotate the device, then the spinners are set on the last normal value of the list for some reason, even though the Fragment class still thinks it's set on the initial value.
Anyone any clue why this happens and / or how to solve this problem?
Code samples:
fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_free_top_up, container, false);
Spinner pet = (Spinner) rootView.findViewById(R.id.pet);
SpinnerHintAdapter<CharSequence> petAdapter = SpinnerHintAdapter.createFromResource(getActivity(),
R.array.pet_array, android.R.layout.simple_spinner_item);
petAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
pet.setAdapter(petAdapter);
pet.setSelection(pet.getCount());
return rootView;
}
SpinnerHintAdapter:
#Override
public int getCount() {
int count = super.getCount();
// The last item will be the hint.
return count > 0 ? count - 1 : count;
}
example string-array
<string-array name="pet_array">
<item>Yes</item>
<item>No</item>
<item>(initial value)</item>
</string-array>
The activity is re-created when you rotate the device. You need to override onSavedInstanceState function, save your data in a Bundle and then use that data in onCreate again.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save your data
}
and then use that in your onCreate to restore your spinner.
Haven't found an answer to this specific problem but used another method to display an initial value which can be found here: https://stackoverflow.com/a/12221309/3453217
Any clarification as to what went wrong with my first attempt is still welcome.

Keep ListView scroll position and data when popping fragment stack

Here's my situation, I have 1 Activity, and 2 Fragments. When the Activity is started it loads a Fragment, let's call it list, into the view (in a FrameLayout filling the screen). List simply lists blogs posts which are retreived via an async HTTP call to out web api. When a blog is clicked in list is runs this method in the Activity
public void loadBlog(FragmentBlog.Blog theBlog) {
blog = theBlog;
FragmentBlogDetails d = new FragmentBlogDetails();
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_BLOG, theBlog);
d.setArguments(bundle);
fragmentManager.beginTransaction().replace(R.id.container, d).addToBackStack("Detail").commit();
}
Then the blog loads up and is displayed fine. Now, once I press the back key, my list Fragment calls onCreateView again and I have to run the async call again to populate my list, which reset the scroll to the top too. How can I keep my data for needing to be fetched again, and keep the view so the scroll stays where it is?
Here's my onCreateView method in the list Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("FragmentBlog", "onCreateView");
View view = inflater.inflate(R.layout.fragment_blog, container, false);
list = (ListView) view.findViewById(R.id.the_list);
list.setOnItemClickListener(this);
if (baa != null) {
list.setAdapter(baa);
}
text = (TextView) view.findViewById(R.id.my_text_view);
pb = (ProgressBar) view.findViewById(R.id.progressBar);
asyncForBlogs();
return view;
}
If you need any other bits of code let me know and I'll post them.
You need to save index of last item user clicked. Define a index variable in your fragment and when users clicked on an item store item index in it. Then save data in onSaveInstanceState Call back. You can save other data like this. But make sure the data you put inside bundle is not so big. Do not put any images or videos here!
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("index",index);
super.onSaveInstanceState(outState);
}
after saving your data you can access it in onCreate method. like this
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
index = savedInstanceState.getInt("index");
listView.smoothScrollToPosition(index);
}
}
smoothScrollToPosition scroll your list view to the last place you were been.

Updating an Android Fragment's ListView from the background

I've been searching for the past few days, but have had no success, so I'm posting this question in the hopes someone can answer it.
Setup (Using android-support-library-v4, revision 12):
I have 1 Main Activity with a ViewPager.
I have 1 Fragment Class, called DataFragmentClass which consists of a ListView.
This fragment class loads data off the internet asynchronously and displays it in the listview. However, this fragment class can load two types of data, called DataTypeA and DataTypeB.
What I do is create two instances of this fragment in my main activity like so:
Fragment[] mFragmentList = new Fragment[2];
mFragmentList[0] = DataFragmentClass.newInstance(Modes.DataTypeA);
mFragmentList[1] = DataFragmentClass.newInstance(Modes.DataTypeB);
I then pass this array to my ViewPager, which works fine.
After being created, the Fragments will begin to async load the data:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.genericlistview, container, false);
DataProvider mDataProvider = new DataProvider(this.getActivity());
mDataProvider.addListener(mDataResponseAdapter);
return view;
}
The first data load everything is fine - DataTypeA will load in Fragment1 and DataTypeB will load in Fragment2.
However, any subsequent loads result in the data 'spilling over'. All the data (Type A and B) is now loaded in Fragment2.
I've narrowed the problem down to the ListView updates.
What I do is:
Runnable updateListView = new Runnable(){
#Override
public void run() {
adapter.add(myNewData);
adapter.notifyDataSetChanged();
}
};
DataFragmentClass.this.getActivity().runOnUiThread(updateListView);
I think something goes wrong at these points:
When posting the runnable on the UI thread.
When the remote server returns the data.
Any suggestions at all would be really helpful. Thanks!

Categories

Resources