I have MainActivity which add two tabs namely "A","B" both tabs send some request to server tab "A" is a fragment and Tab "B" is also a fragment ,problem is that when MainActivity start both tab send server request,by default tab1 is visible to user but what I want when tab 2 is visible then tab2 send request to server.pls help me .
mainactivity code:-
m_TabLayout = (TabLayout) findViewById(R.id.tab_layout);// finding Id of tablayout
m_ViewPager = (ViewPager) findViewById(R.id.pager);//finding Id of ViewPager
m_TabLayout.addTab(m_TabLayout.newTab().setText("Deals"));// add deal listin tab
m_TabLayout.addTab(m_TabLayout.newTab().setText("Stories"));
m_TabLayout.setTabGravity(TabLayout.GRAVITY_FILL);// setting Gravity of Tab
CDealMainListingPager m_oDealMainScreenPager = new CDealMainListingPager(getSupportFragmentManager(), m_TabLayout.getTabCount());
m_ViewPager.setAdapter(m_oDealMainScreenPager);// adiing adapter to ViewPager
m_ViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(m_TabLayout));// performing action of page changing
m_TabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
m_ViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
tab2 code:-
private View m_Main;
private String m_szMobileNumber, m_szEncryptedPassword;
private CAdscenMediaDealStorage m_Item;
private static ListView m_ListView;// declaring Listview variable..
private String TAG = CAdscendMediaDeal.class.getSimpleName();
Integer pubId = 105639;
Integer aswallId = 6453;
private ArrayList<CAdscenMediaDealStorage> s_oDataset;// declaring Arraylist variable
private CADscendDealAdapter m_oAdapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
m_Main = inflater.inflate(R.layout.adscend_deal_list, container, false);
getDetail();
init();
return m_Main;
}
private void getDetail() {
CLoginSessionManagement m_oSessionManagement = new CLoginSessionManagement(getActivity());// crating object of Login Session
HashMap<String, String> user = m_oSessionManagement.getLoginDetails();// get String from Login Session
m_szMobileNumber = user.get(CLoginSessionManagement.s_szKEY_MOBILE).trim();// getting password from saved preferences..........
}
public void init() {
m_ListView = (ListView) m_Main.findViewById(R.id.dealList);
s_oDataset = new ArrayList<>();
m_ListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
String url = s_oDataset.get(position).getM_Link();
if (!url.startsWith("http://") && !url.startsWith("https://"))
url = "http://" + url;
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(myIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), "No application can handle this request."
+ " Please install a webbrowser", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
});
}
#Override
public void onResume() {
super.onResume();
getAdscendDeal();
}
public void getAdscendDeal() {
String url = "http://adsc.com/adwall/api/publisher/" + pubId + "/profile/" + aswallId + "/offers.json?subid1=" + m_szMobileNumber;
JSONObject jsonObject = new JSONObject();
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, jsonObject, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e(TAG, "Server Response::" + response);
try {
JSONArray post = response.optJSONArray("offers");
for (int i = 0; i < post.length(); i++) {
JSONObject obj = post.getJSONObject(i);
m_Item = new CAdscenMediaDealStorage();
m_Item.setM_szHeaderText(obj.getString("name"));
m_Item.setM_szsubHeaderText(obj.getString("description"));
m_Item.setM_szDealValue(obj.getString("currency_count"));
m_Item.setM_szImageView(obj.getString("creative_url"));
m_Item.setM_Link(obj.getString("click_url"));
s_oDataset.add(m_Item);
}
if (!s_oDataset.isEmpty()) {
m_oAdapter = new CADscendDealAdapter(getActivity(), s_oDataset);// create adapter object and add arraylist to adapter
m_ListView.setAdapter(m_oAdapter);//adding adapter to recyclerview
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Server Error::" + error);
}
});
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(jsonObjectRequest);
}
When you set the viewpager adapter, onCreateView of Tab2 will also be called. Hence it sends the request.
Override the Fragment method : setUserVisibleHint
public class MyFragment extends Fragment {
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Fragment is visible
// make request here
}
else {
// Fragment is not visible
}
}
}
View pager retain some pages either side so that on sliding page shows without loading. The offset limit of ViewPager is by default 0.
setOffscreenPageLimit(int limit) :
Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.
But setting viewpager.setOffscreenPageLimit(0) will not work
Warning : Requested offscreen page limit 0 too small; defaulting to 1
try to user this
So user this
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// load data here
}else{
// fragment is no longer visible
}
}
Related
I am trying to make a volley request to api's url. The problem is that data is fetched appropriately, but every time data set updates, the whole recycler view refreshes again and begins from the start; in the docs it is mentioned that use notifyDataSetChanged() as the last resort. How can it be avoided and what are the best practices for such tasks? Any design pattern that should be followed?
Here is the Fragment Code :-
public class PageFragment extends Fragment implements SortDialogCallback {
private static final String TAG = PageFragment.class.getSimpleName();
/**
* Unsplash API, By Default=10
*/
private static final String per_page = "10";
public static String order_By;
/**
* Unsplash API call parameter, By Default=latest
* Change it in Pager Fragment, based on Tab tapped
*/
RecyclerView recyclerView;
ImageAdapter imageAdapter;
GridLayoutManager layoutManager;
EndlessRecyclerViewScrollListener scrollListener;
FloatingActionButton actionButton;
FrameLayout no_internet_container;
Bundle savedInstanceState;
// Attaching Handler to the main thread
Handler handler = new Handler();
boolean shouldHandlerRunAgain = true;
private ArrayList<DataModel> model;
/**
* Handler is attached to the Main Thread and it's message queue, because it is the one who created it.
* <p>
* Handler is responsible for checking every second that are we connected to internet, and if we are, then :-
* 1. Then we remove empty view
* 2. Make the network call
* 3. Stop handler from posting the code again using shouldHandlerRunAgain variable
* 3.1 This is a kill switch otherwise handler will post the runnable again and again to the message queue, which will be executed as soon as it reaches the looper
* <p>
* Handler removeCallbacks is used to remove all the pending runnables in the Message Queue
*/
Runnable job = new Runnable() {
#Override
public void run() {
Log.d(TAG, "Thread run " + job.hashCode());
swapViews();
if (shouldHandlerRunAgain)
handler.postDelayed(job, HANDLER_DELAY_TIME);
}
};
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("ORDER_BY", order_By);
}
#Override
public void onResume() {
super.onResume();
if (handler != null)
handler.post(job);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "Starting Handler");
layoutManager = new GridLayoutManager(getContext(), 2);
scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
Log.w(TAG, "On load More Called with page number " + page);
loadDataUsingVolley(page, order_By);
}
};
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.search:
Toast.makeText(getContext(), "Async task", Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(getContext(), "Invalid Options", Toast.LENGTH_SHORT).show();
}
return true;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_page_fragment, menu);
}
private void swapViews() {
if (detectConnection(getContext()) == false) {
recyclerView.setVisibility(View.INVISIBLE);
actionButton.setVisibility(View.INVISIBLE);
no_internet_container.setVisibility(View.VISIBLE);
} else {
Log.d(TAG, "Removing callbacks from handler and stopping it from posting");
shouldHandlerRunAgain = false;
handler.removeCallbacks(job, null);
handler = null;
recyclerView.setVisibility(View.VISIBLE);
actionButton.setVisibility(View.VISIBLE);
no_internet_container.setVisibility(View.INVISIBLE);
if (savedInstanceState != null) {
loadDataUsingVolley(1, savedInstanceState.getString("ORDER_BY"));
} else {
order_By = "latest";
loadDataUsingVolley(1, order_By);
}
}
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, final Bundle savedInstanceState) {
this.savedInstanceState = savedInstanceState;
View view = inflater.inflate(R.layout.fragment_page, container, false);
actionButton = (FloatingActionButton) view.findViewById(R.id.sort_button);
actionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SortDialog sortDialog = new SortDialog();
sortDialog.setTargetFragment(PageFragment.this, 911);
sortDialog.show(getChildFragmentManager(), "sortfragment");
}
});
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
recyclerView.setHasFixedSize(true);
no_internet_container = (FrameLayout) view.findViewById(R.id.no_internet_container);
return view;
}
void setUpRecyclerView() {
if (imageAdapter == null)
imageAdapter = new ImageAdapter(getContext(), (model==null)?new ArrayList<DataModel>():model);
recyclerView.setAdapter(imageAdapter);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addOnScrollListener(scrollListener);
}
void loadDataUsingVolley(int page, String order_by) {
final ProgressDialog dialog = ProgressDialog.show(getContext(), "Wallser", "Loading");
RequestQueue requestQueue = Volley.newRequestQueue(getContext());
String URL = "https://api.unsplash.com/photos/?page=" + page + "&client_id=" + api_key + "&per_page=" + per_page + "&order_by=" + order_by;
Log.d(TAG, URL);
JsonArrayRequest objectRequest = new JsonArrayRequest(Request.Method.GET, URL, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray array) {
int len = array.length();
if (model == null)
model = new ArrayList<>();
for (int i = 0; i < len; i++) {
try {
JSONObject object = array.getJSONObject(i);
String id = object.getString("id");
JSONObject object1 = object.getJSONObject("urls");
String imageURL = object1.getString("regular");
JSONObject object2 = object.getJSONObject("links");
String downloadURL = object2.getString("download");
model.add(new DataModel(imageURL, downloadURL, id));
Log.d(TAG, downloadURL);
} catch (JSONException e) {
e.printStackTrace();
}
}
if (dialog != null) {
dialog.dismiss();
}
Log.d(TAG, model.size() + "");
setUpRecyclerView();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
dialog.dismiss();
Toast.makeText(getContext(), "" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
requestQueue.add(objectRequest);
}
/**
* marks a new network call to Unsplash API
* Thus, set model array list to null, to start fresh.
* as model is reset, ImageAdapter also needs to start fresh.
*
* #param order_by
*/
#Override
public void onDialogFinish(String order_by) {
model = null;
imageAdapter=null;
order_By = order_by;
loadDataUsingVolley(1, order_By);
}
}
I've found several questions about this, none of which help me. Each question relates to other functions and views I don't implement in my fragments, and the issue is not that I need to swap my method getting the FragmentManager to getChildFragmentManager() anywhere in my fragments, because I don't need to get a FragmentManager there.
I'm guessing that my issue stems from the fragments and not the FragmentTabHost in the main activity, but I am not really sure. At all. All I know is that when you page between tabs, the adapter content disappears, but not the fragment itself. All views are still functional, so the functionality of each fragment remains intact.
This issue popped up only after I added a tab change listener for when to initialize the adapter for my chat fragment.
Note that the content of the tabs is fine when they are first initialized, but when you return to the tab the content in the adapters empty. This means that the tab that is not initialized yet when the FragmentTabHost is created, the hidden tabs haven't been initialized yet, so they will still work the first time you page over to them.
Through debugging, I can see that this issue occurs when the transition happens, and all adapters will remain empty for the duration of the usage session. I put this snippit of code before the initial checks in my tabHost.setOnTabChangedListener call:
//Before paging back to an initialized tab for the first time, the adapters of the initialized tab is populated.
Log.d("test", "pre");
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//At this point, the adapter is empty.
Log.d("test", "post");
}
}, 50);
The two fragments are as follows:
public class GroupTasksFragment extends Fragment {
public ArrayAdapter<String> adapter;
private Context context;
public ListView taskListView;
public GroupTasksFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_group_tasks, container, false);
taskListView = (ListView) rootView.findViewById(R.id.tasksList);
adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new ArrayList<String>());
taskListView.setAdapter(adapter);
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
#Override
public void onDetach() {
super.onDetach();
}
}
public class GroupChatFragment extends Fragment{
public ArrayAdapter<String> adapter;
private Context context;
public ListView chatListView;
public GroupChatFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_group_chat, container, false);
chatListView = (ListView) rootView.findViewById(R.id.chatList);
adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new ArrayList<String>());
chatListView.setAdapter(adapter);
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
#Override
public void onDetach() {
super.onDetach();
}
}
The main activity with the FragmentTabHost (I have excluded methods that just take input and send content to PubNub):
public class GroupContentActivity extends AppCompatActivity {
private GroupChatFragment chatFrag;
private GroupTasksFragment taskFrag;
private FragmentTabHost tabHost;
private PubNub connection;
private String groupName;
private String nickName;
private boolean chatFragInitialized = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_content);
tabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
tabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
tabHost.addTab(tabHost.newTabSpec("tasks").setIndicator("Tasks"),
GroupTasksFragment.class, null);
tabHost.addTab(tabHost.newTabSpec("chat")
.setIndicator("Chat"), GroupChatFragment.class, null);
groupName = getIntent().getStringExtra("groupName");
nickName = getIntent().getStringExtra("nickName");
PNConfiguration config = new PNConfiguration();
config.setPublishKey(Constants.publishKey);
config.setSubscribeKey(Constants.subscribeKey);
connection = new PubNub(config);
tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
#Override
public void onTabChanged(String tabId) {
if (!chatFragInitialized && tabId.equals("chat")) {
chatFragInitialized = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
chatFrag = (GroupChatFragment) getSupportFragmentManager().findFragmentByTag("chat");
connection.history()
.channel(groupName)
.count(50)
.async(new PNCallback<PNHistoryResult>() {
#Override
public void onResponse(PNHistoryResult result, PNStatus status) {
for (PNHistoryItemResult item : result.getMessages()) {
final String[] sForm = item.getEntry().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "groupCreated":
chatFrag.adapter.clear();
break;
case "chat":
chatFrag.adapter.add(mCopy);
}
}
});
}
}
});
}
}, 50);
}
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
taskFrag = (GroupTasksFragment) getSupportFragmentManager().findFragmentByTag("tasks");
connection.history()
.channel(groupName)
.count(50)
.async(new PNCallback<PNHistoryResult>() {
#Override
public void onResponse(PNHistoryResult result, PNStatus status) {
for (PNHistoryItemResult item : result.getMessages()) {
final String[] sForm = item.getEntry().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "addTask":
if (taskFrag.adapter.getPosition(mCopy) < 0) {
taskFrag.adapter.add(mCopy);
}
break;
case "deleteTask":
if (taskFrag.adapter.getPosition(mCopy) >= 0) {
taskFrag.adapter.remove(mCopy);
}
break;
case "groupCreated":
taskFrag.adapter.clear();
break;
}
}
});
}
}
});
connection.addListener(new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
if (status.getCategory() == PNStatusCategory.PNUnexpectedDisconnectCategory) {
Toast.makeText(getApplicationContext(), "You were disconnected!", Toast.LENGTH_SHORT).show();
} else if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
pubnub.publish().channel(groupName).message("chat>>>><ADMIN> User '" + nickName + "' Connected").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult result, PNStatus status) {
}
});
}
} else if (status.getCategory() == PNStatusCategory.PNReconnectedCategory) {
Toast.makeText(getApplicationContext(), "You were reconnected!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
final String[] sForm = message.getMessage().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "chat":
if (chatFragInitialized) {
chatFrag.adapter.add(mCopy);
runOnUiThread(new Runnable() {
#Override
public void run() {
chatFrag.chatListView.setSelection(chatFrag.adapter.getCount() - 1);
}
});
}
break;
case "addTask":
taskFrag.adapter.add(mCopy);
connection.publish().channel(groupName).message("chat>>>><ADMIN> Task '" + mCopy + "' added.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
break;
case "deleteTask":
taskFrag.adapter.remove(mCopy);
connection.publish().channel(groupName).message("chat>>>><ADMIN> Task '" + mCopy + "' deleted.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
break;
}
}
});
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
}
});
connection.subscribe().channels(java.util.Collections.singletonList(groupName)).execute();
}
}, 100);
}
#Override
public void onDestroy(){
super.onDestroy();
connection.publish().channel(groupName).message("chat>>>><ADMIN> User '" + nickName + "' Logged Out.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
connection.disconnect();
Toast.makeText(getApplicationContext(), "Logged out", Toast.LENGTH_SHORT).show();
}
//More Methods
}
Also note that the issue is not that I need to store the FragmentManager instance, as that doesn't do anything.
I found my issue. It turns out that every time a fragment is paged to in the FragmentTabHost, it's createView method is called again, and only that method, so by setting the adapter in the fragment to empty in that view, which I thought was only at the start, I reset the adapter each time.
I fixed this by keeping the adapter content as an instance variable list object that I add or remove strings to/from when I want to change the adapter. DO NOT ALSO PUT THE STRINGS IN THE ADAPTER, updating the list is enough. The list will directly add it to the adapter.
Also note that if you set the initial content outside of the fragment, it may not show when the tabs are first initialized. Just be careful of your statement ordering and when things are called. Fragment construction is funky business.
Then, I set the adapter to whatever is in the list each time the createView method is called.
Some menu in the drawer will open Tab + ViewPager content. Each page (fragment) is list that its data is requested from server.
Every time I click that menu, I want the content will show the tab immediately even the data are still requested instead empty screen. I try to add progress bar in the TabFragment so the content will show a loading when preparing the ViewPager and the pagers' data. But, the content still show an empty screen without loading indicator. I found the problem is because the method to request data from server is called from each pager.
Should I move the method for requesting data to TabFragment?
My TabFragment class looks like:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
content = view.findViewById(R.id.content);
content.setVisibility(View.GONE);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
viewPager = (ViewPager) view.findViewById(R.id.pager);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar1);
farmerViewPagerAdapter = new FarmerViewPagerAdapter(getChildFragmentManager(), titles);
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(farmerViewPagerAdapter);
tabLayout.post(()->{
tabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < titles.length; ++i){
tabLayout.getTabAt(i).setIcon(icons[i]);
}
});
}
And here is fragment for each page (each page requests different data):
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.view = view;
emptyView = view.findViewById(R.id.emptyView);
emptyText = (TextView) view.findViewById(R.id.emptyTextView);
recyclerView = (RecyclerView) view.findViewById(R.id.list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter = new FarmerAdapter(data, getContext());
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(
new HorizontalDividerItemDecoration.Builder(getContext())
.showLastDivider()
.marginResId(R.dimen.divider_margin_left, R.dimen.divider_margin_right)
.build());
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(() -> {
if (!Util.isNetworkAvailable(getContext())) {
if (swipeRefreshLayout.isRefreshing()) swipeRefreshLayout.setRefreshing(false);
} else {
currentPage = 1;
loadData(); //method to request data from server
}
}
);
if (user != null) {
getDataFromLocal();
addToAdapter();
loadData();
}
}
(Ed)loadData method :
Observable<Response<List<Data>>> dataApi = request.getServerData(currentPage,
NUMBER_DATA_PER_PAGE,
token);
dataApi.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(responseData -> {
if (swipeRefreshLayout.isRefreshing()) swipeRefreshLayout.setRefreshing(false);
if (responseData.isSuccessful() && responseData.code() == 200) {
currentPage++;
adapter.add(responseData.body());
if (adapter.getItemCount() < 1) {
emptyText.setText("Empty");
emptyView.setVisibility(View.VISIBLE);
}
} else {
try {
JSONObject json = new JSONObject(responseFarmer.errorBody().string());
Toast.makeText(getContext(), json.getString("message"), Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}, error -> {
if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing())
swipeRefreshLayout.setRefreshing(false);
if (error != null && error.getLocalizedMessage() != null)
Toast.makeText(getContext(), error.getLocalizedMessage(), Toast.LENGTH_LONG).show();
});
This method is called from pager fragment.
Your AsyncTask has an empty doInBackground() body. That essentially makes it synchronous. Say you have this AsyncTask:
private class SetAdapterTask extends AsyncTask<Void,Void,Void> {
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void result) {
doPostExecute();
}
#Override
protected void onPreExecute() {
doPreExecute();
}
}
and you call this in your code like this:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
new SetAdapterTask().execute();
}
but since your AsyncTask doesn't do anything in background, the postExecute fires off right after the preExecute, making the whole thing equivalent to this:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
.....
doPreExecute();
doPostExecute();
}
In other words, you make your ProgressBar visible in preExecute and immediatelly after that you make it disappear in postExecute.
The right way to approach this would be moving the ProgressBar visibility settings to the AsyncTask you use to load your data, which is located somewhere in the loadData() I presume. As for the data loading itself, it's hard to say what is wrong without seeing the actual methods which load the data.
I am using Firebase for my apps back end and I am retrieving my data as excepted. After I retrieve my data, I am posting it by using otto bus and the code can be seen below.
#Subscribe
public void loadBrothers(ServiceCalls.SearchBrothersRequest request) {
final ServiceCalls.SearchBrothersResponse response = new ServiceCalls.SearchBrothersResponse();
response.Brothers = new ArrayList<>();
Firebase reference = new Firebase("my data's url here");
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int index = 0;
for (DataSnapshot brotherSnapchat : dataSnapshot.getChildren()) {
BrotherFireBase bro = brotherSnapchat.getValue(BrotherFireBase.class);
Log.i(LOG_TAG, bro.getName());
Log.i(LOG_TAG, bro.getWhy());
Log.i(LOG_TAG, bro.getPicture());
Log.i(LOG_TAG, bro.getMajor());
Log.i(LOG_TAG, bro.getCross());
Log.i(LOG_TAG, bro.getFact());
Brother brother = new Brother(
index,
bro.getName(),
bro.getWhy(),
bro.getPicture(),
bro.getMajor(),
bro.getCross(),
bro.getFact());
response.Brothers.add(brother);
index++;
}
bus.post(response);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Once the data is in my RecyclerView, I am to click an item and it's respective activity is to pop up in a custom activity dialog. However, since the activity is a dialog, you can see the RecyclerView reloading in the background. This does not happen when I do not retrieve the data from the internet. After a few clicks around, the app crashes due to an out of memory exception. Is there something I am missing?
Here is the activity where the recyclerView is found:
#Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_meet_a_brother, container, false);
adapter = new BrotherRecycleAdapter((BaseActivity) getActivity(),this);
brothers = adapter.getBrothers();
recyclerView =(RecyclerView) view.findViewById(R.id.fragment_meet_a_brother_recycleView);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(),3));
setUpAdapter();
bus.post(new ServiceCalls.SearchBrothersRequest("Hello"));
return view;
}
private void setUpAdapter(){
if(isAdded()){
recyclerView.setAdapter(adapter);
}
}
#Subscribe
public void onBrosLoaded(final ServiceCalls.SearchBrothersResponse response){
int oldBrotherLength = brothers.size();
brothers.clear();
adapter.notifyItemRangeRemoved(0, oldBrotherLength);
brothers.addAll(response.Brothers);
//Delete for Debug method...
adapter.notifyItemRangeChanged(0,brothers.size());
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
#Override
public void onBrotherClicked(Brother brother) {
Intent intent = BrotherPagerActivity.newIntent(getActivity(),brother);
Log.i(LOG_TAG,brother.getBrotherName() + " was Clicked");
startActivity(intent);
}
Just in case, here is also the activity that is started when a list item is clicked, it is a viewPager activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_brother_pager);
brothers = new ArrayList<>();
bus.post(new ServiceCalls.SearchBrothersRequest("Hello"));
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager = (ViewPager) findViewById(R.id.activity_brother_viewPager);
viewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
#Override
public Fragment getItem(int position) {
Brother brother = brothers.get(position);
return BrotherDetailsFragment.newInstance(brother);
}
#Override
public int getCount() {
return brothers.size();
}
});
}
#Subscribe
public void onBrosLoad(final ServiceCalls.SearchBrothersResponse response){
brothers.clear();
brothers.addAll(response.Brothers);
viewPager.getAdapter().notifyDataSetChanged();
Brother brother = getIntent().getParcelableExtra(BROTHER_EXTRA_INFO);
int brotherId = brother.getBrotherId();
for(int i=0;i<brothers.size();i++){
if(brothers.get(i).getBrotherId() == brotherId){
viewPager.setCurrentItem(i);
break;
}
}
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
public static Intent newIntent(Context context, Brother brother){
Intent intent = new Intent(context,BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO,brother);
return intent;
}
Any help is greatly appreciated thank you!
in public void onBrotherClicked(Brother brother) where RecyclerView resides, you call:
Intent intent = BrotherPagerActivity.newIntent(getActivity(),brother);
which will call
Intent intent = new Intent(context,BrotherPagerActivity.class);`
in newIntent of your viewPager activity.
This could be a recursive call.
try adding:
public static Intent newIntent(Context context, Brother brother){
Intent intent = new Intent(context,BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO,brother);
return intent;
}
in Activity where your RecyclerView resides. And call your ViewPage activity there.
-- UPDATE --
Call your viewpager activity (which is used to show Brother data) with the following code:
private void showBrotherData(Brother brother){
Intent intent = new Intent(this, BrotherPagerActivity.class);
intent.putExtra(BROTHER_EXTRA_INFO, brother);
this.startActivity(intent);
}
I found the answer! I changed my recyclerView to only updated if the size of the array was zero.
#Subscribe
public void onBrosLoaded(final ServiceCalls.SearchBrothersResponse response){
int oldBrotherLength = brothers.size();
Log.i(LOG_TAG, "Brother lists old size" + Integer.toString(oldBrotherLength));
if(oldBrotherLength ==0){
brothers.clear();
adapter.notifyItemRangeRemoved(0, oldBrotherLength);
brothers.addAll(response.Brothers);
//Delete for Debug method...
adapter.notifyItemRangeChanged(0,brothers.size());
} else{
return;
}
Log.i(LOG_TAG, Integer.toString(brothers.size()));
}
I don't know how good this solution is in terms of cleaness but it works for me. I hope this helps someone.
I am fetching data from json with Volley and populating RecyclerView with the parsed data but I ran into a bit of problem:
The call to get the items is in onCreate method, so the call is repeated each time the activity is recreated both from configuration changes and otherwise; hence the data is reloaded. So I found this answer that uses parcelables
and this article on Codepath (still on parcelables). After I have followed the instructions explicitly (or so I feel), there seems to be no change: the call to get data is repeated each time the activity is recreated.
FruitItems
public class FruitItems implements Parcelable {
private String fruit_title;
private String fruit_description;
private String fruit_image;
public String getFruit_title() {
return fruit_title;
}
public void setFruit_title(String fruit_title) {
this.fruit_title = fruit_title;
}
public String getFruit_description() {
return fruit_description;
}
public void setFruit_description(String fruit_description) {
this.fruit_description = fruit_description;
}
public String getFruit_image() {
return fruit_image;
}
public void setFruit_image(String fruit_image) {
this.fruit_image = fruit_image;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.fruit_title);
dest.writeString(this.fruit_description);
dest.writeString(this.fruit_image);
}
public FruitItems() {
}
protected FruitItems(Parcel in) {
this.fruit_title = in.readString();
this.fruit_description = in.readString();
this.fruit_image = in.readString();
}
public static final Parcelable.Creator<FruitItems> CREATOR = new Parcelable.Creator<FruitItems>() {
#Override
public FruitItems createFromParcel(Parcel source) {
return new FruitItems(source);
}
#Override
public FruitItems[] newArray(int size) {
return new FruitItems[size];
}
};
}
MainActivity
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
private final String KEY_POST_ITEMS = "fruititems";
//List of fruits
private List<FruitItems> mFruitItemsList;
//Views
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private ProgressDialog mProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate called");
//Initializing Views
recyclerView = (RecyclerView) findViewById(R.id.fruit_recycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_POST_ITEMS)) {
mFruitItemsList = savedInstanceState.getParcelableArrayList(KEY_POST_ITEMS);
} else {
//Initializing the fruitlist
mFruitItemsList = new ArrayList<>();
if (NetworkCheck.isAvailableAndConnected(this)) {
getData();
} else {
final Context mContext;
mContext = this;
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(R.string.alert_titl);
alertDialogBuilder.setCancelable(false);
alertDialogBuilder.setIcon(R.mipmap.ic_launcher);
alertDialogBuilder.setMessage(R.string.alert_mess);
alertDialogBuilder.setPositiveButton(R.string.alert_retry, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (!NetworkCheck.isAvailableAndConnected(mContext)) {
alertDialogBuilder.show();
} else {
getData();
}
}
});
alertDialogBuilder.setNegativeButton(R.string.alert_cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
alertDialogBuilder.show();
}
}
adapter = new FruitAdapter(mFruitItemsList, this);
recyclerView.setAdapter(adapter);
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putParcelableArrayList(KEY_POST_ITEMS, ArrayList<? extends Parcelable>))mFruitItemsList);
super.onSaveInstanceState(savedInstanceState);
}
//Getting json data
private void getData(){
Log.d(TAG, "getData called");
//Show progress dialog
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setCancelable(false);
mProgressDialog.setMessage(this.getResources().getString(R.string.load_fruit));
mProgressDialog.show();
//Creating a json request
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(ConfigFruit.GET_URL,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, "onResponse called");
//Dismissing the progress dialog
if (mProgressDialog != null) {
mProgressDialog.hide();
}
//calling method to parse json array
parseData(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
//Creating request queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
//Adding request to the queue
requestQueue.add(jsonArrayRequest);
}
//parsing json data
private void parseData(JSONArray array){
Log.d(TAG, "Parsing array");
for(int i = 0; i<array.length(); i++) {
FruitItems fruitItem = new FruitItems();
JSONObject jsonObject = null;
try {
jsonObject = array.getJSONObject(i);
fruitItem.setFruit_title(jsonObject.getString(ConfigFruit.TAG_POST_TITLE));
fruitItem.setFruit_description(jsonObject.getString(ConfigFruit.TAG_POST_DESCRIPTION));
//Parsing image
JSONObject fruitImage = jsonObject.getJSONObject("thumbnail");
fruitItem.setFruit_image(fruitImage.getString("url"));
} catch (JSONException w) {
w.printStackTrace()
}
mFruitItemsList.add(fruitItem);
}
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
}
}
I may not be a pro but I know that I have goofed somewhere in the codes above, else it should have worked.
Now, my question is where did I goof and how do I plug this mistake?
EDIT
I have edited the codes above to reflect the answer that I accepted. It works fine but there is still a problem.
I start Activity B from MainActivity. If I press the back-button in Activity B the data is saved but when I press the up-button, the getData is called again and the data is re-fetched.
Please, is there anyway around this?
You don't seem to have an onSaveInstanceState in your mainactivity. You need something like
#Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList(KEY_POST_ITEMS,mFruitItemsList) ;
}
In order to retain your data for the activity that is about to be destructed and the one that is being created, you need to override the onSaveInstance callback
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putParcelableArrayList(KEY_POST_ITEMS, (ArrayList)mFruitItemsList);
super.onSaveInstanceState(savedInstanceState);
}
NOTE: always remember to call the superclass.