Progress Bar during the loading of a ListView - java

So, I want to display a spinning loading indicator while my ListView is being populated. I successfully have implemented the progress bar, BUT for some reason it disappears BEFORE all of the listings are displayed. What I want is the progressbar to be present during the TOTAL load time of the listings. Basically, what it seems like, each listing is being displayed one at a time, not all at once when they are all loaded.
What I'm doing is
1. Creating a new custom adapter class
2. Populating the ListView in an AsyncTask using this adapter class
3. Setting the ListView to this adapter
This works properly, the progress bar just disappears before all of the listings are displayed. Does anyone have any ideas?
Activity class:
public class MainActivity extends ActionBarActivity {
ArrayList<Location> arrayOfLocations;
LocationAdapter adapter;
// public static Bitmap bitmap;
Button refresh;
ProgressBar progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progress=(ProgressBar)findViewById(R.id.progressbar_loading);
// Construct the data source
arrayOfLocations = new ArrayList<Location>();
// Create the adapter to convert the array to views
adapter = new LocationAdapter(this, arrayOfLocations);
FillLocations myFill = new FillLocations();
myFill.execute();
refresh = (Button) findViewById(R.id.refresh);
refresh.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
startActivity(getIntent());
}
});
}
private class FillLocations extends AsyncTask<Integer, Void, String> {
String msg = "Done";
protected void onPreExecute() {
progress.setVisibility(View.VISIBLE);
}
// Decode image in background.
#Override
protected String doInBackground(Integer... params) {
String result = "";
InputStream isr = null;
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://afs.spotcontent.com/"); // YOUR
// PHP
// SCRIPT
// ADDRESS
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
isr = entity.getContent();
// resultView.setText("connected");
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
// convert response to string
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(isr, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
isr.close();
result = sb.toString();
} catch (Exception e) {
Log.e("log_tag", "Error converting result " + e.toString());
}
// parse json data
try {
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
final JSONObject json = jArray.getJSONObject(i);
try {
BitmapWorkerTask myTask = new BitmapWorkerTask(
json.getInt("ID"), json);
myTask.execute();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data " + e.toString());
}
return msg;
}
protected void onPostExecute(String msg) {
// Attach the adapter to a ListView
ListView listView = (ListView) findViewById(R.id.listView1);
// View header = (View) getLayoutInflater().inflate(
// R.layout.listview_header, null);
// listView.addHeaderView(header);
listView.setAdapter(adapter);
progress.setVisibility(View.GONE);
}
}
}
Adapter class:
public class LocationAdapter extends ArrayAdapter<Location> {
public LocationAdapter(Context context, ArrayList<Location> locations) {
super(context, R.layout.item_location, locations);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Location location = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_location, parent, false);
}
// Lookup view for data population
TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
TextView tvDetails = (TextView) convertView.findViewById(R.id.tvDetails);
TextView tvDistance = (TextView) convertView.findViewById(R.id.tvDistance);
TextView tvHours = (TextView) convertView.findViewById(R.id.tvHours);
ImageView ivIcon = (ImageView) convertView.findViewById(R.id.imgIcon);
// Populate the data into the template view using the data object
tvName.setText(location.name);
tvDetails.setText(location.details);
tvDistance.setText(location.distance);
tvHours.setText(location.hours);
ivIcon.setImageBitmap(location.icon);
// Return the completed view to render on screen
return convertView;
}
}

The reason for that behavior is that you are starting multiple threads.
FillLocations preExecute --> SHOW ProgressBar
BitmapWorkerTask_1 --> new thread
BitmapWorkerTask_2 --> new thread
...
BitmapWorkerTask_N --> new thread
FillLocations postExecute --> HIDE ProgressBar
BitmapWorkerTask_K --> continue execution
BitmapWorkerTask_K+1 --> continue execution
etc.
If you want the list to be displayed until it's all loaded, Simply make BitmapWorker's processing synchronous. If you still want to display the list right away but keep the spinner until it's all finished, then keep a counter in your activity and increase it in preexecute and decrease it in postExecute of BitmapWorker via a setter. Once the counter hits 0, remove hide the progressBar.
In activity:
private int asynchCounter = 0;
private void updateCounter(int delta){
asynchCounter+=delta;
if(asynchCounter<=0){
progress.setVisibility(View.GONE);
}else{
progress.setVisibility(View.VISIBLE);
}
}
And instead of BitmapWorkerTask use
class CountedBitmapWorkerTask extends BitmapWorkerTask {
protected void onPreExecute() {
super.onPreExecute();
updateCounter(1);
}
protected void onPostExecute(String msg) {
super.onPostExecute();
updateCounter(-1);
}
}

