Issues running/debugging mapbox android code examples - mapbox-android

I am trying to get started with mapbox android and can't get any of the example projects to work.
My problem is with the imports
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxandroiddemo.R;
I get I "cannot resolve symbol annotation", "cannot resolve symbol v7" and "cannot resolve symbol mapboxandroiddemo".
I feel like this is some android problem that I am just not understanding correctly so if anyone has some insight that would be amazing. I have tried taking out some code and using the recommended bug fixes but all that has done is break my project.
Here is the entire MainActivity.java file
package com.example.mapboxtut;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOffset;
/**
* Use the {#link MapView#addOnStyleImageMissingListener(MapView.OnStyleImageMissingListener)}
* to handle the situation where a SymbolLayer tries using a missing image as an icon. If an icon-image
* cannot be found in a map style, a custom image can be provided to the map via
* the listener.
*/
public class MissingIconActivity extends AppCompatActivity {
private static final String ICON_SOURCE_ID = "ICON_SOURCE_ID";
private static final String ICON_LAYER_ID = "ICON_LAYER_ID";
private static final String PROFILE_NAME = "PROFILE_NAME";
private static final String CARLOS = "Carlos";
private static final String ANTONY = "Antony";
private static final String MARIA = "Maria";
private static final String LUCIANA = "Luciana";
private MapView mapView;
private MapboxMap mapboxMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Mapbox access token is configured here. This needs to be called either in your application
// object or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token));
// This contains the MapView in XML and needs to be called after the access token is configured.
setContentView(R.layout.activity_styles_missing_icon);
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(#NonNull final MapboxMap mapboxMap) {
// Add Features which represent the location of each profile photo SymbolLayer icon
Feature carlosFeature = Feature.fromGeometry(Point.fromLngLat(-7.9760742,
41.2778064));
carlosFeature.addStringProperty(PROFILE_NAME, CARLOS);
Feature antonyFeature = Feature.fromGeometry(Point.fromLngLat(-8.0639648,
37.5445773));
antonyFeature.addStringProperty(PROFILE_NAME, ANTONY);
Feature mariaFeature = Feature.fromGeometry(Point.fromLngLat(-9.1845703,
38.9764924));
mariaFeature.addStringProperty(PROFILE_NAME, MARIA);
Feature lucianaFeature = Feature.fromGeometry(Point.fromLngLat(-7.5146484,
40.2459915));
lucianaFeature.addStringProperty(PROFILE_NAME, LUCIANA);
// Use a URL to build and add a Style object to the map. Then add a source to the Style.
mapboxMap.setStyle(
new Style.Builder().fromUrl(Style.LIGHT)
.withSource(new GeoJsonSource(ICON_SOURCE_ID,
FeatureCollection.fromFeatures(new Feature[] {
carlosFeature,
antonyFeature,
mariaFeature,
lucianaFeature}))),
new Style.OnStyleLoaded() {
#Override
public void onStyleLoaded(#NonNull Style style) {
MissingIconActivity.this.mapboxMap = mapboxMap;
// Add a SymbolLayer to the style. iconImage is set to a value that will
// be used later in the addOnStyleImageMissingListener below
style.addLayer(new SymbolLayer(ICON_LAYER_ID, ICON_SOURCE_ID).withProperties(
iconImage(get(PROFILE_NAME)),
iconIgnorePlacement(true),
iconAllowOverlap(true),
textField(get(PROFILE_NAME)),
textIgnorePlacement(true),
textAllowOverlap(true),
textOffset(new Float[] {0f, 2f})
));
}
});
}
});
// Use the listener to match the id with the appropriate person. The correct profile photo is
// given to the map during "runtime".
mapView.addOnStyleImageMissingListener(new MapView.OnStyleImageMissingListener() {
#Override
public void onStyleImageMissing(#NonNull String id) {
switch (id) {
case CARLOS:
addImage(id, R.drawable.carlos);
break;
case ANTONY:
addImage(id, R.drawable.antony);
break;
case MARIA:
addImage(id, R.drawable.maria);
break;
case LUCIANA:
addImage(id, R.drawable.luciana);
break;
default:
addImage(id, R.drawable.carlos);
break;
}
}
});
}
private void addImage(String id, int drawableImage) {
Style style = mapboxMap.getStyle();
if (style != null) {
style.addImageAsync(id, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(drawableImage)));
}
}
// Add the mapView lifecycle to the activity's lifecycle methods
#Override
public void onResume() {
super.onResume();
mapView.onResume();
}
#Override
protected void onStart() {
super.onStart();
mapView.onStart();
}
#Override
protected void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Related

