I've been tried to search about this problem but I couldn't get any solution. I've got a problem about saving ListView items in fragment. The case is I have several fragments
let say FRAGMENT A, FRAGMENT B and FRAGMENT C, and FRAGMENT A has a listview, the item of the listview can be changed when user scroll (just like timeline in social media apps). When I change the fragment A, I used addToBackStack(null) method.
The problem is, how to save the listview and restore its if back button is pressed?
onSaveInstanceState() is never called because the Activity host is never killed, only change the fragment using this:
public void changeFragment(Fragment fragment, ColorDrawable colorDrawable){
Fragment newFrag = fragment;
ColorDrawable newColor = colorDrawable;
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_fragment, fragment);
ft.addToBackStack(backStateName);
ft.commit();
}
//to change the actionbar color
getSupportActionBar().setBackgroundDrawable(newColor);
}
here is my code trying to save and restore the listview:
@Override
public void onDestroyView() {
super.onDestroyView();
//try to save listview scroll
index = myList.getFirstVisiblePosition();
View v = myList.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - myList.getPaddingTop());
index = 1;
//try to save listview items
savedAdapter = adapter;
fromBackStack = true;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
myList.setSelectionFromTop(index, top);
myList.setAdapter(savedAdapter);
}
here the code I've done so far :
package com.ngapainya.ngapainya.fragment;
import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.net.ParseException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;
import com.ngapainya.ngapainya.R;
import com.ngapainya.ngapainya.activity.volunteer.ContainerActivity;
import com.ngapainya.ngapainya.adapter.HomeAdapter;
import com.ngapainya.ngapainya.fragment.child.DetailPostFragment;
import com.ngapainya.ngapainya.model.Home;
import java.util.ArrayList;
/**
* A simple {@link Fragment} subclass.
*/
public class HomeFragment extends Fragment implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener{
private FragmentActivity myContext;
private View myFragmentView;
private ArrayList<Home> filelist;
private ListView myList;
private HomeAdapter adapter;
private SwipeRefreshLayout swipeRefreshLayout;
//try save instance listview
boolean fromBackStack = false;
int index;
int top;
HomeAdapter savedAdapter;
@Override
public void onDestroyView() {
super.onDestroyView();
//try to save listview scroll
index = myList.getFirstVisiblePosition();
View v = myList.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - myList.getPaddingTop());
index = 1;
//try to save listview items
savedAdapter = adapter;
fromBackStack = true;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
myList.setSelectionFromTop(index, top);
myList.setAdapter(savedAdapter);
}
@Override
public void onAttach(Activity activity) {
myContext = (FragmentActivity) activity;
super.onAttach(activity);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);
filelist = new ArrayList<Home>();
//dummy data
filelist.add(new Home("post", 1)); //post image
filelist.add(new Home("post", 2)); //post location
filelist.add(new Home("post", 3)); //post url
filelist.add(new Home("post", 4)); //become friend with
filelist.add(new Home("post", 0)); //post status
swipeRefreshLayout = (SwipeRefreshLayout) myFragmentView.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
/**
* Showing Swipe Refresh animation on activity create
* As animation won't start on onCreate, post runnable is used
*/
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
//do something
swipeRefreshLayout.setRefreshing(false);
}
}
);
myList = (ListView) myFragmentView.findViewById(R.id.list_home);
//myList.setAdapter(new HomeAdapter(myContext, filelist));
adapter = new HomeAdapter(myContext, filelist);
myList.setOnItemClickListener(this);
if(!fromBackStack) {
new RemoteDataTask().execute();
}
return myFragmentView;
}
/**
* This method is called when swipe refresh is pulled down
*/
@Override
public void onRefresh() {
//do something
myList.setAdapter(adapter);
ArrayList<Home> new_filelist = new ArrayList<>();
new_filelist.add(new Home("Post", 4));
filelist.addAll(0, new_filelist);
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Home tmp = (Home) parent.getItemAtPosition(position);
DetailPostFragment detailPostFragment = new DetailPostFragment();
Bundle args = new Bundle();
switch (tmp.type) {
case 0:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 1:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 2:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 3:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 4:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
}
}
private class RemoteDataTask extends AsyncTask<String, Void, ArrayList<Home>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected ArrayList<Home> doInBackground(String... urls) {
try {
ArrayList<Home> new_filelist = new ArrayList<Home>();
new_filelist.add(new Home("post", 3)); //post url
new_filelist.add(new Home("post", 4)); //become friend with
new_filelist.add(new Home("post", 0)); //post status
filelist.addAll(new_filelist);
} catch (ParseException e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return filelist;
}
@Override
protected void onPostExecute(ArrayList<Home> organization) {
myList.setAdapter(new HomeAdapter(myContext, filelist));
// Create an OnScrollListener
myList.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view,
int scrollState) { // TODO Auto-generated method stub
int threshold = 1;
int count = myList.getCount();
if (scrollState == SCROLL_STATE_IDLE) {
if (myList.getLastVisiblePosition() >= count
- threshold) {
// Execute LoadMoreDataTask AsyncTask
new LoadMoreDataTask().execute();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
}
}
public class LoadMoreDataTask extends AsyncTask<String, Void, ArrayList<Home>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected ArrayList<Home> doInBackground(String... urls) {
try {
// web service request
ArrayList<Home> new_filelist = new ArrayList<Home>();
new_filelist.add(new Home("post", 0)); //post status
if(new_filelist != null && new_filelist.size() > 0 ){
filelist.addAll(new_filelist);
}
} catch (Exception e) {
e.printStackTrace();
}
return filelist;
}
@Override
protected void onPostExecute(ArrayList<Home> organization) {
int position = myList.getLastVisiblePosition();
adapter = new HomeAdapter(myContext, filelist);
myList.setAdapter(adapter);
adapter.notifyDataSetChanged();
myList.setSelectionFromTop(position, 0);
}
}
}
I hope someone could help me out, thank you .
Updated Code This is my code so far, it works when I try to saved the listview items and its adapter, perhaps this is a bit tricky. If someone has any idea would be appreciated.
package com.ngapainya.ngapainya.fragment;
import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.net.ParseException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;
import com.ngapainya.ngapainya.R;
import com.ngapainya.ngapainya.activity.volunteer.ContainerActivity;
import com.ngapainya.ngapainya.adapter.HomeAdapter;
import com.ngapainya.ngapainya.fragment.child.DetailPostFragment;
import com.ngapainya.ngapainya.model.Home;
import java.util.ArrayList;
/**
* A simple {@link Fragment} subclass.
*/
public class HomeFragment extends Fragment implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener{
private FragmentActivity myContext;
private View myFragmentView;
private ArrayList<Home> filelist;
private ListView myList;
private HomeAdapter adapter;
private SwipeRefreshLayout swipeRefreshLayout;
/*
* these variables used to restore the listview
* when calling the fragment from backstack
* */
private boolean fromBackStack = false;
private HomeAdapter savedAdapter;
private ArrayList<Home> savedFilelist;
@Override
public void onDestroyView() {
super.onDestroyView();
savedAdapter = adapter;
savedFilelist = filelist;
fromBackStack = true;
Log.e("onDestroyView", "onDestroyView");
}
@Override
public void onAttach(Activity activity) {
myContext = (FragmentActivity) activity;
super.onAttach(activity);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);
Log.e("onCreateView", "onCreateView");
swipeRefreshLayout = (SwipeRefreshLayout) myFragmentView.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
/**
* Showing Swipe Refresh animation on activity create
* As animation won't start on onCreate, post runnable is used
*/
/*swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
//do something
swipeRefreshLayout.setRefreshing(false);
}
}
);*/
myList = (ListView) myFragmentView.findViewById(R.id.list_home);
//myList.setAdapter(new HomeAdapter(myContext, filelist));
myList.setOnItemClickListener(this);
filelist = new ArrayList<Home>();
adapter = new HomeAdapter(myContext, filelist);
if(fromBackStack) {
filelist = savedFilelist;
adapter = savedAdapter;
}
new RemoteDataTask().execute();
return myFragmentView;
}
/**
* This method is called when swipe refresh is pulled down
*/
@Override
public void onRefresh() {
ArrayList<Home> new_filelist = new ArrayList<>();
new_filelist.add(new Home("Post", 4));
filelist.addAll(0, new_filelist);
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
Log.e("onRefresh", "onRefresh");
}
/*
* This method is called whenever the listview item is clicked
* */
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Home tmp = (Home) parent.getItemAtPosition(position);
DetailPostFragment detailPostFragment = new DetailPostFragment();
Bundle args = new Bundle();
switch (tmp.type) {
case 0:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 1:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 2:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 3:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
case 4:
args.putInt("postType", tmp.type);
detailPostFragment.setArguments(args);
((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
break;
}
}
/*
* This method is called in onCreateView method
* Used to load data from server and populate them into
* the listview
* */
private class RemoteDataTask extends AsyncTask<String, Void, ArrayList<Home>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected ArrayList<Home> doInBackground(String... urls) {
try {
ArrayList<Home> new_filelist = new ArrayList<Home>();
//dummy data
new_filelist.add(new Home("post", 1)); //post image
new_filelist.add(new Home("post", 2)); //post location
new_filelist.add(new Home("post", 3)); //post url
new_filelist.add(new Home("post", 4)); //become friend with
new_filelist.add(new Home("post", 0)); //post status
new_filelist.add(new Home("post", 3)); //post url
new_filelist.add(new Home("post", 4)); //become friend with
new_filelist.add(new Home("post", 0)); //post status
filelist.addAll(new_filelist);
} catch (ParseException e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return filelist;
}
@Override
protected void onPostExecute(ArrayList<Home> organization) {
myList.setAdapter(adapter);
// Create an OnScrollListener
myList.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view,
int scrollState) { // TODO Auto-generated method stub
int threshold = 1;
int count = myList.getCount();
if (scrollState == SCROLL_STATE_IDLE) {
if (myList.getLastVisiblePosition() >= count
- threshold) {
// Execute LoadMoreDataTask AsyncTask
new LoadMoreDataTask().execute();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
}
}
/*
* This method is called when the listview pulled down
* it will trigger this method to load more items from server
* */
public class LoadMoreDataTask extends AsyncTask<String, Void, ArrayList<Home>> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected ArrayList<Home> doInBackground(String... urls) {
try {
// web service request
ArrayList<Home> new_filelist = new ArrayList<Home>();
new_filelist.add(new Home("post", 0)); //post status
new_filelist.add(new Home("post", 1));
if(new_filelist != null && new_filelist.size() > 0 ){
filelist.addAll(new_filelist);
}
} catch (Exception e) {
e.printStackTrace();
}
return filelist;
}
@Override
protected void onPostExecute(ArrayList<Home> organization) {
int position = myList.getLastVisiblePosition();
//adapter = new HomeAdapter(myContext, filelist);
myList.setAdapter(adapter);
adapter.notifyDataSetChanged();
myList.setSelectionFromTop(position, 0);
}
}
}
Finally I figured it out... I don't know whether this is the best solution or not but it works so far for me. I only need one method and several variables, like this.
@Override
public void onDestroyView() {
super.onDestroyView();
savedAdapter = adapter;
savedFilelist = filelist;
fromBackStack = true;
Log.e("onDestroyView", "onDestroyView");
}
Thanks SO.