I'm trying to populate a simple listview using an array of strings in ArrayList. Every time I try though it force closes. I know I'm getting the correct strings in the array as I've seen with Logcat. I can't seem to figure out why it is force closing. Maybe I'm forgetting something in ArrayAdapter (it looks correct to me) or maybe I'm putting my populate method in the wrong place... Can someone help me with this?
public class SchedLayout extends Activity {
public ArrayList<String> titleArray;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sched_layout_layout);
new doParse().execute();
}
private class doParse extends AsyncTask<Void, Void, Void> {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/directory/");
File file = new File(dir, "file.html");
#Override
protected Void doInBackground(Void... params) {
try {
FileInputStream input = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(
input, "UTF-8"));
String line;
titleArray = new ArrayList<String>();
while ((line = br.readLine()) != null) {
String html = line;
Document doc = Jsoup.parse(html);
Elements rels = doc.select("a[rel]");
for (Element title : rels) {
String exclude = "Follow";
if (title.attr("title").contains(exclude)) {
continue;
}
titleArray.add(title.attr("title"));
// Log.v("", title.attr("title")); <--works
}
}
br.close();
input.close();
populate(titleArray); <--does not work
} catch (FileNotFoundException e) {
//Never happens
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void populate(ArrayList<String> array) {
ListView showList = (ListView) findViewById(R.id.listView1);
ArrayAdapter<String> shows = new ArrayAdapter<String>(
getApplicationContext(),
android.R.layout.simple_list_item_1, array);
showList.setAdapter(shows);
}
}
}
Move your populate call to onPostExecute. You cannot modify the ListView in doInBackground or anything UI related.
#Override
protected void onPostExecute(Void v) {
populate(titleArray);
}
Related
I'm writing a simple Android app to get a JSON array into RecyclerView with AsyncTask. I know that I can use libraries as Retrofit or OKHTTP, but this time I tried to write the connection IO from scratch. The connection succeeded and data has been parsed and added to ArrayList. I do all of these in doInBackground(), and in onPostExecute() I just call notifyDataSetChanged() to the adapter, but it didn't work. I tried several ways such as move setAdapter() to onPostExecute(), or move all the AsyncTask to Adapter class and they didn't help anything. Can someone tell me what I miss, if I cannot fix it in 2 or 3 days, I think I will use Retrofit instead.
This is my Main class, I think the bug is only here, but if you need to see my adapter please leave a comment, thanks a lot.
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
ProgressDialog progressDialog;
String apiUrl;
Gson gson;
List<User> userList;
UserAdapter userAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycle_view);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
apiUrl = "https://lebavui.github.io/jsons/users.json";
gson = new Gson();
userList = new ArrayList<>();
userAdapter = new UserAdapter(userList, MainActivity.this);
recyclerView.setAdapter(userAdapter);
DataGetter dataGetter = new DataGetter();
dataGetter.execute();
}
private class DataGetter extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
progressDialog.show();
}
#Override
protected Void doInBackground(Void... voids) {
StringBuilder response = new StringBuilder();
URL url;
HttpsURLConnection urlConnection = null;
try {
url = new URL(apiUrl);
urlConnection = (HttpsURLConnection) url.openConnection();
InputStream is = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int data = isr.read();
while (data != -1) {
response.append((char) data);
data = isr.read();
}
JSONArray jsonArray = new JSONArray(response.toString());
for (int i = 0; i < jsonArray.length(); i++) {
userList.add(gson.fromJson(jsonArray.getJSONObject(i).toString(), User.class));
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return null;
}
#SuppressLint("NotifyDataSetChanged")
#Override
protected void onPostExecute(Void unused) {
super.onPostExecute(unused);
progressDialog.dismiss();
userAdapter.notifyDataSetChanged();
}
}
}
As mentioned in comments you should be using something other than depreciated classes. Below is an example of using runnable, simply add your parser and adapter
This should be moved to android view model.
public class MainActivity extends AppCompatActivity {
private final String LOG_TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v(LOG_TAG, "on Create");
String apiUrl = "https://lebavui.github.io/jsons/users.json";
getUsers(apiUrl);
}
//return interface
public interface Completion{
void onCompletion(List<String> list);
}
//calls a function which call Completion.onCompletion interface off of main thread
public void getUsers(String apiUrl){
getAsyncData(apiUrl, this::setListDataOnMain);
}
//bring back to main thread
//This should be in Android View model for application context instead of this.getMainLooper
private void setListDataOnMain(List<String> list){
Handler mainHandler = new Handler(this.getMainLooper());
Runnable myRunnable = () -> {
//Set local object "list" to your global variable
//Then notify adapter change
//only logging here as example
Log.v(LOG_TAG, "List: " + list);
};
mainHandler.post(myRunnable);
}
//make async
public void getAsyncData(String apiUrl, Completion completion) {
Runnable runnable = () -> {
List<String> userList = makeRequest(apiUrl);
completion.onCompletion(userList);
};
Thread thread = new Thread(runnable);
thread.start();
}
//This is not async calling this func from main thread will crash
public List<String> makeRequest(String apiUrl ) {
List<String> userList = new ArrayList<>();
StringBuilder response = new StringBuilder();
URL url;
HttpsURLConnection urlConnection = null;
try {
url = new URL(apiUrl);
urlConnection = (HttpsURLConnection) url.openConnection();
InputStream is = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int data = isr.read();
while (data != -1) {
response.append((char) data);
data = isr.read();
}
JSONArray jsonArray = new JSONArray(response.toString());
for (int i = 0; i < jsonArray.length(); i++) {
//your json parsing here
userList.add(String.valueOf(i));
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return userList;
}
}
I think the DataGetter class need to executed first than you can set adapter
i test this code and it works
#SuppressLint("NotifyDataSetChanged")
#Override
protected void onPostExecute(Void unused) {
super.onPostExecute(unused);
progressDialog.dismiss();
userAdapter = new UserAdapter(userList, MainActivity.this);
recyclerView.setAdapter(userAdapter);
}
I tried many ways but I got blank layout.I changed lots of lines but the result is always the same. Should I rewrite the code and try something different. I followed some videos on youtube but nobody has the proper solution.I don't think it is caused because array got null result. Anybody knows what might be wrong:
AirportTransportActivity
public class AirportTransportActivity extends AppCompatActivity {
ListView listView;
ArrayAdapter<String> adapter;
String[] data = new String[0];
JSONObject jsonObject = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_airport_transport);
//Get airport details
Intent intent = getIntent();
String getairport = intent.getStringExtra("airport");
final TextView textViewAirport = (TextView) findViewById(R.id.tvairport);
textViewAirport.setText(getairport);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
//List view setup
listView = (ListView) findViewById(R.id.lvairport);
//Get airport transport
new RetrieveTask().execute();
//Adapter
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
}
private class RetrieveTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... voids) {
String strUrl = "http://my database";
URL url = null;
StringBuffer sb = new StringBuffer();
try {
url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream iStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(iStream));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
iStream.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
#Override
protected void onPostExecute(String result) {
try {
JSONArray jsonArray = new JSONArray(result);
data = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
data[i] = jsonObject.getString("airporttransportname");
}
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
}
}
In your original post you have set the adapter before you have added the data. But I also suspect that you are having issues with:
String[] data = new String[0];
So I changed it to
ArrayList<String> data = new ArrayList<>();
I also changed your AsyncTask a bit. Now you can do most of your parsing in the background thread. When the doInBackground is successful just notify the adapter of the changes in onPostExecute.
You will also need to check if the ArrayList<String> fits to your Adapter class. If not just change it as needed.
Do this instead:
public class AirportTransportActivity extends AppCompatActivity {
private static final String TAG = AirportTransportActivity.class.getSimpleName();
ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_airport_transport);
//Get airport details
Intent intent = getIntent();
String getairport = intent.getStringExtra("airport");
final TextView textViewAirport = (TextView) findViewById(R.id.tvairport);
textViewAirport.setText(getairport);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
//List view setup
listView = (ListView) findViewById(R.id.lvairport);
//Get airport transport
new RetrieveTask().execute();
}
private class RetrieveTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... voids) {
String strUrl = "http://my database";
URL url = null;
StringBuffer sb = new StringBuffer();
try {
url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream iStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(iStream));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
iStream.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return toString();
}
#Override
protected void onPostExecute(String result) {
if(result.isEmpty()) return;
try{
ArrayList<String> data = new ArrayList<>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
JSONArray jsonArray = new JSONArray(result);
int len = jsonArray.length();
Log.e(TAG, "Lenth of json array = " + len)
for (int i = 0; i < len; i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
// I add the optString variation just in case the data is corrupt
String s = jsonObject.optString("airporttransportname", "?");
data.add(s);
}
listView.setAdapter(adapter);
}
catch(JSONException e){
e.printStackTrace();
}
}
}
Disclaimer I did this in a text editor and wasn't able to count on "auto-correct" for some of the syntax or method names--so you will need to check it.
There are some other things I would change in your code, but I wanted to leave it as close to the original as I could.
You're setting the data array and calling notifyDataSetChanged() before your async task has a chance to finish. You need to set your adapter and notifyDataSetChanged() from within the onPostExecute() method in your async task.
Just to be clear, when you call asyncTask.execute(), it starts the async task and then immediately keeps executing the rest of the code. So when you set your data array in your list view adapter, the asyncTask hasn't even finished and your array items are still null.
No need to change anything just add adapter.notifyDataSetChanged(); after storing all values into your array
Remove this line adapter.notifyDataSetChanged();in Oncreate ,You
have to use notifyDataSetChanged() when your arraylist values is getting
changed.
#Override
protected void onPostExecute(String result) {
try {
JSONArray jsonArray = new JSONArray(result);
data = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
data[i] = jsonObject.getString("airporttransportname");
}
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
I have an arraylist that gets inputs from a CSV file and is displayed by a listview. What I'm trying to do is add search functionality to that activity. I've seen a couple of other tutorials but i can't seem to get the code to work.
This is the main java class for the CSV arraylist:
public class games extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_games);
InputStream inputStream = getResources().openRawResource(R.raw."filename");
CSVFile csvFile = new CSVFile(inputStream);
List<String[]> slots = csvFile.read();
MyListAdapter adapter = new MyListAdapter(this, R.layout.list_item, R.id.text_view, filename);
ListView listView = (ListView) findViewById(R.id.list);
listView.setAdapter(adapter);
}
private class CSVFile {
InputStream inputStream;
public CSVFile(InputStream inputStream){
this.inputStream = inputStream;
}
public List<String[]> read(){
//
List<String[]> ArrayList = new ArrayList<String[]>();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line;
while ((line = reader.readLine()) != null) {
String[] row = line.split(",");
ArrayList.add(row);
}
}
catch (IOException e) {
Log.e("Main",e.getMessage());
}
finally {
try {
inputStream.close();
}
catch (IOException e) {
Log.e("Main",e.getMessage());
}
}
return ArrayList;
}
}
}
I found code from another stackoverflow post but i just don't know how to really apply it to my specific case or if it even applies:
ArrayList<String> storedContent = new ArrayList<String>();
ArrayList<String> contentArray = new ArrayList<String>();
String searchText = "sometest";
for(String each : storedContent){
if (each.contains(searchText)){
contentArray.add(each);
}
}
ListView displaySearchResult = (ListView)findViewById(R.id.list_id);
myListAdapter adapter = new myListAdapter(contentArray, this) ;
displaySearchResult.setAdapter(myListAdapter);
Any solution would be greatly appreciated, let me know if I left anything out that you may need to help me, I'm pretty fresh to java scene.
the following is your code... I think it is not right. You need to instantiate the arraylist
public List<String[]> read(){
List<String[]> ArrayList = new ArrayList<String[]>();
}
instead, try this...
public ArrayList<String> read(){
ArrayList<String> objName = new ArrayList<String>();
//create an String type arraylist "objName". you store all the string data from csv file into this "objName"
return objName;
}
I hope it helps...
I'm working on an Android app with some API i made on my own. I'm almost done but i can't find the way to put the data i get inside my async task (the one who's getting my json data) inside my dynamic spinner.
Here is my code :
String example;
static final String API_URL2 = "https://xxxxxxxx.xx";
//Start Used for spinner with different value and display
String[] textfordropdown = { "A",
"B",
"C",
};
String[] valueofdropdowtext =
{ "1",
"2",
"3",
};
Spinner spinnerdynamic;
OnCreate i've called my async task to see if i get something displayed so yeah it works :
new GetList().execute();
and i also create my spinner here
spinnerdynamic = (Spinner)findViewById(R.id.dynamic_spinner);
ArrayAdapter<String> adapter1 =
new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_spinner_item, textfordropdown);
adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerdynamic.setAdapter(adapter1);
spinnerdynamic.setOnItemSelectedListener(onItemSelectedListener1);
Here is the function onItemSelectedListener1:
OnItemSelectedListener onItemSelectedListener1 =
new OnItemSelectedListener(){
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
example = String.valueOf(valueofdropdowtext[position]);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {}
};
And here my async task :
class GetList extends AsyncTask<Void, Void, String> {
protected void onPreExecute() {
}
protected String doInBackground(Void... urls) {
try {
URL url = new URL(API_URL2);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
bufferedReader.close();
return stringBuilder.toString();
} finally {
urlConnection.disconnect();
}
} catch (Exception e) {
Log.e("ERROR", e.getMessage(), e);
return null;
}
}
protected void onPostExecute(String response) {
if (response == null) {
response = "Une erreur c'est produite";
}
Log.i("INFO", response);
try {
JSONObject object = (JSONObject) new JSONTokener(response).nextValue();
JSONArray prestationlist = object.getJSONArray("WhatIWant");
//Permet de compter le nombre d'éléments dans le json array
int arrSize = prestationlist.length();
ArrayList<String> value = new ArrayList<String>(arrSize);
ArrayList<String> name = new ArrayList<String>(arrSize);
for(int i=0;i<arrSize;i++) {
object = prestationlist.getJSONObject(i);
value.add(object.getString("Value"));
name.add(object.getString("Text"));
//Here i've made some display to see if it works, i get the data.
responseView.setText(value.toString());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
So how can i put my data inside the String[] textfordropdown or valueofdropdowntext ?
Thanks a lot !
Create a string array list and initialize it at the top of file
And use that arraylist in your spinner
adapter1 =
new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_spinner_item, yourarraylist);
And add your values into that array list in your onPostExecute method
for(int i=0;i<arrSize;i++) {
object = prestationlist.getJSONObject(i);
value.add(object.getString("Value"));
name.add(object.getString("Text"));
//Here i've made some display to see if it works, i get the data.
yourarraylist.add(object.getString("Text"));
responseView.setText(value.toString());
}
adapter1.notifyDataSetChanged();
Note: You need to initialize spinner adapter also at the top of the file
I'm trying to create a window which will display a listbox populated with an array parsed from JSON.
The listview is populated and created in the OnCreate method in my MainActivity. In the main activity I'm also calling an AsyncTask to parse the JSON array from a website.
The task is shown below:
public class JSONParse extends AsyncTask<String, String, String[]> {
#Override
protected String[] doInBackground(String... params) {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream IStream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(IStream));
StringBuffer buffer = new StringBuffer();
String recievedLine = "";
while ((recievedLine = reader.readLine()) != null) {
buffer.append(recievedLine);
}
String full = buffer.toString();
JSONObject JSONParent = new JSONObject(full);
JSONArray j_Puzzles = JSONParent.getJSONArray("PuzzleIndex");
int arraySize = j_Puzzles.length();
String[] s_Puzzles = new String[arraySize];
StringBuffer endString = new StringBuffer();
for (int i = 0; i < j_Puzzles.length(); i++) {
s_Puzzles[i] = j_Puzzles.toString(i);
endString.append(s_Puzzles[i] + "\n");
}
MainActivity.Waiting = true;
return s_Puzzles;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
What is the best way to get s_Puzzles out of the background thread and into my OnCreate so it can be used like this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new JSONParse().execute("http://www.hull.ac.uk/php/349628/08309/acw/index.json");
ListView listView = (ListView) findViewById(R.id.listView);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, s_Puzzles);
listView.setAdapter(arrayAdapter);
}
}
Furthermore, am I going to have to pause the OnCreate method until the background worker is done in order to prevent the listview updating with nothing due to the worker thread not finishing.
Any help appreciated. Thanks
You are trying to call the AsyncTask and then you are setting the information when you are not certain that the information is loaded. Remember that it is being loaded Asynchronously. I recommend you to do that on the onPostExecute() method of the AsyncTask.
An AsyncTask has 4 methods that you can customize:
doInProgress: Send from the thread to the UI thread (Activity) the progress, normally a number. Here you can update the UI.
onPreExecute: To setup things before the thread starts running. Here you can update the UI.
doInBackground: You already have it, it's perfect. Here you cannot update the UI because this one runs on the background thread. It's the only method from the AsyncTask that doesn't run on the UI Thread (Activity).
onPostExecute: You get the result from the doInBackground. Here is where you should update your UI.
You can find any tutorial for AsyncTask and its easy. In this project I use an AsyncTask for kind of the same.
https://github.com/isaacurbina/ViewHolderListView/tree/master/app/src/main/java/com/mac/isaac/viewholderlistview
Hope it helps!
Something like this will work, without the need to overcomplicate what you already have:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView listView = (ListView) findViewById(R.id.listView);
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<String>());
listView.setAdapter(arrayAdapter);
new JSONParse() {
#Override
protected void onPostExecute(String[] puzzles)
{
super.onPostExecute(puzzles);
arrayAdapter.addAll(puzzles);
}
}.execute("http://www.hull.ac.uk/php/349628/08309/acw/index.json");
}