Deleting Data From Database Through JavaFX GUI

What I want to be able to do is load my database through a table view select an item and have it deleted into the database. I do not have users entering the id of a particular song so it makes it harder for me to accomplish this. I have the GUI set up and all the code that I have so far.
GUI CODE:
SongContent Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package playmymusic;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author man
*/
public class SongContent
{
private final StringProperty artist;
private final StringProperty title;
private final StringProperty genre;
private final IntegerProperty id;
public SongContent(int id, String artist, String title, String genre)
{
this.artist = new SimpleStringProperty(artist);
this.title = new SimpleStringProperty(title);
this.genre = new SimpleStringProperty(genre);
this.id = new SimpleIntegerProperty(id);
}
public Integer getId()
{
return id.get();
}
public void setID(int paramId)
{
id.set(paramId);
}
public String getArtist()
{
return artist.get();
}
public void setArtist(String paramArtist)
{
artist.set(paramArtist);
}
public String getTitle()
{
return title.get();
}
public void setTitle(String paramTitle)
{
title.set(paramTitle);
}
public String getGenre()
{
return genre.get();
}
public void setGenre(String paramGenre)
{
genre.set(paramGenre);
}
public StringProperty artistProperty(){return artist;}
public StringProperty titleProperty(){return title;}
public StringProperty genreProperty(){return genre;}
public IntegerProperty idProperty() { return id;}
}
Controller Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package playmymusic;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;
import javafx.beans.property.IntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javax.swing.JOptionPane;
import org.apache.derby.jdbc.ClientDriver;
/**
*
* #author man
*/
public class FXMLDocumentController implements Initializable {
public LoginModel loginModel = new LoginModel();
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private TextField txtArtist;
#FXML
private TextField fxTitle;
#FXML
private TextField fxGenre;
#FXML
private TableView<SongContent> tableView;
#FXML
private TableColumn<SongContent, Integer> id;
#FXML
private TableColumn<SongContent, String> artist;
#FXML
private TableColumn<SongContent, String> title;
#FXML
private TableColumn<SongContent, String> genre;
private ObservableList<SongContent> data;
#FXML
private void Login(ActionEvent event) throws SQLException {
try {
if(loginModel.isLogin(txtUsername.getText(), txtPassword.getText()))
{
Stage primaryStage = new Stage();
FXMLLoader loader = new FXMLLoader();
Pane root = loader.load(getClass().getResource("PopUpWindow.fxml").openStream());
Scene scene = new Scene(root, 785, 809);
primaryStage.setScene(scene);
primaryStage.show();
PlayMyMusic.primaryStage.close();
}else
{
System.out.println("WOOPS");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#FXML
private void songs(ActionEvent e) throws SQLException, ClassNotFoundException
{
loginModel.insertSongs(txtArtist.getText(), fxTitle.getText(), fxGenre.getText());
try
{
int i = 1;
Connection conn = DriverManager.getConnection("jdbc:derby://localhost:1527/PlayMyMusicDB;user=test;password=test");
data = FXCollections.observableArrayList();
ResultSet rs = conn.createStatement().executeQuery("select * from Song");
while(rs.next())
{
data.add(new SongContent(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4)));
i++;
}
}catch(SQLException ex) {
System.err.println("Error" + ex);
}
id.setCellValueFactory(new PropertyValueFactory<>("id"));
artist.setCellValueFactory(new PropertyValueFactory<>("artist"));
title.setCellValueFactory(new PropertyValueFactory<>("title"));
genre.setCellValueFactory(new PropertyValueFactory<>("genre"));
tableView.setItems(null);
tableView.setItems(data);
txtArtist.clear();
fxTitle.clear();
fxGenre.clear();
}
#FXML
public void deleteItems(ActionEvent e) throws SQLException, ClassNotFoundException
{
Connection c = DriverManager.getConnection("jdbc:derby://localhost:1527/PlayMyMusicDB;user=test;password=test");
int action = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this item?");
if(action == 0)
{
try
{
IntegerProperty i = SongContent.idProperty();
ResultSet rs = c.createStatement().executeQuery("DELETE FROM Song where i = " + i);
}catch(Exception e1)
{
e1.printStackTrace();
}
}
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
}
}
`
Any explination of why this could not be deleting my data? I would also love it if someone explained to me a strategy of resetting the SongNumberID every time the GUI opened and closed. But, the main goal for me is to figure out how to delete songs.
Thanks so much
-Aaron
The result of calling toString on a SimpleIntegerProperty is something like IntegerProperty [value: 10]. You should use the value, not the IntegerProperty. Furthermore it's better to use a PreparedStatement to create the query. Also you should get the selected item from the table instead of trying to reference a instance method as if it was static:
SongContent song = tableView.getSelectionModel().getSelectedItem();
if (song != null) {
// there is a selection -> delete
...
PreparedStatement statement = c.prepareStatement("DELETE FROM Song WHERE i = ?");
statement.setInt(1, song.getId());
statement.executeUpdate();
...
}
Furthermore you should make sure i is actually the column name of the id column (and not id).

Image Location?

I am having trouble simply importing an image using this code. Where should the Image be stored? I thought it had to be in a folder within the source folder and in this case called ImageIcon but I am not sure... thanks to anybody who helps!
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
class Surface extends JPanel {
private Image mshi;
public Surface() {
loadImage();
setSurfaceSize();
}
private void loadImage() {
mshi = new ImageIcon("mushrooms.jpg").getImage();
}
private void setSurfaceSize() {
Dimension d = new Dimension();
d.width = mshi.getWidth(null);
d.height = mshi.getHeight(null);
setPreferredSize(d);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(mshi, 1, 1, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class DisplayImage extends JFrame {
public DisplayImage() {
initUI();
}
private void initUI() {
setTitle("Mushrooms");
add(new Surface());
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
DisplayImage ex = new DisplayImage();
ex.setVisible(true);
}
});
}
}
Do you try to put the image in the same directory as your class file Surface ?
Ok, sorry i had not seen the class DisplayImage.
The best way i think to do it, create a separate class file for Surface.
Write in Surface class file, a constructor with parameter. The parameter will be the image path. It will be simple for you to change the image if you want after.
Something like this :
public class Surface extends JPanel {
private Image mshi;
public Surface(String imagePath) {
mshi = new ImageIcon(imagePath).getImage();
setSurfaceSize();
}
private void setSurfaceSize() {
Dimension d = new Dimension();
d.width = mshi.getWidth(null);
d.height = mshi.getHeight(null);
setPreferredSize(d);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(mshi, 1, 1, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
You don't need to write the method loadImage(), it will be probably used in your class because it is private and it just do one simple thing.
Concerning the structure of your project directory make something like this
Project directory\src
Project directory\src\Surface.class
Project directory\src\DisplayImage.class
Project directory\images
Project directory\images\mushrooms.jpg
Ps : Sorry for my english, it still under construction.

Trying to send data from Android Tablet to Android Wear device and losing

I could really use some help. I'm having trouble getting my Android Wear device to recognize data changes made by my activity that runs on my Android tablet. I'm relatively new to Android and very new to the Wear APIs so I might be missing something completely obvious or trivial without realizing. I've pieced together a project from a couple of examples from the Android Wear doc pages and from the TwoToasters example on github. I just want to get data communication set up between the devices so that I can then edit the code in order to display images from my tablet on my Wear device. Ultimately, I want to be able to start/stop a slideshow on my Wear device from my tablet but I should be able to get there on my own once I get the data communication protocol set up and working so I'm really just looking for help with that. Here is my code so far:
Mobile:
package com.example.administrator.moto360displaycontrol;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import android.widget.ToggleButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MainActivity extends Activity {
private static final long CONNECTION_TIME_OUT_MS = 100;
private static final String ON_MESSAGE = "On!";
private static final String OFF_MESSAGE = "Off!";
private String message = null;
int count = 0;
private GoogleApiClient client;
private String nodeId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initApi();
setupWidgets();
}
/**
* Initializes the GoogleApiClient and gets the Node ID of the connected device.
*/
private void initApi() {
client = getGoogleApiClient(this);
retrieveDeviceNode();
}
/**
* Returns a GoogleApiClient that can access the Wear API.
* #param context
* #return A GoogleApiClient that can make calls to the Wear API
*/
private GoogleApiClient getGoogleApiClient(Context context) {
return new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.build();
}
/**
* Connects to the GoogleApiClient and retrieves the connected device's Node ID. If there are
* multiple connected devices, the first Node ID is returned.
*/
private void retrieveDeviceNode() {
new Thread(new Runnable() {
#Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
NodeApi.GetConnectedNodesResult result =
Wearable.NodeApi.getConnectedNodes(client).await();
List<Node> nodes = result.getNodes();
if (nodes.size() > 0) {
nodeId = nodes.get(0).getId();
}
client.disconnect();
}
}).start();
}
/**
* Sets up the button for handling click events.`
*/
private void setupWidgets() {
findViewById(R.id.toggleButton).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Asset asset = createAssetFromBitmap(bitmap);
PutDataRequest request = PutDataRequest.create("/image");
request.putAsset("profileImage", asset);
Wearable.DataApi.putDataItem(client, request);
showToast("SentData");
}
});
}
private static Asset createAssetFromBitmap(Bitmap bitmap) {
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
return Asset.createFromBytes(byteStream.toByteArray());
}
public void showToast(String string) {
Toast.makeText(this, string, Toast.LENGTH_LONG).show();
}
}
Wear:
package com.example.administrator.moto360displaycontrol;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
import com.google.android.gms.wearable.DataMapItem;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import android.widget.Toast;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MainActivity extends Activity implements
DataApi.DataListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private TextView mTextView;
private static final long CONNECTION_TIME_OUT_MS = 100;
private static final String ON_MESSAGE = "On!";
private static final String OFF_MESSAGE = "Off!";
private static final String TAG = "Moto360DisplayControl";
private GoogleApiClient client;
private String nodeId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initApi();
}
private void initApi() {
client = getGoogleApiClient(this);
retrieveDeviceNode();
}
private GoogleApiClient getGoogleApiClient(Context context) {
return new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
private void retrieveDeviceNode() {
new Thread(new Runnable() {
#Override
public void run() {
client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
NodeApi.GetConnectedNodesResult result =
Wearable.NodeApi.getConnectedNodes(client).await();
List<Node> nodes = result.getNodes();
if (nodes.size() > 0) {
nodeId = nodes.get(0).getId();
}
client.disconnect();
}
}).start();
}
#Override
protected void onStart() {
super.onStart();
client.connect();
}
#Override
public void onConnected(Bundle connectionHint) {
Wearable.DataApi.addListener(client, this);
Toast.makeText(this, "AddedListener!", Toast.LENGTH_LONG).show();
}
#Override
public void onConnectionSuspended(int num) {
Toast.makeText(this, "ConnectionSuspended", Toast.LENGTH_LONG).show();
}
#Override
public void onConnectionFailed(ConnectionResult res) {
Toast.makeText(this, "ConnectionFailed", Toast.LENGTH_LONG).show();
}
#Override
protected void onStop() {
Wearable.DataApi.removeListener(client, this);
client.disconnect();
super.onStop();
}
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
Toast.makeText(this, "DataChanged!", Toast.LENGTH_LONG).show();
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED && event.getDataItem().getUri().getPath().equals("/image")) {
DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
Asset profileAsset = dataMapItem.getDataMap().getAsset("profileImage");
Bitmap bitmap = loadBitmapFromAsset(profileAsset);
// Do something with bitmap
Toast.makeText(this, "DataChanged!", Toast.LENGTH_LONG).show();
}
}
}
public Bitmap loadBitmapFromAsset(Asset asset) {
if (asset == null) {
throw new IllegalArgumentException("Asset must be non-null");
}
ConnectionResult result = client.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
if (!result.isSuccess()) {
return null;
}
// Convert asset into a file descriptor and block until it's ready
InputStream assetInputStream = Wearable.DataApi.getFdForAsset(client, asset).await().getInputStream();
client.disconnect();
if (assetInputStream == null) {
Log.w(TAG, "Requested an unknown Asset.");
return null;
}
// Decode the stream into a bitmap
return BitmapFactory.decodeStream(assetInputStream);
}
}
Everything "seems" to succeed as far as I can tell but the Toast in the Wear's "onDataChanged" method never shows up on the Wear's display which makes me think it isn't seeing the data change for some reason. Any help would be greatly appreciated!
Try adding a timestamp to your request on the handheld, it did the trick for me back then, and I think Google updated their documentations adding this bit of info