I had this exact problem, to solve it I had to write AsyncTask complete listener. Which sends a notification to UI thread, that data was loaded and it has to change something, in this case hide the ProgressBar.
This is the basic example of how this should look like. I am not sure this will work for you after you copy it to your project, but complete listener is what you need, so after studying this case you should be able to find a solution.
AsyncTaskCompleteListener.java - listener interface.
public interface AsyncTaskCompleteListener {
public void onTaskComplete();
}
LoadDataTask.java
class LoadDataTask extends AsyncTask<Object, Object, Object> {
/* Your object types according to your task. */
private AsyncTaskCompleteListener callback; // Callback field
public LoadDataTask(AsyncTaskCompleteListener cb){
this.callback = cb;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Object doInBackground(String... urls) {
/* Your task here */
return result;
}
#Override
protected void onPostExecute(Object o) {
callback.onTaskComplete(); // Set the Callback
}
}
MainActivity.java
public class MainActivity implements AsyncTaskCompleteListener{
/* ...Other methods and fields... */
/* onTaskComplete method which fires after your data is loaded. */
#Override
public void onTaskComplete(){
// Hide ProgressBar
}
}

Self Plug: https://github.com/horvste/EasyWebPageDownloadForAndroid
This would separate the threading from the implementation and solve your problem. This is very similar to what Tony suggested except it's already implemented for you.
Github Readme:
Good for connecting to REST API's, HTML parsing, and many other uses. Using this library is meant to be easy:
Create a class which implements OnProgressUpdate
public class SampleClass implements OnProgressUpdate {
#Override
public void onUpdate(Integer percentProgress) {
}
#Override
public void onUpdateFailure() {
}
#Override
public void onSuccess(StringBuilder result) {
}
#Override
public void onFailure() {
}
}
}
Instantiate DownloadWebPage object
DownloadWebPage webPage = new DownloadWebPage(new SampleClass(), myUrl);
Call .downloadHtml() from the DownloadWebPage
webPage.downloadHtml();
Also if the asynchtask is updating properly and the amount of items is to large look here:
listing a listview is taking too much time and memory in android
Another option would be to only list a certain amount of items then have a next page button or gesture to deal with the ListView loading too slow.

Related

Cannot get correct SharedPreferences value on onClick

