ArrayList being empty after adding elements - java

I have this android code which bring a JSON from a server and fill an ArrayList from that JSON
I checked the size of the ArrayList "meals" inside the onresponse void it gives me 1 but when i check it after the StringRequest object i get 0 items .
meals is defined in global scope and initialized inside the oncreateview function
The Code:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.i("debug","on create view");
View view =inflater.inflate(R.layout.fragment_meal_list,container,false);
ListView List ;
meals=new ArrayList<meal>();
String url="http://syriankitchen.tk/get_recent.php";
StringRequest mealsrequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try{
JSONObject object= new JSONObject(response);
JSONArray mealsArray = object.getJSONArray("result");
for(int i=0;i<mealsArray.length();i++){
JSONObject cur = mealsArray.getJSONObject(i);
int id= cur.getInt("id");
String name= cur.getString("name");
String description = cur.getString("description");
int price = cur.getInt("price");
meals.add(new meal(id,name,price,description));
}
Log.i("debug","meals size = "+meals.size());
}
catch(JSONException e){
e.printStackTrace();
}
}
},new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getActivity(),error.getMessage(),Toast.LENGTH_SHORT).show();
}
});
Volley.newRequestQueue(getActivity()).add(mealsrequest);
ArrayList<String> strs=new ArrayList<String>();
String mealsnames[]=new String[meals.size()];
for(int i=0;i<meals.size();i++)strs.add(meals.get(i).getName());
strs.toArray(mealsnames);
Log.i("debug","meals ou size "+meals.size());
CustomList adapter = new CustomList(getActivity(),mealsnames,meals);
List = (ListView)view.findViewById(R.id.list);
List.setAdapter(adapter);

The problem here is about understanding how asynchronous tasks work. When you are adding a volley request to the queque, it will run in background thread (off the main thread) and control will pass to the next line.
So, after this:
Volley.newRequestQueue(getActivity()).add(mealsrequest);
control passes to this:
ArrayList<String> strs=new ArrayList<String>();
String mealsnames[]=new String[meals.size()];
Now since meals is updated on the background thread, you are not able to get the data by the time control reaches String mealsnames[]=new String[meals.size()];
So you will get zero size (meals.size()) here.
Try to move this portion of the code into onResponse.
Try like this:
public void updateData(){
ArrayList<String> strs=new ArrayList<String>();
String mealsnames[]=new String[meals.size()];
for(int i=0;i<meals.size();i++)strs.add(meals.get(i).getName());
strs.toArray(mealsnames);
Log.i("debug","meals ou size "+meals.size());
CustomList adapter = new CustomList(getActivity(),mealsnames,meals);
List = (ListView)view.findViewById(R.id.list);
List.setAdapter(adapter);
}
and call this method from onResponse:
#Override
public void onResponse(String response) {
try{
JSONObject object= new JSONObject(response);
JSONArray mealsArray = object.getJSONArray("result");
for(int i=0;i<mealsArray.length();i++){
JSONObject cur = mealsArray.getJSONObject(i);
int id= cur.getInt("id");
String name= cur.getString("name");
String description = cur.getString("description");
int price = cur.getInt("price");
meals.add(new meal(id,name,price,description));
}
Log.i("debug","meals size = "+meals.size());
updateData();
}
catch(JSONException e){
e.printStackTrace();
}
}

When you write this -
Volley.newRequestQueue(getActivity()).add(mealsrequest);
That means you are making an asynchronous call and that mealsrequest will run on another thread.
You are printing -
Log.i("debug","meals ou size "+meals.size());
just after you make your mealsrequest. When control reaches this statement your network request is not completed yet. So apparently, you don't have anything in your list. Your list will be populated in onResponse() only, since that method is executed after the network request gets completed.

Related

How to change recycler view item dynamically?

