Updating listview on fragment - android

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();

Related

RecyclerView is a null in fragment

I have MainActivity,which contain viewpager,which contain 2 fragments:
MainActivity
ViewPager viewPager = findViewById(R.id.viewPager);
MyPagerAdapter adapter = new MyPagerAdapter(this,
getSupportFragmentManager());
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.TabLayout);
tabLayout.setupWithViewPager(viewPager);
Fragment fragmentHour=adapter.getItem(1);
onChangeDayListener2= (OnChangeDayListener) fragmentHour;
I am also have interface for send data from main activity in fragment:
public interface OnChangeDayListener{
void OnChange(WeatherList list,int pos);
}
but when i try to work with this callback in interface my recycler adapter always is null:
HourWeatherFragment:
public class HourWeatherFragment extends Fragment implements MainScreen.OnChangeDayListener{
private RecyclerView recyclerView;
private AdapterHour adapter;
private ArrayList<Weather> days;
Context context;
static HttpClient httpClient;
public HourWeatherFragment() {
// Required empty public constructor
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
days=new ArrayList<>();
adapter = new AdapterHour(days,getActivity());
LinearLayoutManager LayoutManager = new LinearLayoutManager(getActivity().getBaseContext(),
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(LayoutManager);
recyclerView.setAdapter(adapter);
days.add(new Weather());
recyclerView.getAdapter().notifyDataSetChanged();
Log.d("recycler","adapter seted!");
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_hour_weather, container, false);
recyclerView = rootView.findViewById(R.id.hour_list);
httpClient=new HttpClient();
MyAsyncTask task=new MyAsyncTask();
// task.execute("lviv");
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context=context;
}
#Override
public void OnChange(WeatherList list,int pos) {
String date=list.getAverageWeather().get(pos).getDate();
ArrayList<Weather> weather=list.getByDate(date);
Log.d("ldata","size of weather for adding="+weather.size());
// days.add(new Weather());
if(adapter!=null){
adapter.notifyDataSetChanged();
Log.d("tag","first null!");}
else{
try{
recyclerView.getAdapter();}
catch (NullPointerException ex){
Log.d("tag","null!");//THERE ALWAYS NULL
}}
So,firstly creating activity,then i start load data and when data loaded i send data from MainActivity to HourWeatherFragment throught callback,
but when i receive data in fragment,my adapter is null,how i can fix it?
You can create a static singleton class to hold data and retrieve that data in the fragment.
public class DataHolder {
//create static variables here.
}
In your fragment simply retrieve the data and make it null after that.
Alternative solution
Another solution is to use Bundles. Follow the bundle solution here.
You must cast RecyclerView.
in your fragment and onCreateView method :
recyclerView = (RecyclerView) rootView.findViewById(R.id.hour_list);
I know this is an old question, but contrary to the accepted answer I think the issue is that the handle to the fragment's widget such as recyclerView is not available in the OnCreateView function.
The handles (or pointers if you wish) to the widgets are only guaranteed to exist in the OnViewCreated function.
This documentation has good description of the lifecycle of fragments:
https://guides.codepath.com/android/Creating-and-Using-Fragments
an extract is:
// This event is triggered soon after onCreateView().
// onViewCreated() is only called if the view returned from onCreateView() is non-null.
// Any view setup should occur here. E.g., view lookups and attaching view listeners.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView lv = (ListView) view.findViewById(R.id.lvSome);
lv.setAdapter(adapter);
}
Hope this helps.

Recyclerview Redraw itself When navigating to another recycler view in Tabbed Activity

So this is the AsyncTask that I run in onCreateView in the first fragment
class GetDatas extends AsyncTask<Void, Void, Void>
{
Collection<ProfileDataModel> enums = Collections.EMPTY_LIST;
#Override
protected Void doInBackground(Void... voids) {
try{
client = new OkHttpClient();
String response = OkHttpService.GET(client, webUrl);
Gson gson = new Gson();
Type type = new TypeToken<Collection<ProfileDataModel>>(){}.getType();
enums = gson.fromJson(response, type);
profileDataModels = enums.toArray(new ProfileDataModel[enums.size()]);
listAdapter = new ListAdapter(Arrays.asList(profileDataModels), getContext());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
profileDataModels = enums.toArray(new ProfileDataModel[enums.size()]);
listAdapter = new ListAdapter(Arrays.asList(profileDataModels), getContext());
recyclerView.setAdapter(listAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
}
}
On the third fragment I use dummy data that does not require fetching
public class MainFragment extends Fragment {
RecyclerView recyclerView;
LinearLayout linearLayout;
#Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_video_list_pages, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.video_list_recycler_view);
recyclerView.setHasFixedSize(true);
linearLayout = (LinearLayout) rootView.findViewById(R.id.video_list_adapter_rootframe);
ListAdapter adapter = new ListAdapter(
new String[] {
"mm","aa","sss","mm","aa","sss","mm","aa","sss","mm","aa","sss","mm","aa","sss"
}
);
recyclerView.setAdapter(adapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
return rootView;
}
}
Some of you maybe confused by the naming, but I like to keep it that way,
My Directory names
The problem is that when I swipe to the third fragment from the first fragment, the recycle view redraws itself. This doesn't happen when I go to the second fragment, which only contains a textview
Can someone explain why this happens?
ok whats happening is the fragment one is being destroyed when you go to the third fragment so to stop this functionality you have to override destroyItem and keep it blank, use it in your Activity class
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
and your problem will be solved and next time try to go through all methods of the class before posting a question.
Suppose you had 4 fragments in the Tabs it will keep only 2 and destroy rest to save memory you, it can also keep 3 if you have next and previous tab like number two if you have 3 tabs, you can override it by keeping destroyItem empty cheers!
Don't try to implement if you have scrolling 10-20 tabs your application might crash
You can do like this in OnCreate() when setting up the adapter
mAdapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
This way, the adapter will save its position in case the fragment gets destroyed and recreated.

Android - asynctask that fills an adapter for a listview

Basically the flow is like the following: when a fragment is created (or when the user swipes to refresh the layout), an AsyncTask is executed. The ASyncTask retrieves the info from a URL and setup an adapter which is then assigned to a ListView.
This is the code of the Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tasks_page, container, false);
mSwipeRefreshLayout = (SwipeRefreshLayout)
rootView.findViewById(R.id.list_container);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light, android.R.color.holo_red_light);
lv = (ListView) rootView.findViewById(android.R.id.list);
getTasks();
return rootView;
}
private void getTasks() {
new TasksRetriever(mSwipeRefreshLayout,tasksAdapter,lv).execute();
}
#Override
public void onRefresh() {
mSwipeRefreshLayout.setRefreshing(true);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
getTasks();
}
}, 1000);
}
The TaskRetriever constructor is:
public TasksRetriever(SwipeRefreshLayout srl, TasksAdapter ta, ListView lv) {
adpt = ta;
this.srl = srl;
this.lv = lv;
}
and postExecute is:
#Override
protected void onPostExecute(List<TasksItem> result) {
super.onPostExecute(result);
dialog.dismiss();
if (adpt == null) adpt = new TasksAdapter(result);
else adpt.setItemList(result);
lv.setAdapter(adpt);
adpt.notifyDataSetChanged();
srl.setRefreshing(false);
}
Not sure yet if it works but I was wondering if I'm on the right track because it doesn't look clean to me. On the other hand, I can't create the adapter and assign it to the ListView until I actually have data for it... Is this considering OK or am I doing it wrong?
I would set the list adapter up in the onCreate() method under your list view and make this a instance variable (not a local one) so that the Async task can access it as follows i.e.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
lv = (ListView) rootView.findViewById(android.R.id.list);
mAdpt = new TasksAdapter(null);
lv.setAdapter(mAdpt);
}
And then in your onPostExecute()
#Override
protected void onPostExecute(List<TasksItem> result) {
dialog.dismiss();
mAdpt.clear();
for( TaskItem ti : result )
{
mAdpt.add( ti );
}
mAdpt.notifyDataSetChanged();
srl.setRefreshing(false);
}
Also make sure you account for the null value being passed into your adapter in the getView() method.
Hope this helps.
you CAN create an empty adapter and fill it later - in AsyncTask
your onPostExecute() should look like:
#Override
protected void onPostExecute(List<TasksItem> result) {
dialog.dismiss();
if (adpt == null){
adpt = new TasksAdapter(result);
lv.setAdapter(adpt);
}else{
adpt.clear();
}
for( TaskItem ti : result ) adpt.add( ti );
adpt.notifyDataSetChanged();
srl.setRefreshing(false);
}