I'm currently making a small app and I'm getting stuck on changing fragments using an onClick listener. I've searched the site and could find similar situations, but none of the proposed solutions worked.
So, when a user logs in, it sets a few values in SharedPreferences such as username, email and account level using a method from a class used to set and get SharedPreferences values. Afterwards, it should automatically redirect the user to a different Fragment. What's not happening, is redirecting the user to the other fragment.
I'm using AsyncTask for accessing the database. This is my code for the Login AsyncTask:
public class LoginSync extends AsyncTask <String, Void, String> {
AlertDialog dialog;
Context context;
String result;
JSONObject jObject;
String username, password;
String jEmail, jLevel;
public LoginSync(Context context){
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new AlertDialog.Builder(context).create();
dialog.setTitle("Login Status");
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(result.equals("login")) {
dialog.setMessage("Logged in successfully!");
}else{
dialog.setMessage("Failed to login! Please check username/password.");
}
dialog.show();
}
#Override
protected String doInBackground(String... voids) {
username = voids[0];
password = voids[1];
String connstr = "URL HERE";
try{
URL url = new URL(connstr);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("POST");
http.setDoInput(true);
http.setDoOutput(true);
OutputStream ops = http.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ops, StandardCharsets.UTF_8));
String data = URLEncoder.encode("username","UTF-8")+"="+URLEncoder.encode(username,"UTF-8")
+"&&"+URLEncoder.encode("password","UTF-8")+"="+URLEncoder.encode(password,"UTF-8");
writer.write(data);
writer.flush();
writer.close();
ops.close();
InputStream ips = http.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ips, StandardCharsets.ISO_8859_1));
String line;
while((line = reader.readLine()) != null){
jObject = new JSONObject(line);
result = "false";
if (jObject != null){
jEmail = jObject.getString("email");
jLevel = jObject.getString("account_level");
result = "login";
}
}
if(result.equals("login")) {
AppPreferences.setUserInfo(context.getApplicationContext(), username,jEmail,jLevel);
AppPreferences.setLoggedStatus(context.getApplicationContext(), true);
}
reader.close();
ips.close();
http.disconnect();
return result;
}catch (MalformedURLException e){
result = e.getMessage();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
}
Using the debugger, I see that the values are being set as intended in the SharedPreferences. However, in the onClick check on the Login Fragment, it's set to false until the onClick method ends.
This is my Login Fragment code:
public class LoginFragment extends Fragment {
private FragmentLoginBinding binding;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentLoginBinding.inflate(inflater, container, false);
View root = binding.getRoot();
binding.btnLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String user = binding.username.getText().toString().trim();
final String pass = binding.password.getText().toString().trim();
LoginSync login = new LoginSync(getActivity());
login.execute(user,pass);
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
}
});
binding.lnkRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_register);
}
});
return root;
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
On the first click, the values are set correctly, but checking the onClick with the debugger tells me that it's still false, after running the AsyncTask, and it doesn't trigger the fragment change in the if clause. On the second click, it changes the fragment.
What am I doing wrong? How can I make it change the fragment on the same click as it sets the information?
Thank you.
You are getting correct value from sharedPreference, only your timing to get that value is not correct. You are using async task, which works on a different thread. in your onCLick you have these lines:
LoginSync login = new LoginSync(getActivity());
login.execute(user,pass);
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
you must have assumed that your if statement will execute after your login async task is completed, but this will not happen, it will execute straight after starting the login process and will check the sharedPref before the value is even set. You are doing network call and IO operation which will take some time and shared pref should be checked after the async task has been completed. So yo should write your if statement in async class's onPostExecute method like this:
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(result.equals("login")) {
dialog.setMessage("Logged in successfully!");
if(AppPreferences.getLoggedStatusBool(getActivity()).equals(true)){
NavHostFragment.findNavController(getParentFragment()).navigate(R.id.action_nav_login_to_nav_home);
}
}else{
dialog.setMessage("Failed to login! Please check username/password.");
}
dialog.show();
}

No content displays when app runs on recyclerViewPager

This is the code I am Using.
public class MainActivity extends AppCompatActivity {
public ArrayList<String> ImageUrls = new ArrayList<>();
public ArrayList<String> ImageNames = new ArrayList<>();
public ArrayList<String> ImageDesc = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initImages();
}
private void initImages(){
final OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url("http://url.in/wp-json/wp/v2/posts?_embed")
.build();
#SuppressLint("StaticFieldLeak") AsyncTask<Void, Void, String> asyncTask = new AsyncTask<Void, Void, String>() {
private static final String TAG = "SlideFragment";
#Override
protected String doInBackground(Void... params) {
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
Log.d(TAG, "doInBackground: REsponse Un Successfull - 56");
return null;
}
String Data = response.body().string();
response.body().close();
return Data;
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "doInBackground: Exceptione on line63");
return null;
}
}
#Override
protected void onPostExecute(String Data) {
super.onPostExecute(Data);
if (Data != null) {
Log.d(TAG, "onPostExecute: line72");
try {
JSONArray json = new JSONArray(Data);
for (int i = 0; i < json.length(); i++) {
JSONObject post = json.getJSONObject(i);
String title = post.getJSONObject("title").getString("rendered");
String description = post.getJSONObject("content").getString("rendered");
String imgURL = post.getJSONObject("_embedded").getJSONArray("wp:featuredmedia").getJSONObject(0).getJSONObject("media_details").getString("file");
String imagUrl = "http://url.in/wp-content/uploads/" + imgURL;
ImageNames.add(title);
ImageDesc.add(description);
ImageUrls.add(imagUrl);
Log.d(TAG, "onPostExecute: " + ImageNames);
}
}catch(JSONException j){
j.printStackTrace();
Log.d(TAG, "onPostExecute: on line 121");
}
}
}
};
asyncTask.execute();
initRecycler();
}
private void initRecycler(){
RecyclerViewPager mRecyclerView = (RecyclerViewPager) findViewById(R.id.list);
// setLayoutManager like normal RecyclerView, you do not need to change any thing.
LinearLayoutManager layout = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(layout);
//set adapter
//You just need to implement ViewPageAdapter by yourself like a normal RecyclerView.Adpater.
RecyclerViewAdapter adapter = new RecyclerViewAdapter(ImageUrls, ImageNames, ImageDesc, this);
mRecyclerView.setAdapter(adapter);
}
}
I have run the same code with local data i..e the ArrayList with hardcoded data. It works. But If I try with API data It shows Nothing. I have checked the ArrayList with logging. It is fine.
I don't know where I am Wrong.
UPDATE
Thanks to #sonhnLab. In the code I have removed initRecycler(); from initImages(); and added to onPostExecute();. That worked.
Due to the asynchronous nature of Asynctask, the following line: "initRecycler();" doesn't necessarily gets called after completion of the network request hence no content. Remember, any task that depends on the asynchronous response needs to be implemented inside response method, in this case inside onPostExecute().
With the Help of sonhnlab I have successfully got the desired output.
I have made this initRecycler(); call into onPostExecute() call. so when the information is ready from the API call it initiates the Recycler.
I have Updating the Code in the question.
You should call initRecyler() onPostExecute when async task is completed

