SwipeRefreshLayout configuration - android

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

Related

RxJava Take operator is not working

What I want to achieve: I am loading data from some web service in recyclerView. I want to load first 10 data and display it in recyclerView. When User scrolls, call web service for another 10 data to display it.
What I have done: For above aim, I'm using .take operator of RxJava. But It seems not working for me or else I'm doing some mistake.
What issue I'm having: I'm getting all the data instead of first 5 data. There might be something that I'm missing it.
My code is like below.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
private RecyclerView recyclerView;
private ProgressDialog mProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
setupProgressDialog();
ApiEndPoints apiEndPoints = ApiClient.getClient().create(ApiEndPoints.class);
mProgressDialog.show();
apiEndPoints.fetchAllUsers()
.take(5)
.subscribeOn(Schedulers.io()) //Background Thread
.observeOn(AndroidSchedulers.mainThread()) //UI Thread
.subscribe(new Observer<List<Pojo>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Pojo> pojoList) {
recyclerView.setAdapter(new RVAdapter(pojoList));
Log.e(TAG, "List Size: " + pojoList.size());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
mProgressDialog.dismiss();
}
}
);
}
private void setupProgressDialog() {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("Loading . . . ");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setCancelable(false);
}
}
Ask me if anything required. Thanks in advance.
If you use take operator, It will emit first 5 items and than onCompleted function will trigger. So you need re observable source.
I found example application on github which is using rxjava for load more operation.
You receive a list, which is considered as an object. You need to iterate them, take first 5 and then get them back as a list:
apiEndPoints.fetchAllUsers()
// iterate users
.concatMap(new Func1<List<Pojo>, Observable<Pojo>>() {
#Override
public Observable<Pojo> call(List<Pojo> pojos) {
return Observable.from(pojos);
}
})
// take 5 from iteration
.take(5)
// put all 5 users back to a list
.toList()
.subscribeOn(Schedulers.io()) //Background Thread
.observeOn(AndroidSchedulers.mainThread()) //UI Thread
If you had an array (Pojos[] users) take() operator should work without iteration.

SQLite Operations in Android

I have worked with SQLite on android before but this is the first time I'm handling quite a lot of data.
When my activity is opened for the first time it downloads the data from the server. I use google's volley library so I assume I don't need to run it in an async task.
My issue however seems to be with saving the data into sqlite db, rows returned for that single request are between 500-1000. From that point on anytime new data is added on the server I update the app using FCM so no issue with that.
While saving all those records I want to display a circular progress bar and message to the user explaining to them what is happening however I have noticed that my app freezes for 10-15 seconds while the data is being saved. By freezes I mean the progress bar stops moving and activities components and views are not loaded until that process is done.
So my question now is this, what is the right way of doing this so it doesn't interfere with the UI thread allowing me to have the views loaded and a smoother progress bar animation before the operation has completed.
I tried using an async task but noticed no change in performance:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bus_schedules);
new SaveDataAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
}
private class SaveDataAsync extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
getSchedules();
return null;
};
}
protected void getSchedules() {
showProgress(true)
mScheduleReq.all(new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
JSONArray schedules = response;
for (int c = 0; c < schedules.length(); c++) {
JSONObject schedule = schedules.getJSONObject(c);
mSchedulesDat.save(new TravelSchedule(
schedule.getInt("id"),
schedule.getInt("company_id"),
schedule.getInt("route_id"),
schedule.getString("class"),
(float) schedule.getDouble("fare"),
schedule.getString("weekday"),
schedule.getString("reporting_time"),
schedule.getString("departure_time"),
schedule.getInt("by_bus"),
schedule.getInt("by_plane")
));
// Saving Extras
JSONArray extras = schedule.getJSONArray("extras");
for (int k=0; k < extras.length(); k++) {
JSONObject extra = extras.getJSONObject(k);
mExtrasDat.save(new Extra(
extra.getInt("id"),
schedule.getInt("id"),
extra.getString("category"),
extra.getString("name"),
(float) extra.getDouble("price"),
extra.getString("description"),
extra.getString("company")
));
}
// This will prevent fetching all data again from server the next time
mAppDat.save("initialised_travel_schedules", String.valueOf(true));
}
} catch (JSONException e) {
e.printStackTrace();
}
// Hide progress bar
showProgress(false);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// Hide progress bar
showProgress(false);
// Parse and display network error response
showNetworkResponse(mContext, error);
}
});
}
You are doing your work on the UI/main thread, which is blocking all other updates to the UI. Any heavy processing that needs to be done should be handled in the background, either with a loader, AsyncTask, or (preferably) RxJava.
If you decide to go the reactive/RxJava route, I would suggest trying out https://github.com/square/sqlbrite. This will allow you to subscribe/unsubscribe to your query and insert operations in the background and post results to the view asynchronously (Activity, Fragment, etc.) as data comes in.
The official docs have a pretty good overview about how threading works on Android if you want to dig deeper.

AsyncTask show loading progress and return a value jsoup