I have created a RecyclerView to show each node that I have connected. In the RecyclerView I have added two buttons and one TextView. The TextView will show the current status of the node i.e. whether the node in currently on or off. The nodes are sending their current status over MQTT, I am able to detect the current status of every node on my app but I am unable to update the TextView according to it. How can I update the TextView?
I have tried adding new item and deleting the previous one, but none of them works for me. I have added the code where I am detecting from which node the status message is coming.
//From this section I am getting details of my all node stored into the database
public void addData()
{
pumpList = new ArrayList<>();
StringRequest stringRequest = new StringRequest(Request.Method.GET, REQUEST_URL,
new Response.Listener<String>()
{
#Override
public void onResponse(String response)
{
try
{
JSONArray pumpData = new JSONArray(response);
for(int i = 0; i < pumpData.length(); i++)
{
JSONObject pump = pumpData.getJSONObject(i);
String name = pump.getString("name");
String device_id = pump.getString("device_id");
String stat = pump.getString("stat");
Log.d("HTTP",String.valueOf(name));
Log.d("HTTP",String.valueOf(device_id));
Log.d("HTTP",String.valueOf(stat));
pumpList.add(new pumpData(name,device_id,stat));
}
pumpAdapter adapter = new pumpAdapter(getActivity(),pumpList);
recyclerView.setAdapter(adapter);
progressDialog.dismiss();
}
catch (JSONException e)
{
e.printStackTrace();
progressDialog.dismiss();
}
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error)
{
Toast.makeText(getActivity(),error.getMessage(),Toast.LENGTH_SHORT).show();
progressDialog.dismiss();
}
});
Volley.newRequestQueue(getActivity()).add(stringRequest);
}
//When I get a message over MQTT then I am showing from which device the MQTT Message is Coming from
public void changeDataByFiltering(String MQTT_TOPIC, String MQTT_PAYLOAD)
{
for(pumpData pD: pumpList)
{
insertStringSubscribe INSSUB = new insertStringSubscribe();
String mqtt_topic_from_list = INSSUB.insertString(pD.getDeviceID());
if(mqtt_topic_from_list.trim().toLowerCase().equals(MQTT_TOPIC.trim().toLowerCase()))
[enter image description here][1]{
if(MQTT_PAYLOAD.trim().toLowerCase().equals(payloadON.trim().toLowerCase()))
{
Toast.makeText(mCtx, pD.getName()+": ON",Toast.LENGTH_SHORT).show();
}
if(MQTT_PAYLOAD.trim().toLowerCase().equals(payloadOFF.trim().toLowerCase()))
{
Toast.makeText(mCtx, pD.getName()+": OFF",Toast.LENGTH_SHORT).show();
}
}
}
}
I assume that we have a List<String> data as our data source and we have rvadapter which is our RecyclerView.Adapter . and we will do as below to update an item:
data.set(position,"newdata");
rvadapter.notifyItemChanged(position);
Also, you can change the range of items. and insert new item or delete item but have to call notify... related function in rvadapter
but if you recreate the data for example :
data = new ... then you have to use notifydatasetchanged;

I'm Using NotifydataAdapter But It's Not Working When Value change on database;

