This is my first time working with Google Maps in android. I was able to create a map displaying one marker. That was a good start for me. However, I would like to display multiple markers. To do these, I am fetching the locations from the database using volley. However, I am experiencing one problem, one that I have experienced before but I managed to have a work around then and now it has surfaced again in my current project i.e I like using Singletons in my application to store data as long as the application is in memory. My singleton for instance could hold an array list of objects and I can get the array list anywhere and any time in any activity/fragment. However, I need to populate the arraylist in my singleton before activity/fragment becomes active and access the arraylist of objects in onCreate/onCreateView but it seems the activity/fragment loads very fast and a reference to the arraylist of objects from the singleton is always null. In my current project:
This is the singleton class that handles all the locations
public class PointOfInterestLab {
private ArrayList<PointOfInterest> mPointOfInterests;
private static PointOfInterestLab sPointOfInterestLab;
private Context mAppContext;
private PointOfInterestLab(Context appContext){
mAppContext = appContext;
mPointOfInterests = new ArrayList<PointOfInterest>();
}
public static PointOfInterestLab get(Context c){
if(sPointOfInterestLab == null){
sPointOfInterestLab = new PointOfInterestLab(c.getApplicationContext());
}
return sPointOfInterestLab;
}
public ArrayList<PointOfInterest> getPointOfInterests(){
return mPointOfInterests;
}
public PointOfInterest getPointOfInterest(int id){
for(PointOfInterest pointOfInterest: mPointOfInterests){
if(pointOfInterest.getID() == id){
return pointOfInterest;
}
}
return null;
}
public void addPointOfInterest(PointOfInterest pointOfInterest){
mPointOfInterests.add(pointOfInterest);
}
public void clearPointOfInterests(){
mPointOfInterests.clear();
}
public void deletePointOfInterest(PointOfInterest pointOfInterest){
mPointOfInterests.remove(pointOfInterest);
}
}
In the fragment that I want to display the locations:
public class PointOfInterestMapFragment extends Fragment implements OnMapReadyCallback {
private static final String TAG = PointOfInterestMapFragment.class.getSimpleName();
private GoogleMap mGoogleMap;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//get locations from server
getPOISFromDB();
}//end method onCreate
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle saveInstanceState){
View v = inflater.inflate(R.layout.fragment_poi_map, parent, false);
//obtain the support fragment and get notified when the map is ready
SupportMapFragment mapFragment = (SupportMapFragment)getChildFragmentManager()
.findFragmentById(R.id.map);
//pass fragment in getMapAsync handler
mapFragment.getMapAsync(this);
return v;
}//end method onCreateView
#Override
public void onMapReady(GoogleMap googleMap){
mGoogleMap = googleMap;
ArrayList<PointOfInterest> pointOfInterests;
pointOfInterests = PointOfInterestLab.get(getActivity())
.getPointOfInterests();
for(PointOfInterest pointOfInterest : pointOfInterests){
//add marker and move camera
/*LatLng location = new LatLng(pointOfInterest.getLocation().getLatitude()
, pointOfInterest.getLocation().getLongitude());
mGoogleMap.addMarker(new MarkerOptions()
.position(location)
.title(pointOfInterest.getName()));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(location));*/
Log.d(TAG, pointOfInterest.getName());
}
/*This is just for debugging, it is returning a null object
Meaning by the time the array list of the singleton class
is being populated this has been called I guess*/
PointOfInterest pointOfInterest = PointOfInterestLab.get(getActivity()).getPointOfInterest(3);
LatLng location = new LatLng(pointOfInterest.getLocation().getLatitude(),pointOfInterest.getLocation().getLongitude());
mGoogleMap.addMarker(new MarkerOptions()
.position(location)
.title("location"));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(location));
}
//Get locations from db
private void getPOISFromDB(){
// Tag used to cancel the request
String tag_string_req = "req_poi_list";
StringRequest strReq = new StringRequest(Request.Method.POST,
AppConfig.URL_POI_LIST, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, "Response: " + response);
try {
JSONObject jObj = new JSONObject(response);
JSONArray jsonArray = jObj.getJSONArray("pois");
PointOfInterestLab.get(getActivity()).clearPointOfInterests();
for(int i=0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
int id = jsonObject.getInt("poi_id");
String name = jsonObject.getString("name");
String summary = jsonObject.getString("summary");
double latitude = jsonObject.getDouble("latitude");
double longitude = jsonObject.getDouble("longitude");
Location location = new Location("dummyProvider");
location.setLatitude(latitude);
location.setLongitude(longitude);
PointOfInterest pointOfInterest = new PointOfInterest(id, name, summary
, location);
PointOfInterestLab.get(getActivity()).addPointOfInterest(pointOfInterest);
}
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Server error: " + error.getMessage());
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
SQLiteHandler sqLiteHandler = new SQLiteHandler(getActivity());
User user = sqLiteHandler.getUserDetails();
params.put("user_id", user.getUserID());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}//end method getPOISFromDB
}//end class
This is the part of the code that I am using to test for now. I am getting error PointOfInterest.getLocation()' on a null object reference on line LatLng location = new LatLng(pointOfInterest.getLocation().getLatitude(),pointOfInterest.getLocation().getLongitude());
/*This is just for debugging, it is returning a null object
Meaning by the time the array list of the singleton class
is being populated this has been called I guess*/
PointOfInterest pointOfInterest = PointOfInterestLab.get(getActivity()).getPointOfInterest(3);
LatLng location = new LatLng(pointOfInterest.getLocation().getLatitude(),pointOfInterest.getLocation().getLongitude());
mGoogleMap.addMarker(new MarkerOptions()
.position(location)
.title("location"));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLng(location));
I have also tried calling the database api in the onCreate method of the hosting activity but doesn't seem to work
public class MainActivity extends AppCompatActivity {
......
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pedometer);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
User user = new User();
user.checkLogin(MainActivity.this);
tabLayout = (TabLayout)findViewById(R.id.tabs);
viewPager = (ViewPager)findViewById(R.id.viewpager);
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
viewPager.setOffscreenPageLimit(2);
//runnable to get rid of bug
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
setTitle("Updates");
//get locations from db
getPOISFromDB();
}//end method onCreate
private class MyAdapter extends FragmentStatePagerAdapter {
private MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position)
{
switch (position){
case 0 :
return new FirstFragment();
case 1 :
eturn new SecondFragment();
case 2 :
return new PointOfInterestMapFragment();
}
return null;
}
#Override
public int getCount() {
return 3;
}
/**
* This method returns the title of the tab according to the position.
*/
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0 :
return getResources().getString(R.string.fragment_1);
case 1 :
return getResources().getString(R.string.fragment_2);
case 2 :
return getResources().getString(R.string.fragment_3);
}
return null;
}
}//end class MyAdapter
private void getPOISFromDB(){
// Tag used to cancel the request
String tag_string_req = "req_poi_list";
StringRequest strReq = new StringRequest(Request.Method.POST,
AppConfig.URL_POI_LIST, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, "Response: " + response);
try {
JSONObject jObj = new JSONObject(response);
JSONArray jsonArray = jObj.getJSONArray("pois");
PointOfInterestLab.get(AppController.getInstance()).clearPointOfInterests();
for(int i=0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
int id = jsonObject.getInt("poi_id");
String name = jsonObject.getString("name");
String summary = jsonObject.getString("summary");
double latitude = jsonObject.getDouble("latitude");
double longitude = jsonObject.getDouble("longitude");
Location location = new Location("dummyProvider");
location.setLatitude(latitude);
location.setLongitude(longitude);
PointOfInterest pointOfInterest = new PointOfInterest(id, name, summary
, location);
PointOfInterestLab.get(AppController.getInstance()).addPointOfInterest(pointOfInterest);
}
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Server error: " + error.getMessage());
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
SQLiteHandler sqLiteHandler = new SQLiteHandler(AppController.getInstance());
User user = sqLiteHandler.getUserDetails();
params.put("user_id", user.getUserID());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}//end method getPOISFromDB
}
How do I display the markers in onMapReady method i.e populate the singleton before the onMapReady method executes?
Thanks to #soham, I changed to initialize my adapter after the response from the api. Removed this line of code in onCreate in the MainActivity
#Override
public void onCreate(Bundle savedInstanceState) {
......
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
.......
}
Placed it in method getPOISFromDB()
......
#Override
public void onResponse(String response) {
Log.d(TAG, "Response: " + response);
try {
JSONObject jObj = new JSONObject(response);
JSONArray jsonArray = jObj.getJSONArray("pois");
PointOfInterestLab.get(AppController.getInstance()).clearPointOfInterests();
for(int i=0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
int id = jsonObject.getInt("poi_id");
String name = jsonObject.getString("name");
String summary = jsonObject.getString("summary");
double latitude = jsonObject.getDouble("latitude");
double longitude = jsonObject.getDouble("longitude");
Location location = new Location("dummyProvider");
location.setLatitude(latitude);
location.setLongitude(longitude);
PointOfInterest pointOfInterest = new PointOfInterest(id, name, summary
, location);
PointOfInterestLab.get(AppController.getInstance()).addPointOfInterest(pointOfInterest);
}
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
}
viewPager.setAdapter(new MyAdapter(getSupportFragmentManager()));
}
.....
Related
So I am new to programming, what I'm trying to do is to make an android app that will give to the client the nearest and fastest way to go the cinema/gas station/market. The problem with that is that first I need to find the nearest places via google api get the lats and lngs and then use them to direction api.
So what I have done is this:
GetNearbyPlaces.java
public class GetNearbyPlaces extends AsyncTask<Object, String, String>
{
private String googleplaceData, url;
private GoogleMap mMap;
#Override
protected String doInBackground(Object... objects)
{
mMap = (GoogleMap) objects[0];
url = (String) objects[1];
DownloadUrl downloadUrl = new DownloadUrl();
try
{
googleplaceData = downloadUrl.ReadTheURL(url);
}
catch (IOException e)
{
e.printStackTrace();
}
return googleplaceData;
}
#Override
protected void onPostExecute(String s)
{
List<HashMap<String, String>> nearbyPlacesList = null;
DataParser dataParser = new DataParser();
nearbyPlacesList = dataParser.parse(s);
DisplayNearbyPlaces(nearbyPlacesList);
super.onPostExecute(s);
Log.d("Message","telos execute ");
}
private void DisplayNearbyPlaces(List<HashMap<String, String>> nearbyPlacesList)
{
for (int i=0; i<nearbyPlacesList.size();
{
Log.d("Message","mesa stin for ");
MarkerOptions markerOptions = new MarkerOptions();
HashMap<String, String> googleNearbyPlace = nearbyPlacesList.get(i);
String nameOfPlace = googleNearbyPlace.get("place_name");
String vicinity = googleNearbyPlace.get("vicinity");
double lat = Double.parseDouble(googleNearbyPlace.get("lat"));
double lng = Double.parseDouble(googleNearbyPlace.get("lng"));
LatLng latLng = new LatLng(lat, lng);
markerOptions.position(latLng);
markerOptions.title(nameOfPlace + " : " + vicinity);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW));
mMap.addMarker(markerOptions);
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(10));
}
}
}
GoogleUserMaps.java
case R.id.gasstation_nearby:
mMap.clear();
mMap.addMarker(userLocMarkerOptions);
mMap.setTrafficEnabled(true);
url = getUrl(latitude,longitude,gasStation);
transferData[0] = mMap;
transferData[1] = url;
Log.d("Message","Prin kanei execute ");
getNearbyPlaces.execute(transferData);
Log.d("Message","afou kanei execute ");
Toast.makeText(this, "Searching for Nearby Gas Stations.", Toast.LENGTH_SHORT).show();
Toast.makeText(this, "Showing for Nearby Gas Stations.", Toast.LENGTH_SHORT).show();
break;
I want to add something inside DisplayNearbyPlaces a public variable in order to get the lats and lngs but when I use it after the getNearbyPlaces.execute(transferData).get(); of GoogleUserMaps.java it gives me 0 result. I can't understand AsyncTask. Is there any way that I can wait until
getNearbyPlaces.execute(transferData).get(); finishes the execute in order to get the lats and lngs ?
Thank you in advance !
my logcat
I want to get the result after the execute finishes as I show you in the image bellow
Async task runs in a seperate thread than UI thread. It would be better if you try to use a Callback interface and when onPost of Async task is called perform the action in that callback
#Strack I am posting code example of interface
`
public interface PlaceListener{
void searchStarted();
void searchEnded();
}
public class GetNearbyPlaces extends AsyncTask<Object, String, String>
{
private String googleplaceData, url;
private GoogleMap mMap;
private PlaceListener placeListener;
public GetNearbyPlaces(PlaceListener placeListener){
this.placeListner = placeListener;
}
//onPreExecute(){ placeListner.searchStarted();}
#Override
protected String doInBackground(Object... objects)
{
mMap = (GoogleMap) objects[0];
url = (String) objects[1];
DownloadUrl downloadUrl = new DownloadUrl();
try
{
googleplaceData = downloadUrl.ReadTheURL(url);
}
catch (IOException e)
{
e.printStackTrace();
}
return googleplaceData;
}
#Override
protected void onPostExecute(String s)
{
placeListner.serachEnded();
List<HashMap<String, String>> nearbyPlacesList = null;
DataParser dataParser = new DataParser();
nearbyPlacesList = dataParser.parse(s);
DisplayNearbyPlaces(nearbyPlacesList);
super.onPostExecute(s);
Log.d("Message","telos execute ");
}
}
`
Implement the PlaceListener in Caller Activity and perform your desired task in searchEnded. You can make the parameters of your interface methods according to your desired data you want to pass
ok. create a new call back:
public interface MyCallBack {
void myTaskDone(List<HashMap<String, String>> nearbyPlacesList);
}
create one in your activity:
MyCallBack myCallBack = new MyCallBack() {
#Override
public void myTaskDone(List<HashMap<String, String>> nearbyPlacesList) {
DisplayNearbyPlaces(nearbyPlacesList);
}
};
send a reference to the Aynctask:
getNearbyPlaces.execute(transferData, map, new MyCallBack() {
#Override
public void myTaskDone(List<HashMap<String, String>> nearbyPlacesList) {
DisplayNearbyPlaces(nearbyPlacesList);
}
});
complete code:
public interface MyCallBack {
void myTaskDone(List<HashMap<String, String>> nearbyPlacesList);
}
public class GetNearbyPlaces extends AsyncTask<Object, String, String> {
private String googleplaceData;
private String url;
private GoogleMap mMap;
private MyCallBack myCallBack;
public GetNearbyPlaces(String url, GoogleMap mMap, MyCallBack myCallBack) {
this.url = url;
this.mMap = mMap;
this.myCallBack = myCallBack;
}
#Override
protected String doInBackground(Object... objects) {
DownloadUrl downloadUrl = new DownloadUrl();
try {
googleplaceData = downloadUrl.ReadTheURL(url);
} catch (IOException e) {
e.printStackTrace();
}
return googleplaceData;
}
#Override
protected void onPostExecute(String s) {
List<HashMap<String, String>> nearbyPlacesList = null;
DataParser dataParser = new DataParser();
nearbyPlacesList = dataParser.parse(s);
myCallBack.myTaskDone(nearbyPlacesList);
}
}
private void DisplayNearbyPlaces(List<HashMap<String, String>> nearbyPlacesList) {
/// your code
}
MyCallBack myCallBack = new MyCallBack() {
#Override
public void myTaskDone(List<HashMap<String, String>> nearbyPlacesList) {
DisplayNearbyPlaces(nearbyPlacesList);
}
};
How to call it:
//.....
transferData[0] = mMap;
transferData[1] = url;
Log.d("Message","Prin kanei execute ");
GetNearbyPlaces getNearbyPlaces = new GetNearbyPlaces(transferData, map, myCallBack);
getNearbyPlaces.execute();
//or:
GetNearbyPlaces getNearbyPlaces = new GetNearbyPlaces(transferData, map, new MyCallBack() {
#Override
public void myTaskDone(List<HashMap<String, String>> nearbyPlacesList) {
DisplayNearbyPlaces(nearbyPlacesList);
}
});
getNearbyPlaces.execute();
I want to get string from arraylist inside oncreateview fragment but i cant figure itout since no position index has been pass. get(position) return error.
String price = arrayList.get(position).getPrice();
i need to get string price and settext for price.this is my main concern.
this values should return from arraylist.
this is response JSON array from volley using mysingleton.
Single Product Response: [{"price":"75","date":"2017-07-13 03:25:31","pk_i_id":"4"}]
this main activty fragment
public class MainActivityFragment extends Fragment {
private TextView product,price,date,title;
private String product_id;
ArrayList<ProductItem> arrayList = new ArrayList<>();
Context context;
public MainActivityFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main_activity, container, false);
product = (TextView) view.findViewById(R.id.tv1);
title = (TextView) view.findViewById(R.id.tvTitle);
price = (TextView) view.findViewById(R.id.tvPrice);
date = (TextView) view.findViewById(R.id.tvDate);
if (getArguments() != null) {
Log.i(TAG, "getArgument is not null");
product_id = getArguments().getString("product_id");
ProductBackgroundTask productBackgroundTask = new ProductBackgroundTask(this.getActivity(), product_id);
arrayList = productBackgroundTask.getList();
String price = arrayList.get(position).getPrice();
// Log.d(TAG, "price: " + price);
product.setText(product_id);
// price.setText(price);
}else {
Log.i(TAG, "getArgument is null");
}
return view;
}
}
this is task to get arraylist using volley
public class ProductBackgroundTask {
private Context context;
ArrayList<ProductItem> arrayList = new ArrayList<>();
String json_url = "phpfile.php";
private String product_id;
public ProductBackgroundTask(Context context, String product_id) {
this.context = context;
this.product_id = product_id;
}
public ArrayList<ProductItem> getList(){
StringRequest stringRequest = new StringRequest(Request.Method.POST, json_url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, "Single Product Response: " + response);
try {
JSONArray jsonarr = new JSONArray(response);
for (int i = 0; i < jsonarr.length(); i++) {
JSONObject jsonobj = jsonarr.getJSONObject(i);
ProductItem productItem = new ProductItem(jsonobj.getString("price"), jsonobj.getString("date"), jsonobj.getInt("pk_i_id"));
arrayList.add(productItem);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("product_id", product_id);
return params;
}
};
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
return arrayList;
}
}
and this is class of array list
public class ProductItem {
private String Price,Date;
private int ProductId;
public ProductItem(String Price, String Date, int ProductId){
this.setPrice(Price);
this.setDate(Date);
this.setProductId(ProductId);
}
public int getProductId() {
return ProductId;
}
public void setProductId(int productId) {
ProductId = productId;
}
public String getPrice() {
return Price;
}
public void setPrice(String price) {
Price = price;
}
public String getDate() {
return Date;
}
public void setDate(String date) {
Date = date;
}
Clearly in your oncreate you haven’t initialized the product item and you cannot parse the complete list.You can try two to solve this
1.Pass specific item number instead of position i.e
say if you want to show 4th item then position=3
2.Or write a loop like this to parse entire arrayList like this
for(ProductItem productItem:arrayList){
String price = productItem.getPrice();
// Log.d(TAG, "price: " + price);
product.setText(product_id);
price.setText(price);
}
Mistake you're doing is that in the MainActivityFragment your trying to assign the value to the arrayList even before the data is added to the arrayList in the ProductBackgroundTask-getList. That's the reason you are getting the list null all the time. Try to use interfaces.
1.Make your MainActivityFragment implement the interface.
2.Set the value to the interface method once you get the data from the server.
3.Get the data in the MainActivityFragment inside interface method and do all the operation you're doing inside the onCreateView method.
Now your arraylist will have the data whatever you received from the server.
Below is the link for the example on interfaces if you haven't used them before. He is doing exactly as your requirement.
https://www.justinmccandless.com/post/setting-up-a-callback-function-in-android/
Allow me. The arrayList that you return from getList isn't populated at the time you call String price = arrayList.get(position).getPrice();. The server call using volley takes some time to process and that's when the onResponse gets called. This happens AFTER you've returned the arrayList which is in fact empty.
The sequence of events is as follows.
• Call to arrayList = productBackgroundTask.getList(); which returns an empty ArrayList.
• String price = arrayList.get(position).getPrice();
Now after a while..
• onResponse inside getList() gets called.
Do you now see why it's empty?
Simple Solution: • Define a simple interface ProductListener alongside ProductBackgroundTask. (With only a single abstract method onProducts).
• Instantiate it inside the Fragment's onCreateView using an anonymous class and pass it to the constructor of ProductListener to save it for later use. Do whatever you want to do with the products inside the onProducts method. (Since that will be called with the actual data)
• Call its onProducts method with the data that's parsed and fetched inside the onResponse method.
ProductBackgroundTask code:
public class ProductBackgroundTask {
private Context context;
// I removed the instance ArrayList since that can be made
// local.
// Here, we add a reference to our callback interface as we can use it later.
private ProductListener listener;
String json_url = "http://192.168.43.55/android/v1/productList.php";
private String product_id;
// Instantiate this class using an additional listener argument
// which would be a concrete implementation of our interface.
public ProductBackgroundTask(Context context, String product_id, ProductListener listener) {
this.context = context;
this.product_id = product_id;
this.listener = listener;
}
// getList should not return anything,
// so I keep the return as void.
public void getList() {
StringRequest stringRequest = new StringRequest(Request.Method.POST, json_url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
ArrayList<ProductItem> arrayList = new ArrayList<>();
Log.d(TAG, "Single Product Response: " + response);
try {
JSONArray jsonarr = new JSONArray(response);
for (int i = 0; i < jsonarr.length(); i++) {
JSONObject jsonobj = jsonarr.getJSONObject(i);
ProductItem productItem = new ProductItem(jsonobj.getString("price"), jsonobj.getString("date"), jsonobj.getInt("pk_i_id"));
arrayList.add(productItem);
}
// Notice this line here, this is what
// calls the callback with the products.
listener.onProducts(arrayList);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("product_id", product_id);
return params;
}
};
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
}
// Callback interface, we would need a concrete implementation
// of this and pass that to the constructor of ProductBackgroundTask.
interface ProductListener {
void onProducts(ArrayList<ProductItem> products);
}
The code inside onCreateView:
ProductBackgroundTask productBackgroundTask = new ProductBackgroundTask(this.getActivity(), product_id, new ProductListener() {
// This method will be called with the needed products.
// Give an anonymous class implementation of our interface
// right here since we won't be using it anymore.
public void onProducts(ArrayList<ProductItem> products) {
// Get the price you want.
String str = arrayList.get(0).getPrice();
// Use str wherever necessary. Use the UI thread here if you need
// to change any visible elements on the screen.
}
});
// Simply call this method to get the ball rolling.
productBackgroundTask.getList();
This is a concrete implementation of the this answer and you won't be changing much code.
I have written a program in which on button click i am getting the nearby atms from json . This is the link
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=19.052696,72.8713694&radius=1000&types=atm&sensor=true&key=AIzaSyA8szrI9Ue4EwyUwTgz7Nk0c39qMal0pN4
I want to plot the atms on google map but the problem is only the last atm is being displayed on the map
Code : Method to get the atm names , latitude , longitude and vicinity
public void showAtm(){
String getAtmUrl =
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?
location="+lat+","+lng+"&radius=1000&types=atm&sensor=true
&key=AIzaSyA8szrI9Ue4EwyUwTgz7Nk0c39qMal0pN4";
try{
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(getAtmUrl).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Map_Activity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getBaseContext(), "Request to atm
locations failed", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onResponse(Call call, Response response) throws
IOException {
Log.i("response ", "onResponse(): " + response);
String result = response.body().string();
Log.i("result",result);
try{
JSONObject jsonObject = new JSONObject(result);
String resultData = jsonObject.getString("results");
JSONArray urlDetails = new JSONArray(resultData);
for (int i = 0 ; i < urlDetails.length(); i++){
JSONObject json = urlDetails.getJSONObject(i);
geometry = json.getString(GOEMETRY);
vicinity = json.getString(VICINITY);
JSONObject jsonGeometry = new JSONObject(geometry);
String geoLocation =
jsonGeometry.getString(LOCATION);
JSONObject jsonLatLng = new JSONObject(geoLocation);
atmLat = jsonLatLng.getDouble(LATITUDE);
atmLong = jsonLatLng.getDouble(LONGITUDE);
atmName = json.getString(ATM_NAME);
Log.i("JsonArrayAtm", "" + atmName);
Log.i("JsonArrayGeometry",geometry);
Log.i("LatLong",""+atmLat+" , "+atmLong);
Log.i("Vicinity", vicinity);
runOnUiThread(new Runnable() {
#Override
public void run() {
moveAtmMap(atmLat ,atmLong );
}
});
}
}catch (Exception e){
e.printStackTrace();
}
}
});
}catch (Exception e){
e.printStackTrace();
}
}
///////////////////////////// atm locations map ///////////////////
private void moveAtmMap(Double amtLatitude,Double atmLongitude){
fragment.getMap().clear();
CameraPosition position = CameraPosition.builder()
.target(new LatLng(amtLatitude, atmLongitude))
.zoom(16f)
.bearing(0.0f)
.tilt(0.0f)
.build();
String msg = amtLatitude+ ", " + atmLongitude;
LatLng latLng = new LatLng(amtLatitude, atmLongitude);
fragment.getMap().addMarker(new MarkerOptions()
.position(latLng));
fragment.getMap().setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter()
{
#Override
public View getInfoWindow(Marker marker) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
View v = getLayoutInflater().inflate(R.layout.atm_custom_window,
null);
TextView atmHeader = (TextView) v.findViewById(R.id.atmName);
TextView atmLocation = (TextView)
v.findViewById(R.id.atmLocation);
atmHeader.setText(atmName);
atmLocation.setText(vicinity);
return v;
}
});
fragment.getMap().setMapType(GoogleMap.MAP_TYPE_NORMAL);
fragment.getMap().setTrafficEnabled(true);
fragment.getMap().setMyLocationEnabled(true);
fragment.getMap().animateCamera(CameraUpdateFactory
.newCameraPosition(position), null);
}
How do i achieve the above , can anyone suggest me ?
Thanks
You have written the method as,
private void moveAtmMap(Double amtLatitude,Double atmLongitude){
fragment.getMap().clear();
...
}
so every time this method will be called, it will clear all previous markers and you will end up having only the last marker.
Edit
for (int i = 0 ; i < urlDetails.length(); i++){
JSONObject json = urlDetails.getJSONObject(i);
String geometry = json.getString(GOEMETRY);
String vicinity = json.getString(VICINITY);
JSONObject jsonGeometry = new JSONObject(geometry);
String geoLocation = jsonGeometry.getString(LOCATION);
JSONObject jsonLatLng = new JSONObject(geoLocation);
double atmLat = jsonLatLng.getDouble(LATITUDE);
double atmLong = jsonLatLng.getDouble(LONGITUDE);
String atmName = json.getString(ATM_NAME);
runOnUiThread(new Runnable() {
#Override
public void run() {
moveAtmMap(atmLat, atmLong, atmName, vicinity, geometry);
}
});
}
and change method like,
private void moveAtmMap(Double amtLatitude,Double atmLongitude, String name, String vicinity, String geometry)
If you want to clear markers from previous web service hit, then do it before you start adding markers for new service hit, like before the for loop.
Remove this line from the method moveAtmMap:
fragment.getMap().clear();
For the first time, write it before for loop in onResponse.
Try this simple code,
for(int i=0;i<jsonArray.length();i++){
MarkerOptions markerOptions;
markerOptions = new MarkerOptions().position(new LatLng(lattitude,
longitude)
).title("Title").snippet("This is snippet");
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_icon));
marker = googleMap.addMarker(markerOptions);
}
Just replace lattitude,longitude with your values.
If you want to have instance of each marker,then you can put each "marker" object into hashmap with key as marker id. Let me know your feedback.
Note: remove this line - fragment.getMap().clear(); because it will clear map everytime when compiler comes into loop and it will take only last object. this is what happenng right now.
How can i pass the position of item using intent to start a new activity?
I want to start a new activity called single which displays the rating of the movie correspondingly..pls help
I have been trying this for the past two days.
Here is the code:
public class NowPlaying extends Fragment {
private static final String TAG = NowPlaying.class.getSimpleName();
// Movies json url
private static final String url = "http://private-8149-themoviedb.apiary-mock.com/3/movie/now_playing?api_key=";
private ProgressDialog pDialog;
private List<NowPlayingInfo> bottom = new ArrayList<NowPlayingInfo>() ;
NowPlayingAdapter adapter;
RecyclerView recyclerView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_main, container, false);
ActionBar toolbar = ((AppCompatActivity) getActivity()).getSupportActionBar();
toolbar.setTitle("Now playing");
recyclerView = (RecyclerView) v.findViewById(R.id.cardList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new NowPlayingAdapter(getActivity(), bottom);
recyclerView.setAdapter(adapter);
pDialog = new ProgressDialog(getActivity());
pDialog.setMessage("Loading...");
pDialog.show();
adapter.SetOnItemClickListener(new NowPlayingAdapter.OnItemClickListener() {
#Override
public void onItemClick(View v, int position) {
// do something with position
Intent i = new Intent(v.getContext(), Single.class);
//pass the position of the item to single class
v.getContext().startActivity(i);
}
});
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, response.toString());
hidePDialog();
try {
JSONArray jsonArray = response.getJSONArray("results");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
NowPlayingInfo trailer = new NowPlayingInfo();
trailer.setTitle(jsonObject.getString("original_title"));
String iss = "http://image.tmdb.org/t/p/w500" + jsonObject.getString("poster_path") ;
trailer.setImage(iss);
bottom.add(trailer);
adapter.notifyDataSetChanged();
}
} catch (JSONException e) {
e.printStackTrace();
}
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
hidePDialog();
}
});
AppController.getInstance().addToRequestQueue(jsonObjectRequest);
return v;
}
#Override
public void onDestroy() {
super.onDestroy();
hidePDialog();
}
private void hidePDialog() {
if (pDialog != null) {
pDialog.dismiss();
pDialog = null;
}
}
}
adapter.SetOnItemClickListener(new NowPlayingAdapter.OnItemClickListener() {
#Override
public void onItemClick(View v, int position) {
NowPlayingInfo _nowPlaying = bottom.get(position);
// do something with position
Intent i = new Intent(v.getContext(), Single.class);
//pass the position of the item to single class
i.putExtra("ISS", _nowPlaying.getImage()); //you can put your current playing info.
i.putExtra("POSITION", position); //you can put your position to next activity.
v.getContext().startActivity(i);
}
});
Add this in your SingleInfo Class.
String _rating = "";
public String get_rating() {
return _rating;
}
public void set_rating(String _rating) {
this._rating = _rating;
}
Add this in your Single class -
int _currentPos = 0 ; //Global variable .
_currentPos = getIntent().getIntExtra("position", 0);// paste this in onCreate()
Add this code in onResponse of Single Class -
try {
JSONArray jsonArray = response.getJSONArray("results");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
SingleInfo s = new SingleInfo();
s.set_rating(jsonObject.getString("rating"));
single.add(s);
}
//changed by Shoeb
SingleInfo _singleInfo = single.get(_currentPos); //position from previous activity
textView.setText(_singleInfo.get_rating());
//end changes
} catch (JSONException e) {
e.printStackTrace();
}
Add an extra to your intent
i.putExtra("position",position);
And on the other activity:
getIntent().getIntExtra("position", 0);
I am following an Android weather app tutorial where the weather app requests a wether information about a place using an API;
However, the app was not "location aware" so I decided to try to do that, but when my app is created it calls the API with the hardcoded coordinates (private latitude and longitude). I dont understand why thid is happening, I am calling the getLoation method before the getForecast method. getLocation method should use the location manager's internet provider and set the latitude and longitude before the getForecast in called (which uses the coordinates to make the asynchronous API call). The strange thing is that when I press the refresh button, the location's coordinates are obtained from the location manager and the getLocationName is working fine too (finds the name of the location using the coordinates and set's the locationLabel to the name).
I suspect e problem is because if the fact that OkHTTP API that I am using to make the asynchronous call uses worker thread.
NODE: I am using Butter knife and YoYo API's also I have four classes which store the information about the weather called Forecast, Hour and Current and Day. I did not include them because I thought it is not important, since the problem is in my main activity class
Here is the code in my main activity class:
public static final String TAG = MainActivity.class.getSimpleName();
private Forecast mForecast;
//default coordinates - Aberdeen, UK Lati:57.156866 ; Long:
private double latitude = 57.156866;
private double longitude = -2.094278;
private LocationManager locationManager;
#InjectView(R.id.timeLabel) TextView mTimeLabel;
#InjectView(R.id.temperatureLabel) TextView mTemperatureLabel;
#InjectView(R.id.humidityValue) TextView mHumidityValue;
#InjectView(R.id.precipValue) TextView mPrecipValue;
#InjectView(R.id.summaryLabel) TextView mSummaryLabel;
#InjectView(R.id.locationLabel) TextView mLocationLabel;
#InjectView(R.id.windSpeedValue) TextView mWindSpeedValue;
#InjectView(R.id.iconImageView) ImageView mIconImageView;
#InjectView(R.id.refreshImageView) ImageView mRefreshImaveView;
#InjectView(R.id.progressBar) ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getLocation();
getForecast(latitude, longitude);
}
});
getLocation();
getForecast(latitude, longitude);
}
#Override
protected void onResume() {
super.onResume();
getForecast(latitude, longitude);
}
private void getForecast(double latitude, double longitude) {
//animations
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mLocationLabel);
YoYo.with(Techniques.FadeIn).duration(1600).playOn(mTemperatureLabel);
YoYo.with(Techniques.FadeIn).duration(1800).playOn(mIconImageView);
YoYo.with(Techniques.FadeIn).duration(1000).playOn(mSummaryLabel);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mHumidityValue);
YoYo.with(Techniques.FadeIn).duration(1400).playOn(mWindSpeedValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mPrecipValue);
YoYo.with(Techniques.FadeIn).duration(1200).playOn(mTimeLabel);
String API_KEY = "API_KEY";
String forecast = "https://api.forecast.io/forecast/"+ API_KEY +"/"+ latitude+","+ longitude+"?units=auto";
if(isNetworkAvailable()) {
toggleRefresh();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(forecast)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
alertUserAboutError();
}
//when the call to the Okhttp library finishes, than calls this method:
#Override
public void onResponse(Response response) throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
toggleRefresh();
}
});
try {
String jsonData = response.body().string();
//Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mForecast = parseForecastDetails(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
} else {
alertUserAboutError();
}
} catch (IOException | JSONException e) {
Log.e(TAG, "Exception caught:", e);
}
}
});
}else{
//Toast.makeText(this,getString(R.string.network_unavailable_message),Toast.LENGTH_LONG).show();
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private void toggleRefresh() {
if(mProgressBar.getVisibility() == View.INVISIBLE){
mProgressBar.setVisibility(View.VISIBLE);
mRefreshImaveView.setVisibility(View.INVISIBLE);
}else{
mProgressBar.setVisibility(View.INVISIBLE);
mRefreshImaveView.setVisibility(View.VISIBLE);
}
}
//updates the dysplay with the data in the CUrrentWeather locaal object
private void updateDisplay() {
Current current = mForecast.getCurrent();
//setting the current weather details to the ui
mTemperatureLabel.setText(current.getTemperature()+"");
mTimeLabel.setText("At "+ current.getFormattedTime()+" it will be");
mHumidityValue.setText(current.getHumidity() +"%");
mPrecipValue.setText(current.getPrecipChange()+"%");
mSummaryLabel.setText(current.getSummery());
mWindSpeedValue.setText(current.getWindSpeed()+"");
mLocationLabel.setText(current.getTimeZone());
//sets the mLocationLavel to the appropriate name and not the timezome from the API
getLocationName();
Drawable drawable = ContextCompat.getDrawable(this, current.getIconId());
mIconImageView.setImageDrawable(drawable);
}
private Forecast parseForecastDetails(String jsonData) throws JSONException {
Forecast forecast = new Forecast();
forecast.setCurrent(getCurrentDetails(jsonData));
forecast.setHourlyForecast(getHourlyForecast(jsonData));
forecast.setDailyForecast(getDailyForecast(jsonData));
return forecast;
}
private Day[] getDailyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject daily = forecast.getJSONObject("daily");
JSONArray data = daily.getJSONArray("data");
Day[] days = new Day[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonDay = data.getJSONObject(i);
Day day = new Day();
day.setSummary(jsonDay.getString("summary"));
day.setIcon(jsonDay.getString("icon"));
day.setTemperatureMax(jsonDay.getDouble("temperatureMax"));
day.setTime(jsonDay.getLong("time"));
day.setTimezone(timezone);
days[i] = day;
Log.v(MainActivity.class.getSimpleName(),days[i].getIcon());
}
return days;
}
private Hour[] getHourlyForecast(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
JSONObject hourly = forecast.getJSONObject("hourly");
JSONArray data = hourly.getJSONArray("data");
Hour[]hours = new Hour[data.length()];
for(int i = 0;i < data.length();i++){
JSONObject jsonHour = data.getJSONObject(i);
Hour hour = new Hour();
hour.setSummary(jsonHour.getString("summary"));
hour.setTemperature(jsonHour.getDouble("temperature"));
hour.setIcon(jsonHour.getString("icon"));
hour.setTime(jsonHour.getLong("time"));
hour.setTimezone(timezone);
hours[i] = hour;
}
return hours;
}
/*
* throws JSONException, doing it like that, we place the
* responsability of handaling this exeption to the caller of the method
*/
private Current getCurrentDetails(String jsonData) throws JSONException{
JSONObject forecast = new JSONObject(jsonData);
String timezone = forecast.getString("timezone");
Log.i(TAG,"From JSON: " + timezone);
JSONObject currently = forecast.getJSONObject("currently");
Current mCurrent = new Current();
mCurrent.setHumidity(currently.getDouble("humidity"));
mCurrent.setTime(currently.getLong("time"));
mCurrent.setIcon(currently.getString("icon"));
mCurrent.setPrecipChange(currently.getDouble("precipProbability"));
mCurrent.setSummery(currently.getString("summary"));
mCurrent.setTemperature(currently.getDouble("temperature"));
mCurrent.setTimeZone(timezone);
mCurrent.setWindSpeed(currently.getDouble("windSpeed"));
Log.d(TAG, mCurrent.getFormattedTime());
return mCurrent;
}
private boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
//contition to check if there is a network and if the device is connected
if(networkInfo != null && networkInfo.isConnected()){
isAvailable = true;
}
return isAvailable;
}
private void alertUserAboutError() {
AlertDIalogFragment dialog = new AlertDIalogFragment();
dialog.show(getFragmentManager(),getString(R.string.error_dialog_text));
}
private void getLocation(){
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if(isNetworkAvailable()){
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1, 1000, new MyLocationListener());
}else{
WIFIDialogFragment dialog = new WIFIDialogFragment();
dialog.show(getFragmentManager(), getString(R.string.error_dialog_text));
}
}
private class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
latitude = loc.getLatitude();
longitude = loc.getLongitude();
Toast.makeText(MainActivity.this,
"Location changed: Lat: " + loc.getLatitude() + " Lng: "
+ loc.getLongitude(), Toast.LENGTH_SHORT).show();
locationManager.removeUpdates(this);
}
#Override
public void onProviderDisabled(String provider) {}
#Override
public void onProviderEnabled(String provider) {}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
}
private void getLocationName(){
Geocoder geo = new Geocoder(this, Locale.getDefault());
try {
List<Address> addressList = geo.getFromLocation(this.latitude,this.longitude,1);
if (addressList.isEmpty()){
//gets the default name from the timeZone
//that we set in as a local variable
}else{
if(addressList.size() > 0){
Log.v(MainActivity.class.getSimpleName(),addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName()+"");
mLocationLabel.setText(addressList.get(0).getLocality() + ", "+ addressList.get(0).getCountryName());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
This is a screenshot of the app:
By looking at your code, there are couple of things which are wrong.
you have called getLocation Method in OnCreate and onResume as well which is logically incorrect. you can keep it at onResume only if you wish you get location frequently.
when we call getLocation method it's not guarantee to give the latitude and longitude straight away, it takes it's own sweet time depending on the providers (Read API of location manager) hence your first call for getForecast will probably fail.
Solution : you can call getForecast method when onLocationChange method is called, by that time you can show progressDialog.
Make sure you have given Location related permissions in the manifest file