Why is my ListView not showing the new data?

my problem is, that I have a ListView and a ArrayAdapter. Now I create the adapter like this:
items = OtherClass.DataSet1;
adapter = new MyArrayAdapter(getActivity(), R.layout.mein_listitem, items);
then I want to change the items e.g:
items = OtherClass.DataSet2;
adapter.notifyDataSetChanged();
But the result is, that I still see the DataSet1 items in my ListView and I dont know why.
I have the following whole code:
public class PlaceholderFragment extends Fragment implements ActionBar.TabListener {
ArrayList<Item> items;
MyArrayAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
items = OtherClass.DataSet1;
adapter = new MyArrayAdapter(getActivity(), R.layout.mein_listitem, items);
{...}
}
#Override
public void onTabSelected(Tab arg0, FragmentTransaction arg1) {
if (arg0.getText().equals(getText(R.string.foo))) {
items = OtherClass.DataSet1;
adapter.notifyDataSetChanged();
} else {
items = OtherClass.DataSet2;
adapter.notifyDataSetChanged();
}
}
}
public class Item {
public Item(String foo1) {
super();
Foo1 = foo1;
}
public String Foo1;
}
You have to add your items on your ArrayAdapter object via the .add() or .addAll() methods. This will actually append the objects you've provided as parameters to the dataset already showing up.
adapter.add(...);
adapter.addAll(...);
And (as you done), don't forget always calling .notifyDataSetChanged() on your adapter after adding items to it.

ListView and PullToRefresh swipe down conflict

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);

Categories

Resources