I am getting data from server as json. Everything works but notifyDataSetChanged is not working.
BackGroundTask backGroundtask = new BackGroundtask();
backGroundTask.execute(arrayList);
private class BackGroundWorkeroneSignup extends AsyncTask<List<CheckingDTO>,CheckingDTO, CheckingDTO> {
#Override
public void onResponse(JSONObject response) {
CheckingDTO check = new CheckingDTO(email_c+"=="+pass_c,email_c,pass_c,email_c,pass_c);
arrayList.add(checkingDTO);
adapterActivity = new HistoryAdapter(arrayList);
recyclerView.setAdapter(adapterActivity);
#Override
protected void onPostExecute(CheckingDTO data) {
super.onPostExecute(data);
arrayList.clear();
List<CheckingDTO> list = new ArrayList<>();
list.add(data);
adapterActivity = new HistoryAdapter(list);
adapterActivity.notifyDataSetChanged();
}
I'm posting the relevant code that I've sent you in the email.
oncreate method
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
adapterActivity = new HistoryAdapter(arrayList);
recyclerView.setAdapter(adapterActivity);
backGroundWorkeroneSignup = new BackGroundWorkeroneSignup();
backGroundWorkeroneSignup.execute(arrayList);
background method:
try {
JSONArray jsonArray = response.getJSONArray("get_request");// this is your array containing data
for (int i=0;i<jsonArray.length();i++) {
JSONObject jobj = jsonArray.getJSONObject(i);
String id = jobj.getString("id");
String email_c =jobj.getString("pick_lat");
String pass_c = jobj.getString("pick_lon");
checkingDTO = new CheckingDTO(email_c+"=="+pass_c,email_c,pass_c,email_c,pass_c);
CheckingDTO check = new CheckingDTO(email_c+"=="+pass_c,email_c,pass_c,email_c,pass_c);
arrayList.add(check);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
on PostExecute
adapterActivity.notifyDataSetChanged();
In First Part Notice the recyclerView.setAdapter(adapterActivity);
In Background Mode:
when you loop in Array always remember: index starts from 0 therefore it's always less then size of array i.e. for (int i=0;i<jsonArray.length();i++)
You should always create new object of Model class in loop for and then add it in loop. There in no need of calling the notifydatasetchanged in loop.
In onPostExecute: we call the notifydatasetchanged only because we've already added all the elements in the list.
hope that explains all of your queries. let me know if you have confusion in any case

ListView OnClicklistener Returns Only single json item value

I have created a listview which will display some youtube video list (title, thumbnail, videocode) from json using volley library. List is displayed as I expected. Then I implemented onclick listener for list view. Here is the code
// Creating volley request obj
JsonArrayRequest movieReq = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, response.toString());
hidePDialog();
// Parsing json
for (int i = 0; i < response.length(); i++) {
try {
JSONObject obj = response.getJSONObject(i);
Movie movie = new Movie();
movie.setTitle(obj.getString("title"));
movie.setThumbnailUrl(obj.getString("image"));
movie.setPYear(obj.getString("time"));
videocode = (obj.getString("videocode"));
//movie.setRating(((Number) obj.get("rating"))
// .doubleValue());
//movie.setYear(obj.getInt("releaseYear"));
// Genre is json array
// JSONArray genreArry = obj.getJSONArray("genre");
for (int j = 0; j < genreArry.length(); j++) {
genre.add((String) genreArry.get(j)); }
//movie.setGenre(genre);
// adding movie to movies array
movieList.add(movie);
} catch (JSONException e) {
e.printStackTrace();
}
}
// notifying list adapter about data changes
// so that it renders the list view with updated data
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
hidePDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(movieReq);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent videoplayer = new Intent(Videos.this, VideoPlayer.class);
//String videocode = null;
videoplayer.putExtra("videocode", videocode);
startActivity(videoplayer);
finish();
}
});
So that after clicking on list item, another activity opens and plays the video. To play the video, youtube video code is required, that I am getting from Json and using putExtra I am transferring value to player activity.
The problem I am facing
Clicking on anyvideo plays in listview plays 1st video only. Means suppose list is showing 2 videos - Video1 and Video2. Naturally, clicking on video1 should play video1 and clicking on video2 should play video 2. But what happening is either I click on Video1 or Video2 or Video3, Player is playing video1 only..
Here is The JSON file
http://angelqr.cf/api.php
You have to get the videocode from the movielist. At first add the videocode for every movie individually.
movie.setVideocode(obj.getString("videocode"));
//videocode = (obj.getString("videocode"));
Then make a function for fetching the particular movie's videocode.
public String getVideocode(int position) {
return movieList.get(position).getVideocode();
}
Then call it from your listener's code.
videoplayer.putExtra("videocode", getVideocode(position));

