In the code below (shortened for clarity) when I rotate the device, the adapter variable receives null and gridView variable receives null.
Can anyone help me keep the adapter and gridView after rotating the device screen?
Thank.
(Sorry for bad english)
public class FlickrXmlFragment extends Fragment {
private GridView gridView;
private FlickrGridViewAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//...
View view = darkInflater.inflate(R.layout.fragment_flickr_xml,
container, false);
//...
gridView = (GridView) view.findViewById(R.id.flickr_gridView);
return view;
}
class FlickrPhotoSearchThread extends
AsyncTask<String, Void, List<FlickrImagem>> {
#Override
protected void onPreExecute() { //... process... }
#Override
protected List<FlickrImagem> doInBackground(String... params) { //process... }
#Override
protected void onPostExecute(List<FlickrImagem> result) {
//...
adapter = new FlickrGridViewAdapter(getActivity(), 0, result);
gridView.setAdapter(adapter);
adapter.notifyDataSetChanged();
//...
}
}
}
Handle the Orientation changes for your activity.
Please refer to this: http://developer.android.com/guide/topics/manifest/activity-element.html#config
Declare your activity like this:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="#string/app_name">
Related
I have a ListView that has a SwipeRefreshLayout implemented on it. What I am trying to do is update the ListView when the user swipes down. I've tried looking for different options but unfortunately I am unable to find a solution.
Would it be better to reload the Fragment or the onCreate function?
Here is my code
public class NewsTab extends Fragment implements OnRefreshListener{
final LinkedList<News> listnews = new LinkedList<News>();
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_tab, container, false);
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
newslist = (ListView) view.findViewById(R.id.displaynews);
populatelist(listnews);
adapter = new CustomAdapter(getActivity(),listnews);
newslist.setAdapter(adapter);
return view;
}
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
new StartNewsLoad().execute("");
}
private class StartNewsLoad extends AsyncTask<String, Void, String> {
//ProgressDialog pd = null;
protected String doInBackground(String... params) {
reloadNews();
loadTopNews();
//NewsTab.listnews = new LinkedList<News>();
return null;
}
protected void onPostExecute(String result) {
adapter = new CustomAdapter(getActivity(),listnews);
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
}
///ANSWER
I found the answer! I added the following line of code inside my costume adapter and called it outside as adapter.refresh();
public void refresh(LinkedList<News> newsList)
{
this.results = newsList;
notifyDataSetChanged();
}
Thank you everybody for your help!
First of all no need to create the adapter again in onPostExecute() as you have already initialized it in onCreateView().You simple need to notify the adapter of the changes in the "listnews" if it has changed.Make sure that your list actually changes before calling the notifyDataSetChanged().
Also please use debugger or log to check whether your onRefresh() is called or not.From the way i see, it should be overriden with #override annotation as it is the method of the interface implemented by your fragment(swipeRefreshLayout.setOnRefreshListener(this);).It should be like:
#Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
new StartNewsLoad().execute("");
}
In onCreateView() intialize your listview and swiperefereshLayout with listener and set your adapter to your listview like that --->
if(adapter==null&& listNews==null){
listnews=new ArrayList<object>();
adapter=new CustomAdapter(getActivity(),listnews);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();}
else{
listView.setAdapter(adapter);}
this way it also handle retaining the listData onOrientationchange.
and when you get api response or referesh listview with swipe layout,dont create new adapter just add listNews to your adapter and call adapter.notifyDataSetChanged();
I'm trying to parse a RSS feed and display it to the user via a Listview which is in a Fragment. The problem is that nothing shows up on the list and I get a few errors:
V/ErrorĀ ParsingĀ Data: java.io.IOException: Couldn't open http://feeds.bbci.co.uk/news/england/rss.xml
W/FragmentManager: moveToState: Fragment state for Thingy{3633e4e #1 id=0x7f0c0069} not updated inline; expected state 3 found 2
My Fragments class:
public class Thingy extends Fragment {
ListView mList;
ArrayAdapter<String> adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_main, container, false);
adapter = new ArrayAdapter<>(getActivity(), R.layout.basic_list_item);
mList = ((ListView) v.findViewById(R.id.listView));
new GetRssFeed().execute("http://feeds.bbci.co.uk/news/england/rss.xml");
return v;
}
private class GetRssFeed extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
adapter.add("Pizza Steak");
try {
RssReader rssReader = new RssReader(params[0]);
for (RssItem item : rssReader.getItems()) {
adapter.add(item.getTitle());
}
} catch (Exception e) {
Log.v("Error Parsing Data", e + "");
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
adapter.notifyDataSetChanged();
mList.setAdapter(adapter);
}
}
}
Please add the permission on the manifest
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<application ...
</manifest>
The problem may be in the URL that you are using where it isn't a valid RSS feed url,
so that, try to run your code by the this url: http://www.aljazeera.com/xml/rss/all.xml (for testing only).
I developed an app which fills a list. It works fine in the way I did it but I'm not conviced that I solved the problem in a recommended way. I read that you should override onActivityCreated in a Fragment and fill the list there instead of doing this in onCreateView. onCreateView should only be used to inflate static views. Is this true? If yes, how should these two methods look like in the end?
This is my Fragment class:
public class FragmentMain extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
List<MyItem> items = createListItems();
ListView listView = (ListView) view.findViewById(R.id.list);
MyListAdapter adapter = new MyListAdapter(view.getContext(), items);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(view.getContext(),
"Clicked " + position, Toast.LENGTH_LONG)
.show();
}
});
return view;
}
.
.
.
}
My MainActivity just adds the fragment:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentMain fm = new FragmentMain();
getFragmentManager().beginTransaction()
.add(R.id.fragment_main_container, fm).commit();
}
.
.
.
}
That is true to a certain extend only because onCreateView happens on the UI thread and you don't want anything slowing that down otherwise your UI will be slow and choppy. For example, in your fragment class you have a call to a method "createListItems()". I don't know how many items you're making but if it's a lot it could slow down your UI (especially if youre accessing a database and querying objects and so on). So you could do it in onActivityCreated but you could also use an AsyncTask. So your code would become something like this:
public class LoadListObjectsTask extend AsyncTask<Void, List<MyItem>, Void> {
private MyListAdapter myListAdapter;
private Context mContext;
public LoadListObjectsTask(Context context) {
this.mContext = context;
}
#Override
public void doInBackground(Void...params) {
//create your list objects here instead of on UI thread. This will run on a separate thread.
myListAdapter = new MyListAdapter(mContext, items);
return items; //return list of MyItems
}
//This is called when doInBackground is done. THIS WILL RUN ON THE UI THREAD So don't do
//anything slow here
#Override
public void onPostExecute(List<MyItem>...params //don't really need the list here//) {
listView.setAdapter(myListAdapter);
}
}
then in your fragment
public class FragmentMain extends Fragment {
private ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
List<MyItem> items = new ArrayList<MyItem>();
listView = (ListView) view.findViewById(R.id.list);
//new code
new LoadListObjectsTask(getActivity()).execute();
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(view.getContext(),
"Clicked " + position, Toast.LENGTH_LONG)
.show();
}
});
return view;
}
public void onResume()... {
//also add the task here so your content is reloaded on resume..
new LoadListObjectsTask(getActivity()).execute();
}
.
.
.
}
If you don't want to do this just make your List of MyItems a private field and move
List<MyItem> items = createListItems();
to onActivityCreated().
Hope that helps!
I have used #chrisbanes ActionBar-PullToRefresh with my project, now I'm facing a conflict between ListView and PullToRefresh swipe down gesture.
When the ListView is already scrolled and I want to get to the top by swiping down, PullToRefresh trigger a refresh action instead of swiping the list up.
PS :
Here is my source code without extra stuff.
public class TestFragment extends Fragment implements OnRefreshListener {
private PullToRefreshLayout mPullToRefreshLayout;
private TestAdapter testAdapter;
private ListView testListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.testActivity = (TestActivity)getActivity();
PullToRefreshLayout testLayout = (PullToRefreshLayout)inflater.inflate(R.layout.test_list_view, null);
this.testData = new ArrayList<Object>();
// Set List Adapter
this.testListView = new ListView(this.testActivity);
this.testData = getSearchResult();
this.testAdapter = new TestAdapter(this.testActivity, this.testData);
testListView.setAdapter(this.testAdapter);
testListView.setFocusableInTouchMode(false);
testLayout.addView(twitterListView);
return testLayout;
}
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewGroup viewGroup = (ViewGroup) view;
// As we're using a Fragment we create a PullToRefreshLayout manually
mPullToRefreshLayout = new PullToRefreshLayout(viewGroup.getContext());
// We can now setup the PullToRefreshLayout
ActionBarPullToRefresh.from(getActivity())
// We need to insert the PullToRefreshLayout into the Fragment's ViewGroup
.insertLayoutInto(viewGroup)
// all children are pullable
.allChildrenArePullable()
.listener(this)
.setup(mPullToRefreshLayout);
}
#Override
public void onRefreshStarted(View view) {
final TestAdapter adapter = this.testAdapter;
final TestActivity activity = this.testActivity;
final PullToRefreshLayout pull2Refresh = this.mPullToRefreshLayout;
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
/// refresh data source
getFragmentData(activity, true);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// Notify Program Adapter that data has changed
adapter.notifyDataSetChanged();
}
});
// Notify PullToRefreshLayout that the refresh has finished
pull2Refresh.setRefreshComplete();
}
}.execute();
}
}
I found the issue
It was my mistake, first i must load ListView from Layout by identifier
-- this.testListView = new ListView(this.testActivity);
++ this.testListView = (ListView)testLayout.findViewById(R.id.test_listview);
And remove the last line
-- testLayout.addView(twitterListView);
I have a class (shown below) which extends Fragment. Now i need my class to return a view to the Other class when i create an object. Everything is working fine but i need this class to do the stuff in a background thread.
I also used ASYNC task but i couldn't make an ASYNC task to return a view. can anyone help.??
MapFragment.java
public class MapsFragment extends Fragment
{
MapView map;
LayoutInflater inflater_;
ViewGroup container_;
View layout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
inflater_=inflater;
container_=container;
super.onCreate(savedInstanceState);
layout = inflater.inflate(R.layout.fragment_maps, container, false);
map = (MapView) layout.findViewById(R.id.mapView);
return (LinearLayout) layout;
}
}
I don't specifically know why you want to pass a View to an AsyncTask, but this is a basic approach:
public class MyAsync extends AsyncTask<Void, Void, Void> {
private View view;
public MyAsync(View view) {
this.view = view;
}
#Override
protected Void doInBackground(Void... params) {
// Do something, but not with view
return null;
}
#Override
protected void onPostExecute(Void v) {
// You can alter view here or in the other methods with UI access
view.setVisibility(View.GONE);
}
}
You can also use your own callbacks should this approach not apply to what you want to do.