Can't read directly from URL using AsynTask

I am trying to develop an application that reads jokes from a URL. I am using an AsyncTask to read from URL and then put the string to a textView. But I can't figure out why it isn't working.
Here is my code:
public class MainActivity extends AppCompatActivity {
private Button oneJokeBtn, threeJokesBtn;
private final static String ERROR_TAG = "Download Error";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Capturing our buttons from the view
oneJokeBtn = findViewById(R.id.joke_1);
threeJokesBtn = findViewById(R.id.joke_3);
// Register the onClick listener
oneJokeBtn.setOnClickListener(buttonHandler);
threeJokesBtn.setOnClickListener(buttonHandler);
// Declaring the Spinner
Spinner spinner = findViewById(R.id.spinner);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.length_array, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter);
// Spinner onItemSelector implemented in the OnCreate Method
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
switch (position){
case 0:
Toast.makeText(parent.getContext(), R.string.short_toast, Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(parent.getContext(), R.string.medium_toast, Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(parent.getContext(), R.string.long_toast, Toast.LENGTH_SHORT).show();
break;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
/** AsyncTask that reads one joke directly from the URL and adds it to the textView */
private class Download1JokeAsyncTask extends AsyncTask <Void, Void, String> {
private ProgressDialog progressDialog;
private String mResponse = "";
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage(getString(R.string.progress_msg));
progressDialog.setIndeterminate(true);
progressDialog.show();
}
#Override
protected String doInBackground(Void... voids) {
String joke = null;
try {
// Open a connection to the web service
URL url = new URL( "http://www-staff.it.uts.edu.au/~rheise/sarcastic.cgi" );
URLConnection conn = url.openConnection();
// Obtain the input stream
BufferedReader in = new BufferedReader( new InputStreamReader(conn.getInputStream()));
// The joke is a one liner, so just read one line.
joke = in.readLine();
// Close the connection
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
Log.e(ERROR_TAG, "Exception: ", e);
mResponse = getString(R.string.fail_msg);
} catch (IOException e) {
e.printStackTrace();
Log.e(ERROR_TAG, "Exception: ", e);
mResponse = getString(R.string.fail_msg);
}
return joke;
}
#Override
protected void onPostExecute(String joke) {
TextView tv = findViewById(R.id.tv_joke);
if (joke == null) {
tv.setText(R.string.fail_msg);
}
else {
tv.setText(joke);
}
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
}
/** AsyncTask that reads three jokes directly from the URL and adds it to the textView */
private class Download3JokeAsyncTask extends AsyncTask<Void, Integer, String[]> {
private ProgressDialog mProgressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setProgress(0);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(100);
mProgressDialog.setCancelable(true);
mProgressDialog.setMessage(getString(R.string.three_jokes_btn));
mProgressDialog.show();
}
#Override
protected String[] doInBackground(Void... voids) {
int count = 2;
for (int i = 0; i < 2; i++){
try {
URL url = new URL("http://www.oracle.com/");
URLConnection conn = url.openConnection();
// Obtain the input stream
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// The joke is a one liner, so just read one line.
String joke;
while ((joke = in.readLine()) != null) {
System.out.println(joke);
}
// Close the connection
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
Log.e(ERROR_TAG, "Exception: ", e);
} catch (IOException e) {
e.printStackTrace();
Log.e(ERROR_TAG, "Exception: ", e);
}
publishProgress((int) ((i / (float) count) * 100));
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
setProgress(0);
}
#Override
protected void onPostExecute(String[] strings) {
super.onPostExecute(strings);
}
}
/** onClickListener that gets the id of the button pressed and download jokes accordingly */
OnClickListener buttonHandler = new OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.joke_1:
new Download1JokeAsyncTask().execute();
break;
case R.id.joke_3:
new Download3JokeAsyncTask().execute();
break;
}
}
};
The AsyncTask is called Download1JokeAsyncTask, it is supposed to read from URL and then put it into a text view. and I've put an error message to appear in the text view if the joke (the string where the joke is stored) is null.
And always the text view says that it failed to download a message.
Please help.
I went to your joke page and inspecting the source (in Firefox) and I found this:
<html>
<head>
<link rel="alternate stylesheet" type="text/css" href="resource://content-accessible/plaintext.css" title="Wrap Long Lines">
</head>
<body>
<pre>I'm really good at stuff until people watch me do that stuff.</pre>
</body>
</html>
So you could save the whole output as a String and then use this:
string.substring(string.indexOf("<pre>"), string.indexOf("</pre>");
string.substring(4);
Basically you are downloading only the first line of the page which would be the content declaration.
Instead you need to download the sixth line and remove the pre tags.
Good Luck!

Android - Send HTTP Request with AsyncTask on Button Click | Problems with delegate

i am about to write a small application. On button click i send a http request in a custom async task class. I want to write this value in a EditText field and in a ListView as item. My problem now is that i want to return the value of the request to the main thread to process it further. I searched around and found a method with an interface. This is my asynctask class:
public class Request extends AsyncTask<String,Void,String> {
public AsyncResponse delegate=null;
private MainActivity mAct;
public Request(MainActivity mainActivity){
this.mAct = mainActivity;
}
#Override
protected String doInBackground(String... url){
String returnString = "";
try {
URL u = new URL(url[0]);
final HttpURLConnection connection = (HttpURLConnection)u.openConnection();
BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
byte[] content = new byte[1024];
int bytesRead = 0;
String strContent = "";
while((bytesRead = bis.read(content)) != -1){
strContent += new String(content,0,bytesRead);
}
returnString = strContent;
} catch (Exception e){
} finally {
return returnString;
}
}
protected void onPostExecute(String result){
delegate.processFinish(result);
}
}
And this is my MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button btnSend = (Button)findViewById(R.id.btnSendMessage);
final ListView lv = (ListView)findViewById(R.id.treeView);
final EditText editText = (EditText)findViewById(R.id.txtReqID);
final MainActivity ma = this;
final ArrayList<String> arrList = new ArrayList<String>();
final ArrayAdapter<String> arrAdapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.simple_list_item_1,arrList);
btnSend.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
String t = new String("http://myhttprequest");
Request r = new Request(ma);
public void onCreate(Bundle savedInstanceState){
r.delegate = this;
}
editText.setText(returnValue);
lv.setAdapter(arrAdapter);
arrList.add(returnValue);
arrAdapter.notifyDataSetChanged();
}
});
}
public interface AsyncResponse{
void processFinish(String output);
}
The problem is that i have to declare every variable as final because i acces them within a function. I don't feel very happy with my code now and i also have no idea how i can make this work. Any help is very much appreciated.
Best regards
Try this way:
btnSend.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
String t = new String("http://myhttprequest");
Request r = new Request(ma){
protected void onPostExecute(String result){
editText.setText(result);
lv.setAdapter(result);
arrList.add(result);
arrAdapter.result();
}
};
}
});
What you should do I pretty simple.
Create an AsyncTask constructor that takes "delegate" as param
1.1 In case the "delegate" is an Activity (it is in your case) just make sure it's hold in a WeakReference (to avoid memory leaks)
Do you thing with http
When you want to dispatch the callback, just use your "delegate" param (check for null - as it is a WeakReference).
Cheers!