XY Scatter AndroidPlot

Attempting to use AndroidPlot to create an XY scatter plot, encountering a problem... whereby the plot only draws points from left to right, a scrolling chart essentially.
Example... say I have the following co-ordinates, (0,1), (1,0), (0,-1), (-1,0) I would expect to see a diamond shape (if all the points were joined by a line)
I've used the AndroidPlot library successfully before so am somewhat familiar with the methods available.
Is there any examples for a scatter plot using the AndroidPlot library?
Hope I'm making sense here..
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import pl.flex_it.androidplot.XYSeries;
import com.androidplot.series.XYSeries;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYPlot;
import android.app.Fragment;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Temp extends Fragment {
private static XYPlot xyPlot;
private XYSeriesShimmer series;
private LineAndPointFormatter series1Format;
private ArrayList<Number> ALdata1, ALdata2;
private int Adata1[], Adata2[];
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_test, container, false);
// Import plot from the layout
xyPlot = (XYPlot) rootView.findViewById(R.id.xyPlot);
xyPlot.setDomainBoundaries(-2, 2, BoundaryMode.FIXED); // freeze the domain boundary:
xyPlot.setRangeBoundaries(-2, 2, BoundaryMode.FIXED);
ALdata1 = new ArrayList<Number>();
ALdata2 = new ArrayList<Number>();
ALdata1.clear();
ALdata2.clear();
Adata1 = new int[]{0,1,0,-1};
Adata2 = new int[]{1,0,-1,0};
series = new XYSeriesShimmer(ALdata1, ALdata2, 0, "Sightings in USA");
series1Format = new LineAndPointFormatter(Color.TRANSPARENT, Color.BLACK, null); // line color, point color, fill color
xyPlot.addSeries(series, series1Format);
plotDataMethod();
return rootView;
}
private void plotDataMethod() {
for(int i=0; i<Adata1.length; i++){
ALdata1.add(Adata1[i]);
ALdata2.add(Adata2[i]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
series.updateData(ALdata1, ALdata2);
xyPlot.redraw();
}
}
}
EDIT:
package pl.flex_it.androidplot;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.androidplot.series.XYSeries;
public class XYSeriesShimmer implements XYSeries {
private List<Number> dataX;
private List<Number> dataY;
private int seriesIndex;
private String title;
public XYSeriesShimmer(List<Number> datasource, int seriesIndex, String title) {
this.dataY = datasource;
this.seriesIndex = seriesIndex;
this.title = title;
}
public XYSeriesShimmer(List<Number> datasourceX, List<Number> datasourceY, int seriesIndex, String title) {
this.dataX = datasourceX;
this.dataY = datasourceY;
this.seriesIndex = seriesIndex;
this.title = title;
}
#Override
public String getTitle() {
return title;
}
#Override
public int size() {
return dataY.size();
}
#Override
public Number getY(int index) {
return dataY.get(index);
}
#Override
public Number getX(int index) {
return index;
}
public void updateData(List<Number> datasourceX){ //dont need to use this cause, the reference is only stored, modifying the datasource externally will cause this to be updated as well
this.dataY=datasourceX;
}
public void updateData(List<Number> datasourceX, List<Number> datasourceY){ //dont need to use this cause, the reference is only stored, modifying the datasource externally will cause this to be updated as well
this.dataX=datasourceX;
this.dataY=datasourceY;
}
}
This looks like it could be the problem - in XYSeriesShimmer:
#Override
public Number getX(int index) {
return index;
}
This is always going to return i, which means each element's x value is 1 larger than the previous...exactly what you are experiencing. Try changing it to this:
#Override
public Number getX(int index) {
return dataX.get(i);
}

