diff --git a/ApiDemos/app/build.gradle b/ApiDemos/app/build.gradle index 8bf13273..f527b9ff 100644 --- a/ApiDemos/app/build.gradle +++ b/ApiDemos/app/build.gradle @@ -23,5 +23,5 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.google.android.gms:play-services-maps:9.8.0' + compile 'com.google.android.gms:play-services-maps:10.2.0' } diff --git a/ApiDemos/app/src/main/java/com/example/mapdemo/CircleDemoActivity.java b/ApiDemos/app/src/main/java/com/example/mapdemo/CircleDemoActivity.java index b6d3bd9a..a4c40d9a 100755 --- a/ApiDemos/app/src/main/java/com/example/mapdemo/CircleDemoActivity.java +++ b/ApiDemos/app/src/main/java/com/example/mapdemo/CircleDemoActivity.java @@ -25,9 +25,13 @@ import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.Circle; import com.google.android.gms.maps.model.CircleOptions; +import com.google.android.gms.maps.model.Dash; +import com.google.android.gms.maps.model.Dot; +import com.google.android.gms.maps.model.Gap; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.PatternItem; import android.graphics.Color; import android.graphics.Point; @@ -35,109 +39,125 @@ import android.location.Location; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.Spinner; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * This shows how to draw circles on a map. */ -public class CircleDemoActivity extends AppCompatActivity implements OnSeekBarChangeListener, - OnMarkerDragListener, OnMapLongClickListener, OnMapReadyCallback { - private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689); - private static final double DEFAULT_RADIUS = 1000000; - public static final double RADIUS_OF_EARTH_METERS = 6371009; +public class CircleDemoActivity extends AppCompatActivity + implements OnSeekBarChangeListener, OnMarkerDragListener, OnMapLongClickListener, + OnItemSelectedListener, OnMapReadyCallback { - private static final int WIDTH_MAX = 50; - private static final int HUE_MAX = 360; - private static final int ALPHA_MAX = 255; + private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689); + private static final double DEFAULT_RADIUS_METERS = 1000000; + private static final double RADIUS_OF_EARTH_METERS = 6371009; + + private static final int MAX_WIDTH_PX = 50; + private static final int MAX_HUE_DEGREES = 360; + private static final int MAX_ALPHA = 255; + + private static final int PATTERN_DASH_LENGTH_PX = 100; + private static final int PATTERN_GAP_LENGTH_PX = 200; + private static final Dot DOT = new Dot(); + private static final Dash DASH = new Dash(PATTERN_DASH_LENGTH_PX); + private static final Gap GAP = new Gap(PATTERN_GAP_LENGTH_PX); + private static final List PATTERN_DOTTED = Arrays.asList(DOT, GAP); + private static final List PATTERN_DASHED = Arrays.asList(DASH, GAP); + private static final List PATTERN_MIXED = Arrays.asList(DOT, GAP, DOT, DASH, GAP); private GoogleMap mMap; - private List mCircles = new ArrayList(1); + private List mCircles = new ArrayList<>(1); - private SeekBar mColorBar; - private SeekBar mAlphaBar; - private SeekBar mWidthBar; - private int mStrokeColor; - private int mFillColor; + private int mFillColorArgb; + private int mStrokeColorArgb; + + private SeekBar mFillHueBar; + private SeekBar mFillAlphaBar; + private SeekBar mStrokeWidthBar; + private SeekBar mStrokeHueBar; + private SeekBar mStrokeAlphaBar; + private Spinner mStrokePatternSpinner; private CheckBox mClickabilityCheckbox; - private class DraggableCircle { - private final Marker centerMarker; - private final Marker radiusMarker; - private final Circle circle; - private double radius; - public DraggableCircle(LatLng center, double radius, boolean clickable) { - this.radius = radius; - centerMarker = mMap.addMarker(new MarkerOptions() - .position(center) - .draggable(true)); - radiusMarker = mMap.addMarker(new MarkerOptions() - .position(toRadiusLatLng(center, radius)) - .draggable(true) - .icon(BitmapDescriptorFactory.defaultMarker( - BitmapDescriptorFactory.HUE_AZURE))); - circle = mMap.addCircle(new CircleOptions() - .center(center) - .radius(radius) - .strokeWidth(mWidthBar.getProgress()) - .strokeColor(mStrokeColor) - .fillColor(mFillColor) - .clickable(clickable)); - } + // These are the options for stroke patterns. We use their + // string resource IDs as identifiers. - public DraggableCircle(LatLng center, LatLng radiusLatLng, boolean clickable) { - this.radius = toRadiusMeters(center, radiusLatLng); - centerMarker = mMap.addMarker(new MarkerOptions() + private static final int[] PATTERN_TYPE_NAME_RESOURCE_IDS = { + R.string.pattern_solid, // Default + R.string.pattern_dashed, + R.string.pattern_dotted, + R.string.pattern_mixed, + }; + + private class DraggableCircle { + private final Marker mCenterMarker; + private final Marker mRadiusMarker; + private final Circle mCircle; + private double mRadiusMeters; + + public DraggableCircle(LatLng center, double radiusMeters) { + this.mRadiusMeters = radiusMeters; + mCenterMarker = mMap.addMarker(new MarkerOptions() .position(center) .draggable(true)); - radiusMarker = mMap.addMarker(new MarkerOptions() - .position(radiusLatLng) + mRadiusMarker = mMap.addMarker(new MarkerOptions() + .position(toRadiusLatLng(center, radiusMeters)) .draggable(true) .icon(BitmapDescriptorFactory.defaultMarker( BitmapDescriptorFactory.HUE_AZURE))); - circle = mMap.addCircle(new CircleOptions() + mCircle = mMap.addCircle(new CircleOptions() .center(center) - .radius(radius) - .strokeWidth(mWidthBar.getProgress()) - .strokeColor(mStrokeColor) - .fillColor(mFillColor) - .clickable(clickable)); + .radius(radiusMeters) + .strokeWidth(mStrokeWidthBar.getProgress()) + .strokeColor(mStrokeColorArgb) + .fillColor(mFillColorArgb) + .clickable(mClickabilityCheckbox.isChecked())); } public boolean onMarkerMoved(Marker marker) { - if (marker.equals(centerMarker)) { - circle.setCenter(marker.getPosition()); - radiusMarker.setPosition(toRadiusLatLng(marker.getPosition(), radius)); + if (marker.equals(mCenterMarker)) { + mCircle.setCenter(marker.getPosition()); + mRadiusMarker.setPosition(toRadiusLatLng(marker.getPosition(), mRadiusMeters)); return true; } - if (marker.equals(radiusMarker)) { - radius = toRadiusMeters(centerMarker.getPosition(), radiusMarker.getPosition()); - circle.setRadius(radius); + if (marker.equals(mRadiusMarker)) { + mRadiusMeters = toRadiusMeters(mCenterMarker.getPosition(), mRadiusMarker.getPosition()); + mCircle.setRadius(mRadiusMeters); return true; } return false; } public void onStyleChange() { - circle.setStrokeWidth(mWidthBar.getProgress()); - circle.setFillColor(mFillColor); - circle.setStrokeColor(mStrokeColor); + mCircle.setStrokeWidth(mStrokeWidthBar.getProgress()); + mCircle.setStrokeColor(mStrokeColorArgb); + mCircle.setFillColor(mFillColorArgb); + } + + public void setStrokePattern(List pattern) { + mCircle.setStrokePattern(pattern); } public void setClickable(boolean clickable) { - circle.setClickable(clickable); + mCircle.setClickable(clickable); } } /** Generate LatLng of radius marker */ - private static LatLng toRadiusLatLng(LatLng center, double radius) { - double radiusAngle = Math.toDegrees(radius / RADIUS_OF_EARTH_METERS) / + private static LatLng toRadiusLatLng(LatLng center, double radiusMeters) { + double radiusAngle = Math.toDegrees(radiusMeters / RADIUS_OF_EARTH_METERS) / Math.cos(Math.toRadians(center.latitude)); return new LatLng(center.latitude, center.longitude + radiusAngle); } @@ -154,44 +174,70 @@ public class CircleDemoActivity extends AppCompatActivity implements OnSeekBarCh super.onCreate(savedInstanceState); setContentView(R.layout.circle_demo); - mColorBar = (SeekBar) findViewById(R.id.hueSeekBar); - mColorBar.setMax(HUE_MAX); - mColorBar.setProgress(0); + mFillHueBar = (SeekBar) findViewById(R.id.fillHueSeekBar); + mFillHueBar.setMax(MAX_HUE_DEGREES); + mFillHueBar.setProgress(MAX_HUE_DEGREES / 2); - mAlphaBar = (SeekBar) findViewById(R.id.alphaSeekBar); - mAlphaBar.setMax(ALPHA_MAX); - mAlphaBar.setProgress(127); + mFillAlphaBar = (SeekBar) findViewById(R.id.fillAlphaSeekBar); + mFillAlphaBar.setMax(MAX_ALPHA); + mFillAlphaBar.setProgress(MAX_ALPHA / 2); - mWidthBar = (SeekBar) findViewById(R.id.widthSeekBar); - mWidthBar.setMax(WIDTH_MAX); - mWidthBar.setProgress(10); + mStrokeWidthBar = (SeekBar) findViewById(R.id.strokeWidthSeekBar); + mStrokeWidthBar.setMax(MAX_WIDTH_PX); + mStrokeWidthBar.setProgress(MAX_WIDTH_PX / 3); + + mStrokeHueBar = (SeekBar) findViewById(R.id.strokeHueSeekBar); + mStrokeHueBar.setMax(MAX_HUE_DEGREES); + mStrokeHueBar.setProgress(0); + + mStrokeAlphaBar = (SeekBar) findViewById(R.id.strokeAlphaSeekBar); + mStrokeAlphaBar.setMax(MAX_ALPHA); + mStrokeAlphaBar.setProgress(MAX_ALPHA); + + mStrokePatternSpinner = (Spinner) findViewById(R.id.strokePatternSpinner); + mStrokePatternSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(PATTERN_TYPE_NAME_RESOURCE_IDS))); + + mClickabilityCheckbox = (CheckBox) findViewById(R.id.toggleClickability); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); + } - mClickabilityCheckbox = (CheckBox) findViewById(R.id.toggleClickability); + private String[] getResourceStrings(int[] resourceIds) { + String[] strings = new String[resourceIds.length]; + for (int i = 0; i < resourceIds.length; i++) { + strings[i] = getString(resourceIds[i]); + } + return strings; } @Override public void onMapReady(GoogleMap map) { - mMap = map; - // Override the default content description on the view, for accessibility mode. map.setContentDescription(getString(R.string.map_circle_description)); - mColorBar.setOnSeekBarChangeListener(this); - mAlphaBar.setOnSeekBarChangeListener(this); - mWidthBar.setOnSeekBarChangeListener(this); + mMap = map; mMap.setOnMarkerDragListener(this); mMap.setOnMapLongClickListener(this); - mFillColor = Color.HSVToColor( - mAlphaBar.getProgress(), new float[]{mColorBar.getProgress(), 1, 1}); - mStrokeColor = Color.BLACK; + mFillColorArgb = Color.HSVToColor( + mFillAlphaBar.getProgress(), new float[]{mFillHueBar.getProgress(), 1, 1}); + mStrokeColorArgb = Color.HSVToColor( + mStrokeAlphaBar.getProgress(), new float[]{mStrokeHueBar.getProgress(), 1, 1}); - DraggableCircle circle = - new DraggableCircle(SYDNEY, DEFAULT_RADIUS, mClickabilityCheckbox.isChecked()); + mFillHueBar.setOnSeekBarChangeListener(this); + mFillAlphaBar.setOnSeekBarChangeListener(this); + + mStrokeWidthBar.setOnSeekBarChangeListener(this); + mStrokeHueBar.setOnSeekBarChangeListener(this); + mStrokeAlphaBar.setOnSeekBarChangeListener(this); + + mStrokePatternSpinner.setOnItemSelectedListener(this); + + DraggableCircle circle = new DraggableCircle(SYDNEY, DEFAULT_RADIUS_METERS); mCircles.add(circle); // Move the map so that it is centered on the initial circle @@ -201,13 +247,41 @@ public class CircleDemoActivity extends AppCompatActivity implements OnSeekBarCh map.setOnCircleClickListener(new OnCircleClickListener() { @Override public void onCircleClick(Circle circle) { - // Flip the r, g and b components of the circle's stroke color. - int strokeColor = circle.getStrokeColor() ^ 0x00ffffff; - circle.setStrokeColor(strokeColor); + // Flip the red, green and blue components of the circle's stroke color. + circle.setStrokeColor(circle.getStrokeColor() ^ 0x00ffffff); } }); } + private List getSelectedPattern(int pos) { + switch (PATTERN_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.pattern_solid: + return null; + case R.string.pattern_dotted: + return PATTERN_DOTTED; + case R.string.pattern_dashed: + return PATTERN_DASHED; + case R.string.pattern_mixed: + return PATTERN_MIXED; + default: + return null; + } + } + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + if (parent.getId() == R.id.strokePatternSpinner) { + for (DraggableCircle draggableCircle : mCircles) { + draggableCircle.setStrokePattern(getSelectedPattern(pos)); + } + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Don't do anything here. + } + @Override public void onStopTrackingTouch(SeekBar seekBar) { // Don't do anything here. @@ -220,11 +294,16 @@ public class CircleDemoActivity extends AppCompatActivity implements OnSeekBarCh @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (seekBar == mColorBar) { - mFillColor = Color.HSVToColor(Color.alpha(mFillColor), new float[]{progress, 1, 1}); - } else if (seekBar == mAlphaBar) { - mFillColor = Color.argb(progress, Color.red(mFillColor), Color.green(mFillColor), - Color.blue(mFillColor)); + if (seekBar == mFillHueBar) { + mFillColorArgb = Color.HSVToColor(Color.alpha(mFillColorArgb), new float[]{progress, 1, 1}); + } else if (seekBar == mFillAlphaBar) { + mFillColorArgb = Color.argb(progress, + Color.red(mFillColorArgb), Color.green(mFillColorArgb), Color.blue(mFillColorArgb)); + } else if (seekBar == mStrokeHueBar) { + mStrokeColorArgb = Color.HSVToColor(Color.alpha(mStrokeColorArgb), new float[]{progress, 1, 1}); + } else if (seekBar == mStrokeAlphaBar) { + mStrokeColorArgb = Color.argb(progress, + Color.red(mStrokeColorArgb), Color.green(mStrokeColorArgb), Color.blue(mStrokeColorArgb)); } for (DraggableCircle draggableCircle : mCircles) { @@ -258,14 +337,12 @@ public class CircleDemoActivity extends AppCompatActivity implements OnSeekBarCh @Override public void onMapLongClick(LatLng point) { // We know the center, let's place the outline at a point 3/4 along the view. - View view = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)) - .getView(); + View view = getSupportFragmentManager().findFragmentById(R.id.map).getView(); LatLng radiusLatLng = mMap.getProjection().fromScreenLocation(new Point( view.getHeight() * 3 / 4, view.getWidth() * 3 / 4)); - // ok create it - DraggableCircle circle = - new DraggableCircle(point, radiusLatLng, mClickabilityCheckbox.isChecked()); + // Create the circle. + DraggableCircle circle = new DraggableCircle(point, toRadiusMeters(point, radiusLatLng)); mCircles.add(circle); } diff --git a/ApiDemos/app/src/main/java/com/example/mapdemo/PolygonDemoActivity.java b/ApiDemos/app/src/main/java/com/example/mapdemo/PolygonDemoActivity.java index 6f9585e5..9fa0d388 100644 --- a/ApiDemos/app/src/main/java/com/example/mapdemo/PolygonDemoActivity.java +++ b/ApiDemos/app/src/main/java/com/example/mapdemo/PolygonDemoActivity.java @@ -20,7 +20,12 @@ import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.Dash; +import com.google.android.gms.maps.model.Dot; +import com.google.android.gms.maps.model.Gap; +import com.google.android.gms.maps.model.JointType; import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.PatternItem; import com.google.android.gms.maps.model.Polygon; import com.google.android.gms.maps.model.PolygonOptions; @@ -28,9 +33,13 @@ import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.Spinner; import java.util.Arrays; import java.util.List; @@ -39,44 +48,82 @@ import java.util.List; * This shows how to draw polygons on a map. */ public class PolygonDemoActivity extends AppCompatActivity - implements OnSeekBarChangeListener, OnMapReadyCallback { + implements OnSeekBarChangeListener, OnItemSelectedListener, OnMapReadyCallback { - private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689); + private static final LatLng CENTER = new LatLng(-20, 130); + private static final int MAX_WIDTH_PX = 100; + private static final int MAX_HUE_DEGREES = 360; + private static final int MAX_ALPHA = 255; - private static final int WIDTH_MAX = 50; - - private static final int HUE_MAX = 360; - - private static final int ALPHA_MAX = 255; + private static final int PATTERN_DASH_LENGTH_PX = 50; + private static final int PATTERN_GAP_LENGTH_PX = 10; + private static final Dot DOT = new Dot(); + private static final Dash DASH = new Dash(PATTERN_DASH_LENGTH_PX); + private static final Gap GAP = new Gap(PATTERN_GAP_LENGTH_PX); + private static final List PATTERN_DOTTED = Arrays.asList(DOT, GAP); + private static final List PATTERN_DASHED = Arrays.asList(DASH, GAP); + private static final List PATTERN_MIXED = Arrays.asList(DOT, GAP, DOT, DASH, GAP); private Polygon mMutablePolygon; - - private Polygon mClickablePolygonWithHoles; - - private SeekBar mColorBar; - - private SeekBar mAlphaBar; - - private SeekBar mWidthBar; - + private SeekBar mFillHueBar; + private SeekBar mFillAlphaBar; + private SeekBar mStrokeWidthBar; + private SeekBar mStrokeHueBar; + private SeekBar mStrokeAlphaBar; + private Spinner mStrokeJointTypeSpinner; + private Spinner mStrokePatternSpinner; private CheckBox mClickabilityCheckbox; + // These are the options for polygon stroke joints and patterns. We use their + // string resource IDs as identifiers. + + private static final int[] JOINT_TYPE_NAME_RESOURCE_IDS = { + R.string.joint_type_default, // Default + R.string.joint_type_bevel, + R.string.joint_type_round, + }; + + private static final int[] PATTERN_TYPE_NAME_RESOURCE_IDS = { + R.string.pattern_solid, // Default + R.string.pattern_dashed, + R.string.pattern_dotted, + R.string.pattern_mixed, + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.polygon_demo); - mColorBar = (SeekBar) findViewById(R.id.hueSeekBar); - mColorBar.setMax(HUE_MAX); - mColorBar.setProgress(0); + mFillHueBar = (SeekBar) findViewById(R.id.fillHueSeekBar); + mFillHueBar.setMax(MAX_HUE_DEGREES); + mFillHueBar.setProgress(MAX_HUE_DEGREES / 2); - mAlphaBar = (SeekBar) findViewById(R.id.alphaSeekBar); - mAlphaBar.setMax(ALPHA_MAX); - mAlphaBar.setProgress(127); + mFillAlphaBar = (SeekBar) findViewById(R.id.fillAlphaSeekBar); + mFillAlphaBar.setMax(MAX_ALPHA); + mFillAlphaBar.setProgress(MAX_ALPHA / 2); - mWidthBar = (SeekBar) findViewById(R.id.widthSeekBar); - mWidthBar.setMax(WIDTH_MAX); - mWidthBar.setProgress(10); + mStrokeWidthBar = (SeekBar) findViewById(R.id.strokeWidthSeekBar); + mStrokeWidthBar.setMax(MAX_WIDTH_PX); + mStrokeWidthBar.setProgress(MAX_WIDTH_PX / 3); + + mStrokeHueBar = (SeekBar) findViewById(R.id.strokeHueSeekBar); + mStrokeHueBar.setMax(MAX_HUE_DEGREES); + mStrokeHueBar.setProgress(0); + + mStrokeAlphaBar = (SeekBar) findViewById(R.id.strokeAlphaSeekBar); + mStrokeAlphaBar.setMax(MAX_ALPHA); + mStrokeAlphaBar.setProgress(MAX_ALPHA); + + mStrokeJointTypeSpinner = (Spinner) findViewById(R.id.strokeJointTypeSpinner); + mStrokeJointTypeSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(JOINT_TYPE_NAME_RESOURCE_IDS))); + + mStrokePatternSpinner = (Spinner) findViewById(R.id.strokePatternSpinner); + mStrokePatternSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(PATTERN_TYPE_NAME_RESOURCE_IDS))); mClickabilityCheckbox = (CheckBox) findViewById(R.id.toggleClickability); @@ -85,55 +132,53 @@ public class PolygonDemoActivity extends AppCompatActivity mapFragment.getMapAsync(this); } + private String[] getResourceStrings(int[] resourceIds) { + String[] strings = new String[resourceIds.length]; + for (int i = 0; i < resourceIds.length; i++) { + strings[i] = getString(resourceIds[i]); + } + return strings; + } + @Override public void onMapReady(GoogleMap map) { // Override the default content description on the view, for accessibility mode. - // Ideally this string would be localised. - map.setContentDescription("Google Map with polygons."); + map.setContentDescription(getString(R.string.polygon_demo_description)); + + int fillColorArgb = Color.HSVToColor( + mFillAlphaBar.getProgress(), new float[]{mFillHueBar.getProgress(), 1, 1}); + int strokeColorArgb = Color.HSVToColor( + mStrokeAlphaBar.getProgress(), new float[]{mStrokeHueBar.getProgress(), 1, 1}); // Create a rectangle with two rectangular holes. - mClickablePolygonWithHoles = map.addPolygon(new PolygonOptions() - .addAll(createRectangle(new LatLng(-20, 130), 5, 5)) + mMutablePolygon = map.addPolygon(new PolygonOptions() + .addAll(createRectangle(CENTER, 5, 5)) .addHole(createRectangle(new LatLng(-22, 128), 1, 1)) .addHole(createRectangle(new LatLng(-18, 133), 0.5, 1.5)) - .fillColor(Color.CYAN) - .strokeColor(Color.BLUE) - .strokeWidth(5) + .fillColor(fillColorArgb) + .strokeColor(strokeColorArgb) + .strokeWidth(mStrokeWidthBar.getProgress()) .clickable(mClickabilityCheckbox.isChecked())); - // Create a rectangle centered at Sydney. - PolygonOptions options = new PolygonOptions() - .addAll(createRectangle(SYDNEY, 5, 8)) - .clickable(mClickabilityCheckbox.isChecked()); + mFillHueBar.setOnSeekBarChangeListener(this); + mFillAlphaBar.setOnSeekBarChangeListener(this); - int fillColor = Color.HSVToColor( - mAlphaBar.getProgress(), new float[]{mColorBar.getProgress(), 1, 1}); - mMutablePolygon = map.addPolygon(options - .strokeWidth(mWidthBar.getProgress()) - .strokeColor(Color.BLACK) - .fillColor(fillColor)); + mStrokeWidthBar.setOnSeekBarChangeListener(this); + mStrokeHueBar.setOnSeekBarChangeListener(this); + mStrokeAlphaBar.setOnSeekBarChangeListener(this); - // Create another polygon that overlaps the previous two. - // Clickability defaults to false, so this one won't accept clicks. - map.addPolygon(new PolygonOptions() - .addAll(createRectangle(new LatLng(-27, 140), 10, 7)) - .fillColor(Color.WHITE) - .strokeColor(Color.BLACK)); - - mColorBar.setOnSeekBarChangeListener(this); - mAlphaBar.setOnSeekBarChangeListener(this); - mWidthBar.setOnSeekBarChangeListener(this); + mStrokeJointTypeSpinner.setOnItemSelectedListener(this); + mStrokePatternSpinner.setOnItemSelectedListener(this); // Move the map so that it is centered on the mutable polygon. - map.moveCamera(CameraUpdateFactory.newLatLng(SYDNEY)); + map.moveCamera(CameraUpdateFactory.newLatLngZoom(CENTER, 4)); // Add a listener for polygon clicks that changes the clicked polygon's stroke color. map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() { @Override public void onPolygonClick(Polygon polygon) { - // Flip the r, g and b components of the polygon's stroke color. - int strokeColor = polygon.getStrokeColor() ^ 0x00ffffff; - polygon.setStrokeColor(strokeColor); + // Flip the red, green and blue components of the polygon's stroke color. + polygon.setStrokeColor(polygon.getStrokeColor() ^ 0x00ffffff); } }); } @@ -149,6 +194,50 @@ public class PolygonDemoActivity extends AppCompatActivity new LatLng(center.latitude - halfHeight, center.longitude - halfWidth)); } + private int getSelectedJointType(int pos) { + switch (JOINT_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.joint_type_bevel: + return JointType.BEVEL; + case R.string.joint_type_round: + return JointType.ROUND; + case R.string.joint_type_default: + return JointType.DEFAULT; + } + return 0; + } + + private List getSelectedPattern(int pos) { + switch (PATTERN_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.pattern_solid: + return null; + case R.string.pattern_dotted: + return PATTERN_DOTTED; + case R.string.pattern_dashed: + return PATTERN_DASHED; + case R.string.pattern_mixed: + return PATTERN_MIXED; + default: + return null; + } + } + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + switch (parent.getId()) { + case R.id.strokeJointTypeSpinner: + mMutablePolygon.setStrokeJointType(getSelectedJointType(pos)); + break; + case R.id.strokePatternSpinner: + mMutablePolygon.setStrokePattern(getSelectedPattern(pos)); + break; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Don't do anything here. + } + @Override public void onStopTrackingTouch(SeekBar seekBar) { // Don't do anything here. @@ -165,28 +254,33 @@ public class PolygonDemoActivity extends AppCompatActivity return; } - if (seekBar == mColorBar) { + if (seekBar == mFillHueBar) { mMutablePolygon.setFillColor(Color.HSVToColor( Color.alpha(mMutablePolygon.getFillColor()), new float[]{progress, 1, 1})); - } else if (seekBar == mAlphaBar) { + } else if (seekBar == mFillAlphaBar) { int prevColor = mMutablePolygon.getFillColor(); mMutablePolygon.setFillColor(Color.argb( progress, Color.red(prevColor), Color.green(prevColor), Color.blue(prevColor))); - } else if (seekBar == mWidthBar) { + } else if (seekBar == mStrokeHueBar) { + mMutablePolygon.setStrokeColor(Color.HSVToColor( + Color.alpha(mMutablePolygon.getStrokeColor()), new float[]{progress, 1, 1})); + } else if (seekBar == mStrokeAlphaBar) { + int prevColorArgb = mMutablePolygon.getStrokeColor(); + mMutablePolygon.setStrokeColor(Color.argb( + progress, Color.red(prevColorArgb), Color.green(prevColorArgb), + Color.blue(prevColorArgb))); + } else if (seekBar == mStrokeWidthBar) { mMutablePolygon.setStrokeWidth(progress); } } /** - * Toggles the clickability of two polygons based on the state of the View that triggered this + * Toggles the clickability of the polygon based on the state of the View that triggered this * call. * This callback is defined on the CheckBox in the layout for this Activity. */ public void toggleClickability(View view) { - if (mClickablePolygonWithHoles != null) { - mClickablePolygonWithHoles.setClickable(((CheckBox) view).isChecked()); - } if (mMutablePolygon != null) { mMutablePolygon.setClickable(((CheckBox) view).isChecked()); } diff --git a/ApiDemos/app/src/main/java/com/example/mapdemo/PolylineDemoActivity.java b/ApiDemos/app/src/main/java/com/example/mapdemo/PolylineDemoActivity.java index 841517ee..2d062587 100644 --- a/ApiDemos/app/src/main/java/com/example/mapdemo/PolylineDemoActivity.java +++ b/ApiDemos/app/src/main/java/com/example/mapdemo/PolylineDemoActivity.java @@ -20,74 +20,138 @@ import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.ButtCap; +import com.google.android.gms.maps.model.Cap; +import com.google.android.gms.maps.model.CustomCap; +import com.google.android.gms.maps.model.Dash; +import com.google.android.gms.maps.model.Dot; +import com.google.android.gms.maps.model.Gap; +import com.google.android.gms.maps.model.JointType; import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.PatternItem; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; +import com.google.android.gms.maps.model.RoundCap; +import com.google.android.gms.maps.model.SquareCap; import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.Spinner; + +import java.util.Arrays; +import java.util.List; /** * This shows how to draw polylines on a map. */ public class PolylineDemoActivity extends AppCompatActivity - implements OnSeekBarChangeListener, OnMapReadyCallback { - - private static final LatLng MELBOURNE = new LatLng(-37.81319, 144.96298); - - private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689); + implements OnSeekBarChangeListener, OnItemSelectedListener, OnMapReadyCallback { + // City locations for mutable polyline. private static final LatLng ADELAIDE = new LatLng(-34.92873, 138.59995); - + private static final LatLng DARWIN = new LatLng(-12.4258647, 130.7932231); + private static final LatLng MELBOURNE = new LatLng(-37.81319, 144.96298); private static final LatLng PERTH = new LatLng(-31.95285, 115.85734); + // Airport locations for geodesic polyline. + private static final LatLng AKL = new LatLng(-37.006254, 174.783018); + private static final LatLng JFK = new LatLng(40.641051, -73.777485); + private static final LatLng LAX = new LatLng(33.936524, -118.377686); private static final LatLng LHR = new LatLng(51.471547, -0.460052); - private static final LatLng LAX = new LatLng(33.936524, -118.377686); + private static final int MAX_WIDTH_PX = 100; + private static final int MAX_HUE_DEGREES = 360; + private static final int MAX_ALPHA = 255; + private static final int CUSTOM_CAP_IMAGE_REF_WIDTH_PX = 50; + private static final int INITIAL_STROKE_WIDTH_PX = 5; - private static final LatLng JFK = new LatLng(40.641051, -73.777485); - - private static final LatLng AKL = new LatLng(-37.006254, 174.783018); - - private static final int WIDTH_MAX = 50; - - private static final int HUE_MAX = 360; - - private static final int ALPHA_MAX = 255; + private static final int PATTERN_DASH_LENGTH_PX = 50; + private static final int PATTERN_GAP_LENGTH_PX = 20; + private static final Dot DOT = new Dot(); + private static final Dash DASH = new Dash(PATTERN_DASH_LENGTH_PX); + private static final Gap GAP = new Gap(PATTERN_GAP_LENGTH_PX); + private static final List PATTERN_DOTTED = Arrays.asList(DOT, GAP); + private static final List PATTERN_DASHED = Arrays.asList(DASH, GAP); + private static final List PATTERN_MIXED = Arrays.asList(DOT, GAP, DOT, DASH, GAP); private Polyline mMutablePolyline; - - private Polyline mClickablePolyline; - - private SeekBar mColorBar; - + private SeekBar mHueBar; private SeekBar mAlphaBar; - private SeekBar mWidthBar; - + private Spinner mStartCapSpinner; + private Spinner mEndCapSpinner; + private Spinner mJointTypeSpinner; + private Spinner mPatternSpinner; private CheckBox mClickabilityCheckbox; + // These are the options for polyline caps, joints and patterns. We use their + // string resource IDs as identifiers. + + private static final int[] CAP_TYPE_NAME_RESOURCE_IDS = { + R.string.cap_butt, // Default + R.string.cap_round, + R.string.cap_square, + R.string.cap_image, + }; + + private static final int[] JOINT_TYPE_NAME_RESOURCE_IDS = { + R.string.joint_type_default, // Default + R.string.joint_type_bevel, + R.string.joint_type_round, + }; + + private static final int[] PATTERN_TYPE_NAME_RESOURCE_IDS = { + R.string.pattern_solid, // Default + R.string.pattern_dashed, + R.string.pattern_dotted, + R.string.pattern_mixed, + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.polyline_demo); - mColorBar = (SeekBar) findViewById(R.id.hueSeekBar); - mColorBar.setMax(HUE_MAX); - mColorBar.setProgress(0); + mHueBar = (SeekBar) findViewById(R.id.hueSeekBar); + mHueBar.setMax(MAX_HUE_DEGREES); + mHueBar.setProgress(0); mAlphaBar = (SeekBar) findViewById(R.id.alphaSeekBar); - mAlphaBar.setMax(ALPHA_MAX); - mAlphaBar.setProgress(255); + mAlphaBar.setMax(MAX_ALPHA); + mAlphaBar.setProgress(MAX_ALPHA); mWidthBar = (SeekBar) findViewById(R.id.widthSeekBar); - mWidthBar.setMax(WIDTH_MAX); - mWidthBar.setProgress(10); + mWidthBar.setMax(MAX_WIDTH_PX); + mWidthBar.setProgress(MAX_WIDTH_PX / 2); + + mStartCapSpinner = (Spinner) findViewById(R.id.startCapSpinner); + mStartCapSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(CAP_TYPE_NAME_RESOURCE_IDS))); + + mEndCapSpinner = (Spinner) findViewById(R.id.endCapSpinner); + mEndCapSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(CAP_TYPE_NAME_RESOURCE_IDS))); + + mJointTypeSpinner = (Spinner) findViewById(R.id.jointTypeSpinner); + mJointTypeSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(JOINT_TYPE_NAME_RESOURCE_IDS))); + + mPatternSpinner = (Spinner) findViewById(R.id.patternSpinner); + mPatternSpinner.setAdapter(new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + getResourceStrings(PATTERN_TYPE_NAME_RESOURCE_IDS))); mClickabilityCheckbox = (CheckBox) findViewById(R.id.toggleClickability); @@ -96,57 +160,124 @@ public class PolylineDemoActivity extends AppCompatActivity mapFragment.getMapAsync(this); } + private String[] getResourceStrings(int[] resourceIds) { + String[] strings = new String[resourceIds.length]; + for (int i = 0; i < resourceIds.length; i++) { + strings[i] = getString(resourceIds[i]); + } + return strings; + } + @Override public void onMapReady(GoogleMap map) { // Override the default content description on the view, for accessibility mode. - // Ideally this string would be localised. - map.setContentDescription("Google Map with polylines."); - - // A simple polyline with the default options from Melbourne-Adelaide-Perth. - map.addPolyline((new PolylineOptions()) - .add(MELBOURNE, ADELAIDE, PERTH)); + map.setContentDescription(getString(R.string.polyline_demo_description)); // A geodesic polyline that goes around the world. - mClickablePolyline = map.addPolyline((new PolylineOptions()) + map.addPolyline(new PolylineOptions() .add(LHR, AKL, LAX, JFK, LHR) - .width(5) + .width(INITIAL_STROKE_WIDTH_PX) .color(Color.BLUE) .geodesic(true) .clickable(mClickabilityCheckbox.isChecked())); - // Rectangle centered at Sydney. This polyline will be mutable. - int radius = 5; - PolylineOptions options = new PolylineOptions() - .add(new LatLng(SYDNEY.latitude + radius, SYDNEY.longitude + radius)) - .add(new LatLng(SYDNEY.latitude + radius, SYDNEY.longitude - radius)) - .add(new LatLng(SYDNEY.latitude - radius, SYDNEY.longitude - radius)) - .add(new LatLng(SYDNEY.latitude - radius, SYDNEY.longitude + radius)) - .add(new LatLng(SYDNEY.latitude + radius, SYDNEY.longitude + radius)); + // A simple polyline across Australia. This polyline will be mutable. int color = Color.HSVToColor( - mAlphaBar.getProgress(), new float[]{mColorBar.getProgress(), 1, 1}); - mMutablePolyline = map.addPolyline(options + mAlphaBar.getProgress(), new float[]{mHueBar.getProgress(), 1, 1}); + mMutablePolyline = map.addPolyline(new PolylineOptions() .color(color) .width(mWidthBar.getProgress()) - .clickable(mClickabilityCheckbox.isChecked())); + .clickable(mClickabilityCheckbox.isChecked()) + .add(MELBOURNE, ADELAIDE, PERTH, DARWIN)); - mColorBar.setOnSeekBarChangeListener(this); + mHueBar.setOnSeekBarChangeListener(this); mAlphaBar.setOnSeekBarChangeListener(this); mWidthBar.setOnSeekBarChangeListener(this); + mStartCapSpinner.setOnItemSelectedListener(this); + mEndCapSpinner.setOnItemSelectedListener(this); + mJointTypeSpinner.setOnItemSelectedListener(this); + mPatternSpinner.setOnItemSelectedListener(this); + // Move the map so that it is centered on the mutable polyline. - map.moveCamera(CameraUpdateFactory.newLatLng(SYDNEY)); + map.moveCamera(CameraUpdateFactory.newLatLngZoom(MELBOURNE, 3)); // Add a listener for polyline clicks that changes the clicked polyline's color. map.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() { @Override public void onPolylineClick(Polyline polyline) { - // Flip the values of the r, g and b components of the polyline's color. - int strokeColor = polyline.getColor() ^ 0x00ffffff; - polyline.setColor(strokeColor); + // Flip the values of the red, green and blue components of the polyline's color. + polyline.setColor(polyline.getColor() ^ 0x00ffffff); } }); } + private Cap getSelectedCap(int pos) { + switch (CAP_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.cap_butt: + return new ButtCap(); + case R.string.cap_square: + return new SquareCap(); + case R.string.cap_round: + return new RoundCap(); + case R.string.cap_image: + return new CustomCap( + BitmapDescriptorFactory.fromResource(R.drawable.chevron), + CUSTOM_CAP_IMAGE_REF_WIDTH_PX); + } + return null; + } + + private int getSelectedJointType(int pos) { + switch (JOINT_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.joint_type_bevel: + return JointType.BEVEL; + case R.string.joint_type_round: + return JointType.ROUND; + case R.string.joint_type_default: + return JointType.DEFAULT; + } + return 0; + } + + private List getSelectedPattern(int pos) { + switch (PATTERN_TYPE_NAME_RESOURCE_IDS[pos]) { + case R.string.pattern_solid: + return null; + case R.string.pattern_dotted: + return PATTERN_DOTTED; + case R.string.pattern_dashed: + return PATTERN_DASHED; + case R.string.pattern_mixed: + return PATTERN_MIXED; + default: + return null; + } + } + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + switch (parent.getId()) { + case R.id.startCapSpinner: + mMutablePolyline.setStartCap(getSelectedCap(pos)); + break; + case R.id.endCapSpinner: + mMutablePolyline.setEndCap(getSelectedCap(pos)); + break; + case R.id.jointTypeSpinner: + mMutablePolyline.setJointType(getSelectedJointType(pos)); + break; + case R.id.patternSpinner: + mMutablePolyline.setPattern(getSelectedPattern(pos)); + break; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Don't do anything here. + } + @Override public void onStopTrackingTouch(SeekBar seekBar) { // Don't do anything here. @@ -163,7 +294,7 @@ public class PolylineDemoActivity extends AppCompatActivity return; } - if (seekBar == mColorBar) { + if (seekBar == mHueBar) { mMutablePolyline.setColor(Color.HSVToColor( Color.alpha(mMutablePolyline.getColor()), new float[]{progress, 1, 1})); } else if (seekBar == mAlphaBar) { @@ -176,14 +307,11 @@ public class PolylineDemoActivity extends AppCompatActivity } /** - * Toggles the clickability of two polylines based on the state of the View that triggered this + * Toggles the clickability of the polyline based on the state of the View that triggered this * call. * This callback is defined on the CheckBox in the layout for this Activity. */ public void toggleClickability(View view) { - if (mClickablePolyline != null) { - mClickablePolyline.setClickable(((CheckBox) view).isChecked()); - } if (mMutablePolyline != null) { mMutablePolyline.setClickable(((CheckBox) view).isChecked()); } diff --git a/ApiDemos/app/src/main/res/drawable/chevron.png b/ApiDemos/app/src/main/res/drawable/chevron.png new file mode 100644 index 00000000..259e277d Binary files /dev/null and b/ApiDemos/app/src/main/res/drawable/chevron.png differ diff --git a/ApiDemos/app/src/main/res/layout/circle_demo.xml b/ApiDemos/app/src/main/res/layout/circle_demo.xml index 18edc26b..b40c5e09 100755 --- a/ApiDemos/app/src/main/res/layout/circle_demo.xml +++ b/ApiDemos/app/src/main/res/layout/circle_demo.xml @@ -33,37 +33,61 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical"> - - - + - - - + - - - + + + + + + + + + + + + + + + + android:text="@string/properties_australia_polygon" /> - - - + - - - + - - - + - - + + + + + + + + + + + + + + + + + + + + + diff --git a/ApiDemos/app/src/main/res/layout/polyline_demo.xml b/ApiDemos/app/src/main/res/layout/polyline_demo.xml index 654bbc91..7c9ddfb9 100644 --- a/ApiDemos/app/src/main/res/layout/polyline_demo.xml +++ b/ApiDemos/app/src/main/res/layout/polyline_demo.xml @@ -18,12 +18,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - @@ -43,9 +35,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical"> - - @@ -53,31 +43,76 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ApiDemos/app/src/main/res/values/strings.xml b/ApiDemos/app/src/main/res/values/strings.xml index 69c461c7..8748422d 100755 --- a/ApiDemos/app/src/main/res/values/strings.xml +++ b/ApiDemos/app/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ Clear Clickable + Color Compass Custom info contents Custom info window @@ -47,7 +48,6 @@ \u2193 Drag Melbourne Custom Duration - Hue Events Demonstrates event handling. Fade In Tiles @@ -59,13 +59,12 @@ Go to Sydney Ground Overlays Demonstrates how to add a GroundOverlay to a map. + Hue Hybrid Indoor @string/normal @string/hybrid - - @string/satellite @string/terrain @string/none_map @@ -102,8 +101,8 @@ Programmatically add map Demonstrates how to add a MapFragment programmatically. Properties for Circle(s) - Properties for Sydney Polyline - Properties for Sydney Polygon + Properties for Polyline over Australia + Properties for Polygon over Australia Rotate Gestures Raw MapView Demonstrates how to use a raw MapView. @@ -123,6 +122,10 @@ Area for the map snapshot. Take snapshot \u25A0 + Stroke Alpha + Stroke Hue + Stroke Joint Type + Stroke Pattern Stroke Width \u2190 Swipe \u2192 Switch Image @@ -154,6 +157,23 @@ + - + + Start cap + End cap + Joint Type + Pattern + Butt + Square + Round + Image + Default + Bevel + Round + Solid + Dotted + Dashed + Mixed + Lite Mode Demonstrates some features on a map in lite mode.