Loop AsyncTask to fetch JSON and store as object in same list

I want to read and store all JSON values from this api Link with get request "Mini" as example (which is actually an user input variable) and the last number is the page your are viewing. Every page can hold a max of 50 results. The same link is also in XML format (I must read and store as JSON, this is for easier understanding)
In this exmaple there are 8 pages with a total of 359 results. I need to loop through all pages and add all the JSON values to the same object list.
I have the code which work to read one page. I do not know how to make it loop through all pages and add to same object list.
In the acitivty.java onCreate I call the AsyncTask.
String userSearchRequest = search_activity_data.getString("userSearchRequest");
int page = 0;
String spidy_iTN_url = "http://www.gw2spidy.com/api/v0.9/json/item-search/" + userSearchRequest + "/" + page;
itemsByInput_AsyncTask itemsByInput_AsyncTask = new itemsByInput_AsyncTask();
itemsByInput_AsyncTask.setItemListToListings(this);
itemsByInput_AsyncTask.execute(spidy_iTN_url);
This is my AsyncTask class called itemsByInput_AsyncTask.java
import constructors.itemResults_api_constr;
import constructors.itemRoot_api_constr;
public class itemsByInput_AsyncTask extends AsyncTask<String, Void, JSONObject> {
JSONObject Jo_result;
private itemListToListings itemListToListings;
public void setItemListToListings (itemListToListings itemListToListings) {
this.itemListToListings = itemListToListings;
}
#Override
protected JSONObject doInBackground(String... params) {
return spidyHttpGetRequest(params[0]);
}
public JSONObject spidyHttpGetRequest(String URL){
try {
HttpGet get = new HttpGet(URL);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);
Jo_result = new JSONObject(result);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return Jo_result;
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
this.itemListToListings.itemListToListings(JoToJO_constructor(jsonObject));
}
public itemRoot_api_constr JoToJO_constructor(JSONObject Jo_result) {
itemRoot_api_constr spidy_iTN_rootO = new itemRoot_api_constr();
try {
spidy_iTN_rootO.setCount(Jo_result.getInt("count"));
spidy_iTN_rootO.setPage(Jo_result.getInt("page"));
spidy_iTN_rootO.setLast_page(Jo_result.getInt("last_page"));
spidy_iTN_rootO.setTotal(Jo_result.getInt("total"));
JSONArray list = new JSONArray(Jo_result.getString("results"));
for (int i = 0; i < spidy_iTN_rootO.getCount(); i++) {
JSONObject resultsObject = list.getJSONObject(i);
itemResults_api_constr spidy_iTN_resultsO = new itemResults_api_constr();
spidy_iTN_resultsO.setData_id(resultsObject
.getInt("data_id"));
spidy_iTN_resultsO.setName(resultsObject
.getString("name"));
spidy_iTN_resultsO.setRarity(resultsObject
.getInt("rarity"));
spidy_iTN_resultsO.setRestriction_level(resultsObject
.getInt("restriction_level"));
spidy_iTN_resultsO.setImg(resultsObject
.getString("img"));
spidy_iTN_resultsO.setType_id(resultsObject
.getInt("type_id"));
spidy_iTN_resultsO.setSub_type_id(resultsObject
.getInt("sub_type_id"));
spidy_iTN_resultsO.setPrice_last_changed(resultsObject
.getString("price_last_changed"));
spidy_iTN_resultsO.setMax_offer_unit_price(resultsObject
.getInt("max_offer_unit_price"));
spidy_iTN_resultsO.setMin_sale_unit_price(resultsObject
.getInt("min_sale_unit_price"));
spidy_iTN_resultsO.setOffer_availability(resultsObject
.getInt("offer_availability"));
spidy_iTN_resultsO.setSale_availability(resultsObject
.getInt("sale_availability"));
spidy_iTN_resultsO.setSale_price_change_last_hour(resultsObject
.getInt("sale_price_change_last_hour"));
spidy_iTN_resultsO.setOffer_price_change_last_hour(resultsObject
.getInt("offer_price_change_last_hour"));
spidy_iTN_rootO.addObject(spidy_iTN_resultsO);
}
} catch (JSONException e) {
e.printStackTrace();
}
return spidy_iTN_rootO;
}
public interface itemListToListings {
public void itemListToListings(itemRoot_api_constr resultClass);
}
}
And finally in my activity.java i can use my object in the method itemListToListings().
How can I make this loop through all pages (last_page property) and add all JSON values as object in the same list.
EDIT: My itemListToListings function in my activity.
public void itemListToListings(final itemRoot_api_constr spidy_iTN_construct) {
ArrayList<listItemWidgets_constr> image_details = getListData(spidy_iTN_construct);
final ListView lv1 = (ListView) findViewById(R.id.listView);
lv1.setAdapter(new itemListAdapter(this, image_details));
lv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
//listItemWidgets_constr newsData = (listItemWidgets_constr) lv1.getItemAtPosition(position);
Toast.makeText(resultsActivity.this, "Selected :" + spidy_iTN_construct.results(position).name, Toast.LENGTH_LONG).show();
Intent i = new Intent(resultsActivity.this, listingsActivity.class);
i.putExtra("itemId", spidy_iTN_construct.results(position).data_id);
startActivity(i);
}
});
}
EDIT 3: error log
05-01 07:17:39.828 3620-3620/com.example.krijn.gw2TP_androidMobile E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.krijn.gw2TP_androidMobile, PID: 3620
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask$itemListToListings.itemListToListings(com.example.krijn.gw2TP_androidMobile.constructors.itemRoot_api_constr)' on a null object reference
at com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask.onProgressUpdate(itemsByInput_AsyncTask.java:88)
at com.example.krijn.gw2TP_androidMobile.AsyncTasks.itemsByInput_AsyncTask.onProgressUpdate(itemsByInput_AsyncTask.java:27)
After I get this error in the Logcat I still see the Log updating with the following in doInBackground
for (int n = 1; n < nPage; n++){
Log.i("gw2Log", "n: " + n);
publishProgress(JoToJO_constructor(spidyHttpGetRequest(makeUrl(n))));
}
After that is done looping the application crashes.
I think you want to make chain calls depending on last_page property you get from the first page. I would do somethig like this where upon each completion of a request the UI is updated on onProgressUpdate
public class itemsByInput_AsyncTask extends AsyncTask<Void, itemRoot_api_constr, Void> {
JSONObject Jo_result;
private itemListToListings itemListToListings;
String userSearchRequest;
public itemsByInput_AsyncTask(String userSearchRequest){
this.userSearchRequest = userSearchRequest;
}
private String makeUrl(int page){
return "http://www.gw2spidy.com/api/v0.9/json/item-search/" +
this.userSearchRequest + "/" + page;
}
#Override
protected Void doInBackground(Void... params) {
itemRoot_api_constr iac;
iac = JoToJO_constructor(spidyHttpGetRequest(makeUrl(0)));
nPage = iac.getLast_page();
publishProgress(iac);
for (int n = 1; n<nPage; n++){
publishProgress(spidyHttpGetRequest(makeUrl(n)));
}
return null;
}
#Override
protected void onProgressUpdate(itemRoot_api_constr... iacs) {
super.onProgressUpdate(iacs);
// assuming method itemListToListings updates UI
// if it doesn't then publishProgress and onProgressUpdate are not needed
// and itemListToListings can be done in doInBackground
this.itemListToListings.itemListToListings(iacs[0]);
}
#Override
protected Void onPostExecute(Void void) {
super.onPostExecute(void);
// unused
}
}
Also:
Adapter, views, and related click listeners should be initiated once. You should move all variables inside of itemListToListings as your Activity field so everytime this callback is called, they won't need to be initiated again.
ListView lv1;
ArrayList<listItemWidgets_constr> image_details = new ArrayList<>();
itemListAdapter adapter;
void onCreate(){
...
lv1 = (ListView) findViewById(R.id.listView);
adapter = new itemListAdapter(this, image_details);
lv1.setOnItemClickListener(...);
}
public void itemListToListings(final itemRoot_api_constr spidy_iTN_construct) {
image_details.clear();
image_details.addAll(getListData(spidy_iTN_construct));
adapter.notifyDataSetChanged();
}

Categories