Update UI from an AsyncTaskLoader

I've converted my AsyncTask to an AsyncTaskLoader (mostly to deal with configuration changes). I have a TextView I am using as a progress status and was using onProgressUpdate in the AsyncTask to update it. It doesn't look like AsyncTaskLoader has an equivalent, so during loadInBackground (in the AsyncTaskLoader) I'm using this:
getActivity().runOnUiThread(new Runnable() {
public void run() {
((TextView)getActivity().findViewById(R.id.status)).setText("Updating...");
}
});
I am using this in a Fragment, which is why I'm using getActivity(). This work pretty well, except when a configuration change happens, like changing the screen orientation. My AsyncTaskLoader keeps running (which is why I'm using an AsyncTaskLoader), but the runOnUiThread seems to get skipped.
Not sure why it's being skipped or if this is the best way to update the UI from an AsyncTaskLoader.
UPDATE:
I ended up reverting back to an AsyncTask as it seems better suited for UI updates. Wish they could merge what works with an AsyncTask with an AsyncTaskLoader.
It's actually possible. You essentially need to subclass the AsyncTaskloader and implement a publishMessage() method, which will use a Handler to deliver the progress message to any class that implements the ProgressListener (or whatever you want to call it) interface.
Download this for an example: http://www.2shared.com/file/VW68yhZ1/SampleTaskProgressDialogFragme.html (message me if it goes offline) - this was based of http://habrahabr.ru/post/131560/
Emm... you shouldn't be doing this.
because how an anonymous class access parent class Method or Field is by storing an invisible reference to the parent class.
for example you have a Activity:
public class MyActivity
extends Activity
{
public void someFunction() { /* do some work over here */ }
public void someOtherFunction() {
Runnable r = new Runnable() {
#Override
public void run() {
while (true)
someFunction();
}
};
new Thread(r).start(); // use it, for example here just make a thread to run it.
}
}
the compiler will actually generate something like this:
private static class AnonymousRunnable {
private MyActivity parent;
public AnonymousRunnable(MyActivity parent) {
this.parent = parent;
}
#Override
public void run() {
while (true)
parent.someFunction();
}
}
So, when your parent Activity destroys (due to configuration change, for example), and your anonymous class still exists, the whole activity cannot be gc-ed. (because someone still hold a reference.)
THAT BECOMES A MEMORY LEAK AND MAKE YOUR APP GO LIMBO!!!
If it was me, I would implement the "onProgressUpdate()" for loaders like this:
public class MyLoader extends AsyncTaskLoader<Something> {
private Observable mObservable = new Observable();
synchronized void addObserver(Observer observer) {
mObservable.addObserver(observer);
}
synchronized void deleteObserver(Observer observer) {
mObservable.deleteObserver(observer);
}
#Override
public void loadInBackground(CancellationSignal signal)
{
for (int i = 0;i < 100;++i)
mObservable.notifyObservers(new Integer(i));
}
}
And in your Activity class
public class MyActivity extends Activity {
private Observer mObserver = new Observer() {
#Override
public void update(Observable observable, Object data) {
final Integer progress = (Integer) data;
mTextView.post(new Runnable() {
mTextView.setText(data.toString()); // update your progress....
});
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreated(savedInstanceState);
MyLoader loader = (MyLoader) getLoaderManager().initLoader(0, null, this);
loader.addObserver(mObserver);
}
#Override
public void onDestroy() {
MyLoader loader = (MyLoader) getLoaderManager().getLoader(0);
if (loader != null)
loader.deleteObserver(mObserver);
super.onDestroy();
}
}
remember to deleteObserver() during onDestroy() is important, this way the loader don't hold a reference to your activity forever. (the loader will probably be held alive during your Application lifecycle...)
Answering my own question, but from what I can tell, AsyncTaskLoader isn't the best to use if you need to update the UI.
In the class in which you implement LoaderManager.LoaderCallback (presumably your Activity), there is an onLoadFinished() method which you must override. This is what is returned when the AsyncTaskLoader has finished loading.
The best method is to use LiveData, 100% Working
Step 1: Add lifecycle dependency or use androidx artifacts as yes during project creation
implementation "androidx.lifecycle:lifecycle-livedata:2.1.0"
Step 2: Create the loader class as follow, in loader create in public method to set the livedata that can be observed from activity or fragment. see the setLiveCount method in my loader class.
package com.androidcodeshop.asynctaskloaderdemo;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.content.AsyncTaskLoader;
import java.util.ArrayList;
public class ContactLoader extends AsyncTaskLoader<ArrayList<String>> {
private MutableLiveData<Integer> countLive = new MutableLiveData<>();
synchronized public void setLiveCount(MutableLiveData<Integer> observer) {
countLive = (observer);
}
public ContactLoader(#NonNull Context context) {
super(context);
}
#Nullable
#Override
public ArrayList<String> loadInBackground() {
return loadNamesFromDB();
}
private ArrayList<String> loadNamesFromDB() {
ArrayList<String> names = new ArrayList<>();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
names.add("Name" + i);
countLive.postValue(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return names;
}
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad(); // forcing the loading operation everytime it starts loading
}
}
Step 3: Set the live data from activity and observe the change as follows
package com.androidcodeshop.asynctaskloaderdemo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity implements
LoaderManager.LoaderCallbacks<ArrayList> {
private ContactAdapter mAdapter;
private ArrayList<String> mNames;
private MutableLiveData<Integer> countLiveData;
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNames = new ArrayList<>();
mAdapter = new ContactAdapter(this, mNames);
RecyclerView mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
countLiveData = new MutableLiveData<>();
countLiveData.observe(this, new androidx.lifecycle.Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
Log.d(TAG, "onChanged: " + integer);
Toast.makeText(MainActivity.this, "" +
integer,Toast.LENGTH_SHORT).show();
}
});
// initialize the loader in onCreate of activity
getSupportLoaderManager().initLoader(0, null, this);
// it's deprecated the best way is to use viewmodel and livedata while loading data
}
#NonNull
#Override
public Loader onCreateLoader(int id, #Nullable Bundle args) {
ContactLoader loader = new ContactLoader(this);
loader.setLiveCount(countLiveData);
return loader;
}
#Override
public void onLoadFinished(#NonNull Loader<ArrayList> load, ArrayList data) {
mNames.clear();
mNames.addAll(data);
mAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(#NonNull Loader loader) {
mNames.clear();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
Hope this will help you :) happy coding

Resources