How to display random items from arraylist<T> derived from a doInBackground?

So i was making this app which displays an arrayList of different poems
I first request the poems from API then the Asynctask returns the poems in the form of Arraylist i wand to show 20 random poems from the list.
This is the Asynctask code
private class TitleAsynctask extends AsyncTask<URL,Void,List<view>> {
private ProgressDialog progressDialog;
#Override
public List<view> doInBackground(URL... urls){
URL url = Query_utils.createurl(POEM_TITLE);
String json = "";
Log.d(LOG_TAG,"this worked");
{
try {
json = Query_utils.makehttprequest(url);
Log.d(LOG_TAG, "make Httprequest works");
} catch (IOException e) {
}
}
List<view> title_view = Query_utils.extracttitlefromjson(json);
return title_view;
}
#RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
#Override
protected void onPostExecute(List<view> data) {
madapter.clear();
if (data != null && !data.isEmpty()){
madapter.addAll(data);
}
}
}
and the onCreate code is
TitleAsynctask task = new TitleAsynctask();
URL url = Query_utils.createurl(POEM_TITLE);
task.execute(url);
ArrayList<view > arr = new ArrayList<view>();
final ListView poem_Title_list = (ListView) findViewById(R.id.list_item);
madapter = new title_adapter(this ,arr);
poem_Title_list.setAdapter(madapter);
I can think of two ways:
Just use a random number generator to generate a number between 0 and the number of entries-1. The disadvantage with this method is that you can get repeats.
Randomly sort the list after fetching. You can do this using Collections.shuffle(list) This way you won't get any repeats but you will sort the whole list which could be a waste if there are hundreds of entries and you only want to show 20.

JSON download to ListView [duplicate]

