I am working on a project which is totally based on Fragments with NavigationDrawer. Each fragment gets data from web services. A single fragment takes here so much time to load data. And one of all the fragments is a Fragment which has 10+ fragments using TabLayout and ViewPager and each fragment is showing data in a ListView and also takes so much time to load. I've tried many approaches and last approach is like:
public class CommodityFragment extends android.support.v4.app.Fragment implements SearchView.OnQueryTextListener {
private boolean isViewShown = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getView() != null) {
isViewShown = true;
} else {
isViewShown = false;
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.equity_activity, container, false);
act = this.getActivity();
return view;
}
public void onActivityCreated(Bundle savedInstanceState1) {
super.onActivityCreated(savedInstanceState1);
setHasOptionsMenu(true);
list = (ListView) view.findViewById(R.id.list_equity);
empty_text = (TextView) view.findViewById(R.id.empty);
if (Utils.isNetworkAvailable(getActivity())) {
if (catListDao.size() > 0) {
adapter = new AdvisorsAdapter(act,R.layout.custom_equity, catListDao);
list.setAdapter(adapter);
} else {
new Thread(new Runnable() {
#Override
public void run() {
// do some work here
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (!isViewShown) {
new FetchAllData(getActivity(), 2).execute();
}
}
});
}
}).start();
}
} else {
CustomToast toast = new CustomToast(getActivity(), "There is no internet connection!");
}
}
}
Calling execute() on an AsyncTask adds it to a que shared by all of your AsyncTask's. This means that each request to the server is made one at a time. By instead adding your task to a thread pool you can have many tasks executing at once.
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
For managing your network requests it would usually be better to use a library designed for doing so. I personally use Retrofit however others such as Volley, RoboSpice and OkHttp are a fine choice also.
I faced same problem. When working with tabLayout, each tabLayout was referring to a unique fragment. That created a lot overhead for the OS and when I Navigate to a tab, drawer's closing motion was cheesy.
Android library "Volley" was very helpful to decrease the time consuming api calling issue. Since it works on its own thread and it's own asynchronous queue.
Here is a example of how to use volley.
https://github.com/ogrebgr/android_volley_examples
I could provide the complete sample code but it is under confidential agreement.
I prefer you to use Retrofits concept.Retrofits Demo
Related
I'm working with an adapter that handles its own service call to update its items (not my adapter). This adapter defines an interface I can pass in to make callbacks to views to show/hide loading indicators. I want to show a Snacker when a call fails (example: no network connection). I've defined that method in the interface and added it to my Fragment, and I've updated the adapter to call that method when a service call fails.
#Override
public void OnLoaded(int count) {
}
#Override
public void OnStartLoading() {
showRefresh();
}
#Override
public void OnStopLoading() {
clearRefresh();
}
#Override
public void OnServiceFailure() {
showSnackBar(getString(R.string.error));
}
protected void showSnackBar(String message) {
View view = getView();
if (view != null && getUserVisibleHint()) {
snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE);
snackbar.setActionTextColor(ContextCompat.getColor(getContext(), R.color.accent_dark));
snackbar.setAction(getString(R.string.retry), v -> {
refreshLayout.setRefreshing(true);
onRefresh();
});
snackbar.show();
}
}
Unfortunately, the Snackbar doesn't show.
Here's the thing: OnServiceFailure() is being called from a thread. I've tried wrapping the call to showSnackBar() in activity.runOnUiThread(), but that doesn't seem to solve the problem. I've also debugged showSnackBar() and it IS finding view and view IS the root FrameLayout.
If I call showSnackBar() anywhere else from the main thread, everything works fine.
I'm not sure where to go from here.
When using an service or thread you can only show a snackbar on the main (UI) thread. for fragments there is a special function inside the view object to post your code to the main thread.
View view = getView();
view.post()
this send your code to the main thread and shows the SnackBar.
I'm populating a GridView wth an AsyncTask:
#Override
public void onLoad() {
super.onLoad();
//... set Loading view and actually get the data
adapter = new DepartmentGridAdapter(getActivity(), R.layout.gird_department_item_layout,
mSalesPresenter.getDepartments());
}
#Override
public void onDoneLoading() {
super.onDoneLoading();
gridView.setAdapter(adapter); // Populate the GridView
onShowTutorial(); // <--- This is where I need to get the firstChild.
}
After the AsyncTask is done, I just need to access the firstChild of the GridView:
#Override
public void onShowTutorial() {
if (gridView.getChildAt( gridView.getFirstVisiblePosition())!= null )
// ... doStuff
}
But the statement is Always null. I even call getCount for the GridView and it's not 0. The problem seems that the GridView childs are not accesible right away. If i Use a button to force the execution of the method onShowTutorial() after the UI is ready then I can access the first child.
I'm running out of ideas to trigger the method after the execution of the thread.
Any solution?
Try to post a runnable to be run when the gridView is done doing what it is currently doing (more info regarding View.post() here) :
#Override
public void onDoneLoading() {
super.onDoneLoading();
gridView.setAdapter(adapter); // Populate the GridView
// Tells the gridView to "queue" the following runnable
gridView.post(new Runnable() {
#Override
public void run() {
onShowTutorial(); // <--- This is where I need to get the firstChild.
}
});
}
Found it.
I used a ViewTreeObserver and it works:
gridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
onShowTutorial();
}
});
I am working on a network project and dynamically update info. I have a few questions about the SwipeRefreshLayout.
After the onRefresh() starts the icon won't stop spinning and will not disappear even when all the data is updated.
When I launch my app there is a white screen (while information is loading). If I try to make the refresh it will work but will load 2 copies of my data. Is there a way to force the onRefresh() method OR to disable it until my data is loaded?
How do I block all actions before the data is loaded?
Here is my code so that everyone understand what I am talking about:
Main Thread:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_page);
final SwipeRefreshLayout mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh);
mSwipeRefreshLayout.setOnRefreshListener((SwipeRefreshLayout.OnRefreshListener) new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
na.eraseList();
new NewsParser().execute();
}
});
mSwipeRefreshLayout.setColorScheme(R.color.red);
recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
na = new NewsAdapter();
recList.setAdapter(na);
new NewsParser().execute();
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
AsyncTask:
protected Void doInBackground(Void... params) {
try {
doc = Jsoup.connect("http://www.gamedev.net/page/index.html").get();
Element e = doc.getElementById("featured");
Elements es = e.getElementsByClass("article_content_inner");
for (Element el : es) {
Element forHeader = el.getElementsByTag("strong").first().getElementsByTag("a").first();
String URLforImg = el.getElementsByTag("img").first().attr("src");
String forDesc = el.getElementsByClass("article_desc").first().html();
forDesc = new String(forDesc.substring(forDesc.indexOf("</a>") + 7,forDesc.length()));
na.changeList(new NewCard(forHeader.html(), forDesc, URLforImg, forHeader.attr("href")));
runOnUiThread(new Runnable()
{
#Override
public void run() {
na.notifyDataSetChanged();
}
}
);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void params) {
}
}
After the onRefresh() starts the icon won't stop spinning and will not disappear even when all the data is updated.
Use SwipeRefreshLayout.setRefreshing(false)
When I launch my app there is a white screen (while information is loading). If I try to make the refresh it will work but will load 2 copies of my data. Is there a way to force the onRefresh() method OR to disable it until my data is loaded?
How do I block all actions before the data is loaded?
SwipeRefreshLayout.setEnabled(false)
You must not use a new thread within asynctask. Get rid of that first. Secondly, you should update your UI in your onPostExecute method, that is where you need to call na.notifyDataSetChanged();Also you need to call swipeLayout.setRefreshing(false);when you update/finish with the asynctask for disabling the loading/refreshing animation.
Regarding the white screen - why don't you put a loading spinner before you populate the list? Or populate the list using old data (that you have saved or preset in SharedPreferences ect.)
I having 2 classes ,
1.Activity class
2.Service class
I need to update my list view in my activity,when service got any updates.
Actually i trying like an chat application , My services always checking my db and if it got any new string , i need to update in my activity without rebuild the again only i need to refresh the list view. i found it will be manipulated using iBinder , But i don't how to use it. Can any one suggest me with some examples of code .
referred pages
You should use a Bound Service. I did the something similar in my application. Where upon clicking refresh, I invoke a service which gets data in background and updates the UI.
Check out my service here:
https://github.com/madhur/GAnalytics/blob/develop/src/in/co/madhur/ganalyticsdashclock/AnalyticsDataService.java
#Override
protected void onPostExecute(AnalyticsAccountResult result) {
super.onPostExecute(result);
App.getEventBus().post(result);
}
Activity:
https://github.com/madhur/GAnalytics/blob/develop/src/in/co/madhur/ganalyticsdashclock/MainActivity.java
#Subscribe
public void UpdateUI(AnalyticsAccountResult result) {
ProgressBar progressbar = (ProgressBar) findViewById(R.id.pbHeaderProgress);
LinearLayout spinnerLayout = (LinearLayout) findViewById(R.id.spinnerslayout);
TextView statusMessage = (TextView) findViewById(R.id.statusMessage);
switch (result.getStatus()) {
case STARTING:
statusMessage.setVisibility(View.GONE);
progressbar.setVisibility(View.VISIBLE);
spinnerLayout.setVisibility(View.GONE);
break;
case FAILURE:
statusMessage.setVisibility(View.VISIBLE);
progressbar.setVisibility(View.GONE);
spinnerLayout.setVisibility(View.GONE);
statusMessage.setText(result.getErrorMessage());
break;
case SUCCESS:
statusMessage.setVisibility(View.GONE);
progressbar.setVisibility(View.GONE);
spinnerLayout.setVisibility(View.VISIBLE);
if (result.getItems() != null)
{
this.acProfiles = result.getItems();
MyAdapter myAdapter = new MyAdapter(acProfiles, this);
listView.setAdapter(myAdapter);
UpdateSelectionPreferences();
if (result.isPersist() && acProfiles.size() > 0)
{
if (App.LOCAL_LOGV)
Log.v(App.TAG, "saving configdata");
try
{
appPreferences.saveConfigData(acProfiles, credential.getSelectedAccountName());
}
catch (JsonProcessingException e)
{
Log.e(App.TAG, e.getMessage());
}
}
}
break;
}
}
It would also helpful to use Otto library:
http://square.github.io/otto/
Let's suppose you have the activity class named MainActivity where you initialized your ListView with the adapter named listviewAdapter. Put this code inside MainActivity:
public static Handler UIHandler;
static {
UIHandler = new Handler(Looper.getMainLooper());
}
public static void runOnUI(Runnable runnable) {
UIHandler.post(runnable);
}
When you made changes to your listview data inside your service class, write this code to apply new data to the ListView:
MainActivity.runOnUI(new Runnable()
{
public void run()
{
try
{
MainActivity.listviewAdapter.notifyDataSetChanged();
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
Without more information I cannot provide any useful code examples, however I think what you may be looking for is a ListAdapter. A ListAdapter takes a listview and a dataset (in your case maybe an array of strings) and combines the 2. Whenever the dataset changes (in your case this would be when your service detects a new string and adds it to the array) you just call ListAdapter.notifyDataSetChanged() and the listview will be automatically updated with your new information.
Check http://developer.android.com/reference/android/widget/ArrayAdapter.html for more info on the specific ListAdapter you might use.
i have an rss feed that comes via an XML. There are several events that are returned with information about them. The events are returned with tags...for eg: ....info...
as soon as i encounter tag, i want to update the listview that i am using to show the events.
So the user does not see the loading progress dialog, rather he sees the events getting added to a list.
How do i do this.
thank you in advance.
Here's pseudo codeish example for one way of doing this using SAX parser;
// MyParserThread is assumed to be inner class of Activity here.
private class MyParserThread extends Thread implements MyParserObserver {
private MyParser mParser;
public MyParserThread() {
mParser = new MyParser();
mParser.setObserver(this);
}
public void run() {
try {
// load xml
mParser.parse(xml);
} catch (Exception ex) {
}
}
public void onMyParserEvent(final DataReceivedFromParsing data) {
runOnUiThread(new Runnable() {
public void run() {
// update data to your UI.
}
});
}
public void cancel() {
mParser.cancel();
}
}
And in your parser you're implementing ContentHandler
public void cancel() {
mCancelled = true;
}
public void startElement(....) {
if (mCancelled) {
// If you want to stop Thread from running, all you have to do
// is make parsing stop.
throw new SAXException("Cancelled");
}
....
}
And triggering parsing once your onCreate is called would be;
public void onCreate(...) {
...
mParserThread = new MyParserThread();
mParserThread.start();
...
}
Now this isn't perfect but hopefully gives some idea how to do Thread handling for this purpose. Fundamentally you just have start it, and adding 'cancel' functionality is somewhat more of a bonus - e.g. for cases in which Activity is destroyed while your Thread is running.