I'm working on a web application that will parse the site and load the news dynamically into the CardView. For now it works and does all the needed stuff. But it's not exactly what I want.
Here's a piece of my code to understand what I am talking about:
public class NewsPage extends ActionBarActivity {
List<NewCard> listNC = new ArrayList<NewCard>();
class NewsParser extends AsyncTask<Void,Void,List<NewCard>> {
Document doc;
List<NewCard> nc = new ArrayList<NewCard>();
ProgressDialog progressDialog;
#Override
protected void onPreExecute()
{
// progressDialog= ProgressDialog.show(NewsPage.this, "Parsing the site", "Please wait while the information is loading...", true);
};
#Override
protected List<NewCard> doInBackground(Void... params) {
try {
//some code skipped
nc.add(new NewCard(forHeader.html(), forDesc, URLforImg, forHeader.attr("href")));
}
} catch (IOException e) {
e.printStackTrace();
}
return nc;
}
protected void onPostExecute(String[] s) {
progressDialog.dismiss();//This method is being called out by new <class name>.execute();
//listNC = new ArrayList<NewCard>(nc);
}
}
In here I am retrieving article headlines for further opening.
This is my onCreate() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_page);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
try {
NewsParser np = new NewsParser();
np.execute();
listNC = np.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
NewsAdapter na = new NewsAdapter(listNC);
size = na.sizes;
recList.setAdapter(na);
I'm using my adapter to fill the cards with information and to handle onClick events.
My question is this:
I need to retrieve information from my AsyncTask in order to create a
list of cards (in this case I need an amount of cards) and I am not
sure I can go on without returning values. But it makes my app freeze
and not show any interface until the action is completed. How is it
better to handle? Maybe I need to make it all different? How do I
load news headlines separately (not all together but in order)? And
what kind of loop (I don't know how to call it correctly) do I need
to add news as they load (because my program doesn't work if it
doesn't have the list before doing UI stuff)?
I've tried to tell every detail from my code and if it's needed I might add my Adapter code too.
Your UI is freezing because your get() method in the try block is blocking waiting on the AsyncTask to complete. This defeats the purpose of even using the AsyncTask. Instead, create your Adapter before you kick off the AsyncTask and then in the onPostExecute() set the data for the adapter to be the new result and call the adapter's notifyDataSetChanged() method. This will cause the UI to pick up the changes.
Be careful with your use of AsyncTask or any other threading mechanism. They are not lifecycle aware, so if the onPostExecute() method has any strong references to the Activity or its inner member fields and tries to directly use them it could run into state exceptions.

update listview in my activity from service android

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.

refresh data in listview when data from server

I'm having a problem refresh the data in list view.
I get the data in the list from a server, and when I want to refresh the data I need to go to the server and receive the new data.
the notifyDataSetChanged() not helping and also the ListView.invalidateViews not helping.
when I rotate the device the list updated.
how can I load the list view in the same way the screen rotation do it?
This is the code on create that fill the list view.
thanks in advance.
query = new ParseQuery(PET_CLASS_NAME);
petListView.addHeaderView((View)getLayoutInflater().inflate(R.layout.header_row, null));
petDetailIntent = new Intent(getApplicationContext(), PetDetailActivity.class);
selectCityIntent = new Intent(this, CitiesActivity.class);
loadingIntent = new Intent(getApplicationContext(), LoadingActivity.class);
startActivityForResult(loadingIntent, LOADING_INTENT_CODE);
/*the user see list of pets that are still missing*/
query.whereEqualTo(PET_FOUNDED, false);
selectedCity = settings.getString("cityQuery", "");
if(selectedCity != ""){
query.whereEqualTo(PET_CITY, selectedCity);
}
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> list, ParseException e) {
if (e == null) { //objects retrieved well
petList.addAll(list);
//MyAdapter
adapter = new MyAdapter(
getApplicationContext(),
android.R.layout.simple_list_item_1,
R.id.tv_pet_name,
petList);
setListAdapter(adapter);
}
else{
toaster(getResources().getString(R.string.error_message_load_pets));
finish();
}
finishActivity(LOADING_INTENT_CODE);
}
});
Use a AsyncTask for loadData from Server. It will load it faster.
Try this out:
private class YourTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... s) {
//Here you have to make the loading / parsing tasks
//Don't call any UI actions here. For example a Toast.show() this will couse Exceptions
// UI stuff you have to make in onPostExecute method
}
#Override
protected void onPreExecute() {
// This method will called during doInBackground is in process
// Here you can for example show a ProgressDialog
}
#Override
protected void onPostExecute(Long result) {
// onPostExecute is called when doInBackground finished
**// Here you can for example fill your Listview with the content loaded in doInBackground method**
}
}
And than you just have to call this AsyncTask always if you loading content from your server:
new YourTask().execute("");
Try it out!.. Hope this helps..
When you rotate the device, the activity is actually started stopped and started and your initial request will be made again.
You should place your request code into a method and recall it yourself
Similar to what Rawkode mentioned, it doesn't seem like the code that actually does the work of retrieving data from the server is reusable (since it lives in onCreate()). Take a look at this diagram: http://developer.android.com/images/activity_lifecycle.png. As you can can see, the onCreate() method only gets executed once, unless the Activity is re-created (i.e. rotating screen).
Also, from the given code, there doesn't seem to be evidence of a refresh method either. How will users be able to refresh the data? Consider refactoring your code such that the work is done in a method that you can call later on (i.e. refreshData()) and then figure out a way in which you would like your users to refresh. For example, you can use either the ActionBar with a refresh ActionItem, or a menu option or even a button.

Categories

Resources