This question already has answers here:
How do I parse JSON in Android? [duplicate]
(3 answers)
Closed 6 years ago.
I found this code that I have modified to suit my needs. However I am facing a bit of an issue. It appears that the data is obtained from the remote host but cannot be parsed into adapter.
I have reviewed my entire code structure to ensure that everything is in place but I cant seem to find the problem. The ListView is inside of a Fragment that is part of a TabbedActivity.
This my code:
Fragment inside a Tabbed Activity
public class shops extends Fragment {
String url="http://link to remote webservice";
//FragmentManager fm;
//newInstance() method return reference to fragment
public static shops newInstance(){
shops fragment = new shops();
return fragment;
}
public shops() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//fm = getFragmentManager();
View view = inflater.inflate(R.layout.fragment_shops, container, false);
final ListView listView = (ListView)view.findViewById(R.id.shops_info);
final Downloader d =new Downloader(getActivity(),url,listView);
d.execute();
//calls DialoFragment
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fab_edset);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
DialogFragment newEdQua = new createNewEdQua();
newEdQua.show(getActivity().getFragmentManager(), "createNewEdQua");
}
});
// Inflate the layout for this fragment
return view;
}
}
Downloader(receives data and parses in the same class)
public class Downloader extends AsyncTask<Void,Integer,String> {
Context c;
String retredq_url;
ListView listView;
String data;
ArrayList<String> shopl=new ArrayList<String>();//its the ArrayList that we bind to ListView
ProgressDialog pd;
public Downloader(Context c, String retredq_url, ListView listView){
this.c=c;
this.retredq_url=retredq_url;
this.listView=listView;
}
//Before job starts
#Override
protected void onPreExecute(){
super.onPreExecute();
pd=new ProgressDialog(c);
pd.setTitle("Refreshing List");
pd.setMessage("Please Wait...");
pd.show();
}
#Override
protected String doInBackground(Void... params) {
data=downloadData();
return data;
}
#Override
protected void onPostExecute(String s){
super.onPostExecute(s);
pd.dismiss();
if (s !=null){
try{
JSONArray ja=new JSONArray(data);
//JSONObject jo=null;
shopl.clear();//we need to add the data to ArrayList, so clear list first to avoid duplicates
for (int i=0;i<ja.length();i++){
String shops=ja.getJSONObject(i).getString("Qualification")+ ja.getJSONObject(i).get("eq_end_date")+
ja.getJSONObject(i).get("eq_loc_shops");//retrieve the column name into a string
shopl.add(shops);
ArrayAdapter<String> adapter=new ArrayAdapter<String>(c,R.layout.list_item_shopl,shopl);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Snackbar.make(view,shopl.get(i),Snackbar.LENGTH_LONG).show();
}
});
}
} catch (JSONException e) {
Log.e("Downloader", "Error", e);
}
/*
//call the Parser here to parse the JSON after we confirm string writer is not null
Parser p=new Parser(c,s,listView);
p.execute();*/
}else {
Toast.makeText(c,"Unable to download data", Toast.LENGTH_SHORT).show();
}
}
private String downloadData(){
//connect and get a stream
InputStream inputStream=null;
String line=null;
try{
URL url=new URL(retredq_url);
HttpURLConnection con=(HttpURLConnection) url.openConnection();
inputStream=new BufferedInputStream(con.getInputStream());
BufferedReader br=new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb=new StringBuffer();
if (br !=null){
while ((line=br.readLine()) !=null){
sb.append(line+"\n");
}
}else{return null;}
return sb.toString();
} catch (MalformlocRLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStream !=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
JSON output (Checked with ARC plugin on Chrome)
{"qualifications":[{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"}],"success":1}
A slight difference from what ADM sees (The success message comes first here)
{"success":1,"qualifications":[{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"},{"eq_edu_Institution":"Oracle","eq_start_Date":"1998-06-14","eq_end_date":"2005-08-23","Qualification":"Software Engineer"}]}
I defined the success message while struturing the array in php webservice
I intend use the Downloader class in several tabs for the same purpose(retrieve data from url parse and display in ListView). The urls and data are independent ot each other so I guess it should work...
Going over LogCat and reviewing JSON data obtained from server I was able to figure the problem. My previous code would have worked without any issues if the data was an Array with Objects in it. But I checked and the structure of the JSON was an Object with the Array inside.
What I had to do was get the Object with JSONObject then that Object retrieve the Array with JSONArray.. Like this:
JSONObject jsonObject=new JSONObject(data);
JSONArray jsonArray= jsonObject.getJSONArray("qualifications");
//now this Array has Objects needed
for (int i=0;i<jsonArray.length();i++){
String institution=jsonArray.getJSONObject(i).getString("Qualification");
edqua.add(institution);
}
//provide the ArrayAdapter<> needed
ArrayAdapter<String> adapter=new ArrayAdapter<String>(c,R.layout.list_item_edqua,edqua);
listView.setAdapter(adapter);
It should be in a try-catch block.. It is good to know the structure of the JSON that is expected. I was rather asking for an Array when Object was being offerd, hence the type mis-match error. I also realised that I will be unable to use the same class for different data sources, as the tables are completely. Any suggestions of how to use one class for different data urls will appreciated.
As the ArrayList<String> shows, the list item will be like
Qualification eq_end_date eq_loc_shops as a single string, so initialize the adapter like
adapter = new ArrayAdapter<>(context,android.R.layout.simple_list_item_1,shopl);
Most of your code can be reused but the part which starts from data generated by downloadData() to the JSONArrayObject that your listview needs.
So you can extract these code to an Interface(here called IPreParser)'s method (called JSONArrayObject arrayFromData(String)), like this:
public interface IPreParser{
JSONArrayObject arrayFromData(String data);
}
Your Downloader need hold a reference to IPreParser, and invoke its method in onPostExecute(). And you can initialize this reference by declaring doInBackground(IPreParser).
In anywhere you what download and parse your data, just implement IPreParser, and then execute your downloader with downloader.execute(yourImplementor);

Categories