My problem is that when I zoom into a higher level, the drawing filled area disappear.
it will be very follow up the topic:
Android MapView overlay disappearing when zooming in
The above link solve my problem to draw the line, but the filled area still stay the same problem.
I need for help to solve the filled area too.
my current code as following :
class MyOverlay extends Overlay {
private final List<GeoPoint> points;
private int oldX;
private int oldY;
public MyOverlay(List<GeoPoint> points) {
this.points = points;
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
int x1 = -1;
int y1 = -1;
int x2 = -1;
int y2 = -1;
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.GREEN);
paint.setStrokeWidth(5);
int size = points.size();
for (int i = 0; i <= size; i++) {
Point point = new Point();
mapView.getProjection().toPixels(points.get(i % size),
point);
x2 = point.x;
y2 = point.y;
if (i > 0) {
canvas.drawLine(x1, y1, x2, y2, paint);
}
x1 = x2;
y1 = y2;
}
//the following are drawing the filled area
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.FILL);
paint.setAlpha(60);
Projection projection = mapView.getProjection();
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
projection.toPixels(gpoint1, p1);
projection.toPixels(gpoint2, p2);
projection.toPixels(gpoint3, p3);
Path path = new Path();
path.moveTo(p1.x, p1.y);
path.lineTo(p2.x, p2.y);
path.lineTo(p3.x, p3.y);
canvas.drawPath(path, paint);
}
}
You can get help from this code.
public abstract class BalloonItemizedOverlay<Item> extends
ItemizedOverlay<OverlayItem> {
private MapView mapView;
private BalloonOverlayView balloonView;
private View clickRegion;
private int viewOffset;
final MapController mc;
/**
* Create a new BalloonItemizedOverlay
*
* #param defaultMarker
* - A bounded Drawable to be drawn on the map for each item in
* the overlay.
* #param mapView
* - The view upon which the overlay items are to be drawn.
*/
public BalloonItemizedOverlay(Drawable defaultMarker, MapView mapView) {
super(defaultMarker);
this.mapView = mapView;
viewOffset = 0;
mc = mapView.getController();
}
#Override
public void draw(Canvas c, MapView m, boolean shadow) { // for disabling the
// shadow of overlay
super.draw(c, m, false);
}
/**
* Set the horizontal distance between the marker and the bottom of the
* information balloon. The default is 0 which works well for center bounded
* markers. If your marker is center-bottom bounded, call this before adding
* overlay items to ensure the balloon hovers exactly above the marker.
*
* #param pixels
* - The padding between the center point and the bottom of the
* information balloon.
*/
public void setBalloonBottomOffset(int pixels) {
viewOffset = pixels;
}
/**
* Override this method to handle a "tap" on a balloon. By default, does
* nothing and returns false.
*
* #param index
* - The index of the item whose balloon is tapped.
* #return true if you handled the tap, otherwise false.
*/
protected boolean onBalloonTap(int index) {
Globals.mapPinpointTap = true;
return false;
}
/*
* (non-Javadoc)
*
* #see com.google.android.maps.ItemizedOverlay#onTap(int)
*/
#Override
protected final boolean onTap(int index) {
boolean isRecycled;
final int thisIndex;
GeoPoint point;
Globals.mapPinpointTap = true;
thisIndex = index;
point = createItem(index).getPoint();
if (balloonView == null) {
balloonView = new BalloonOverlayView(mapView.getContext(),
viewOffset);
clickRegion = (View) balloonView
.findViewById(R.id.balloon_inner_layout);
isRecycled = false;
} else {
isRecycled = true;
}
balloonView.setVisibility(View.GONE);
List<Overlay> mapOverlays = mapView.getOverlays();
if (mapOverlays.size() > 1) {
hideOtherBalloons(mapOverlays);
}
balloonView.setData(createItem(index));
MapView.LayoutParams params = new MapView.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, point,
MapView.LayoutParams.BOTTOM_CENTER);
params.mode = MapView.LayoutParams.MODE_MAP;
setBalloonTouchListener(thisIndex);
balloonView.setVisibility(View.VISIBLE);
// /
/*
* String url = "http://maps.google.com/maps?q=" +
* (point.getLatitudeE6() / 1E6) + "," + (point.getLongitudeE6() / 1E6);
*/
// String url = "http://iceapp.coeus-solutions.de/api/map?location="
// + (point.getLatitudeE6() / 1E6) + ","
// + (point.getLongitudeE6() / 1E6);
// Intent browserIntent = new Intent(Intent.ACTION_VIEW,
// Uri.parse(url));
// balloonView.getContext().startActivity(browserIntent);
// /
if (isRecycled) {
balloonView.setLayoutParams(params);
} else {
mapView.addView(balloonView, params);
}
mc.animateTo(point);
return true;
}
/**
* Sets the visibility of this overlay's balloon view to GONE.
*/
private void hideBalloon() {
if (balloonView != null) {
balloonView.setVisibility(View.GONE);
}
}
/**
* Hides the balloon view for any other BalloonItemizedOverlay instances
* that might be present on the MapView.
*
* #param overlays
* - list of overlays (including this) on the MapView.
*/
private void hideOtherBalloons(List<Overlay> overlays) {
for (Overlay overlay : overlays) {
if (overlay instanceof BalloonItemizedOverlay<?> && overlay != this) {
((BalloonItemizedOverlay<?>) overlay).hideBalloon();
}
}
}
/**
* Sets the onTouchListener for the balloon being displayed, calling the
* overridden onBalloonTap if implemented.
*
* #param thisIndex
* - The index of the item whose balloon is tapped.
*/
private void setBalloonTouchListener(final int thisIndex) {
try {
#SuppressWarnings("unused")
Method m = this.getClass().getDeclaredMethod("onBalloonTap",
int.class);
Globals.mapPinpointTap = true;
clickRegion.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
View l = ((View) v.getParent())
.findViewById(R.id.balloon_main_layout);
Drawable d = l.getBackground();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int[] states = { android.R.attr.state_pressed };
if (d.setState(states)) {
d.invalidateSelf();
}
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
int newStates[] = {};
if (d.setState(newStates)) {
d.invalidateSelf();
}
// call overridden method
onBalloonTap(thisIndex);
return true;
} else {
return false;
}
}
});
} catch (SecurityException e) {
if (Globals.SHOW_LOGS)
Log.e("BalloonItemizedOverlay",
"setBalloonTouchListener reflection SecurityException");
return;
} catch (NoSuchMethodException e) {
// method not overridden - do nothing
return;
}
}
}
You should extend ItemizedOverlay instead of Overlay.
Implement it similar as described here "Part 2: Adding Overlay Items"
And add your draw method and other necessary stuff to it. Then it should work.
Related
I want to draw image on osm map, using osmdroid/osmbonuspack. I have tried Marker and SimpleLocationOverlay. These display the image as overlay, like a pin which doesn't change scale. But I want to show image which becomes part of Map, such that when Map is Zoomed-in, the image should scale up, and when zoomed-out, it should scale down.
With some modifications of GroundOverlay.java and MainActivity.java of osmbonuspack repo:
public class MainActivity extends AppCompatActivity {
MapView mMapView = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context ctx = getApplicationContext();
Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
setContentView(R.layout.activity_main);
mMapView = (MapView) findViewById(R.id.map);
mMapView.setTileSource(TileSourceFactory.MAPNIK);
mMapView.setBuiltInZoomControls(true);
mMapView.setMultiTouchControls(true);
GeoPoint overlayCenterPoint = new GeoPoint(50.450667, 30.523193);
IMapController mapController = mMapView.getController();
mapController.setZoom(17f);
mapController.setCenter(overlayCenterPoint);
mMapView.setMapOrientation(0.0f);
GroundOverlay myGroundOverlay = new GroundOverlay();
myGroundOverlay.setPosition(overlayCenterPoint);
Drawable d = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_launcher, null);
myGroundOverlay.setImage(d.mutate());
myGroundOverlay.setDimensions(200.0f);
myGroundOverlay.setTransparency(0.25f);
myGroundOverlay.setBearing(0);
mMapView.getOverlays().add(myGroundOverlay);
mMapView.invalidate();
}
#Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
#Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
public class GroundOverlay extends Overlay {
protected Drawable mImage;
protected GeoPoint mPosition;
protected float mBearing;
protected float mWidth, mHeight;
protected float mTransparency;
public final static float NO_DIMENSION = -1.0f;
protected Point mPositionPixels, mSouthEastPixels;
public GroundOverlay() {
super();
mWidth = 10.0f;
mHeight = NO_DIMENSION;
mBearing = 0.0f;
mTransparency = 0.0f;
mPositionPixels = new Point();
mSouthEastPixels = new Point();
}
public void setImage(Drawable image){
mImage = image;
}
public Drawable getImage(){
return mImage;
}
public GeoPoint getPosition(){
return mPosition.clone();
}
public void setPosition(GeoPoint position){
mPosition = position.clone();
}
public float getBearing(){
return mBearing;
}
public void setBearing(float bearing){
mBearing = bearing;
}
public void setDimensions(float width){
mWidth = width;
mHeight = NO_DIMENSION;
}
public void setDimensions(float width, float height){
mWidth = width;
mHeight = height;
}
public float getHeight(){
return mHeight;
}
public float getWidth(){
return mWidth;
}
public void setTransparency(float transparency){
mTransparency = transparency;
}
public float getTransparency(){
return mTransparency;
}
protected void computeHeight(){
if (mHeight == NO_DIMENSION && mImage != null){
mHeight = mWidth * mImage.getIntrinsicHeight() / mImage.getIntrinsicWidth();
}
}
/** #return the bounding box, ignoring the bearing of the GroundOverlay (similar to Google Maps API) */
public BoundingBox getBoundingBox(){
computeHeight();
GeoPoint pEast = mPosition.destinationPoint(mWidth, 90.0f);
GeoPoint pSouthEast = pEast.destinationPoint(mHeight, -180.0f);
double north = mPosition.getLatitude()*2 - pSouthEast.getLatitude();
double west = mPosition.getLongitude()*2 - pEast.getLongitude();
return new BoundingBox(north, pEast.getLongitude(), pSouthEast.getLatitude(), west);
}
public void setPositionFromBounds(BoundingBox bb){
mPosition = bb.getCenterWithDateLine();
GeoPoint pEast = new GeoPoint(mPosition.getLatitude(), bb.getLonEast());
GeoPoint pWest = new GeoPoint(mPosition.getLatitude(), bb.getLonWest());
mWidth = (float)pEast.distanceToAsDouble(pWest);
GeoPoint pSouth = new GeoPoint(bb.getLatSouth(), mPosition.getLongitude());
GeoPoint pNorth = new GeoPoint(bb.getLatNorth(), mPosition.getLongitude());
mHeight = (float)pSouth.distanceToAsDouble(pNorth);
}
#Override public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (shadow)
return;
if (mImage == null)
return;
computeHeight();
final Projection pj = mapView.getProjection();
pj.toPixels(mPosition, mPositionPixels);
GeoPoint pEast = mPosition.destinationPoint(mWidth/2, 90.0f);
GeoPoint pSouthEast = pEast.destinationPoint(mHeight/2, -180.0f);
pj.toPixels(pSouthEast, mSouthEastPixels);
int hWidth = mSouthEastPixels.x-mPositionPixels.x;
int hHeight = mSouthEastPixels.y-mPositionPixels.y;
mImage.setBounds(-hWidth, -hHeight, hWidth, hHeight);
mImage.setAlpha(255-(int)(mTransparency*255));
drawAt(canvas, mImage, mPositionPixels.x, mPositionPixels.y, false, -mBearing);
}
}
}
you can get something like that:
Main part is:
// overlay center point
GeoPoint overlayCenterPoint = new GeoPoint(50.450667, 30.523193);
IMapController mapController = mMapView.getController();
mapController.setZoom(17f);
mapController.setCenter(overlayCenterPoint);
mMapView.setMapOrientation(0.0f);
GroundOverlay myGroundOverlay = new GroundOverlay();
myGroundOverlay.setPosition(overlayCenterPoint);
Drawable d = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_launcher, null);
myGroundOverlay.setImage(d.mutate());
// overlay width in meters (height calculated automatically) also you can set both width and height
myGroundOverlay.setDimensions(200.0f);
myGroundOverlay.setTransparency(0.25f);
myGroundOverlay.setBearing(0);
mMapView.getOverlays().add(myGroundOverlay);
and it's "self-commented".
I'm using a custom view to get ripple effect for the pre-lollipop devices. But I also need to customize the container shape like a curved shape.I want to be the button like this.
As you can see in the second and third buttons when we tap the view the ripple effect animation goes outside of the container view. So how to resolve this?
Please note that I want this ripple effect for the Kitkat version with the ability to change the ripple color. So is this possible?
Here is my custom view which is used for the ripple effect
public class MyRippleView extends FrameLayout {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 400;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
#Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public MyRippleView(Context context) {
super(context);
}
public MyRippleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyRippleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* Method that initializes all fields and sets listeners
*
* #param context Context used to create this view
* #param attrs Attribute used to initialize fields
*/
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null) onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
* Launch Ripple animation for the current view with a MotionEvent
*
* #param event MotionEvent registered by the Ripple gesture listener
*/
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
* Launch Ripple animation for the current view centered at x and y position
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
* Create Ripple animation centered at x, y
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
this.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
/**
* Send a click event if parent view is a Listview instance
*
* #param isLongClick Is the event a long click ?
*/
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
* Set Ripple color, default is #FFFFFF
*
* #param rippleColor New color resource
*/
#ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType()
{
return RippleType.values()[rippleType];
}
/**
* Set Ripple type, default is RippleType.SIMPLE
*
* #param rippleType New Ripple type for next animation
*/
public void setRippleType(final RippleType rippleType)
{
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered()
{
return isCentered;
}
/**
* Set if ripple animation has to be centered in its parent view or not, default is False
*
* #param isCentered
*/
public void setCentered(final Boolean isCentered)
{
this.isCentered = isCentered;
}
public int getRipplePadding()
{
return ripplePadding;
}
/**
* Set Ripple padding if you want to avoid some graphic glitch
*
* #param ripplePadding New Ripple padding in pixel, default is 0px
*/
public void setRipplePadding(int ripplePadding)
{
this.ripplePadding = ripplePadding;
}
public Boolean isZooming()
{
return hasToZoom;
}
/**
* At the end of Ripple effect, the child views has to zoom
*
* #param hasToZoom Do the child views have to zoom ? default is False
*/
public void setZooming(Boolean hasToZoom)
{
this.hasToZoom = hasToZoom;
}
public float getZoomScale()
{
return zoomScale;
}
/**
* Scale of the end animation
*
* #param zoomScale Value of scale animation, default is 1.03f
*/
public void setZoomScale(float zoomScale)
{
this.zoomScale = zoomScale;
}
public int getZoomDuration()
{
return zoomDuration;
}
/**
* Duration of the ending animation in ms
*
* #param zoomDuration Duration, default is 200ms
*/
public void setZoomDuration(int zoomDuration)
{
this.zoomDuration = zoomDuration;
}
public int getRippleDuration()
{
return rippleDuration;
}
/**
* Duration of the Ripple animation in ms
*
* #param rippleDuration Duration, default is 400ms
*/
public void setRippleDuration(int rippleDuration)
{
this.rippleDuration = rippleDuration;
}
public int getFrameRate()
{
return frameRate;
}
/**
* Set framerate for Ripple animation
*
* #param frameRate New framerate value, default is 10
*/
public void setFrameRate(int frameRate)
{
this.frameRate = frameRate;
}
public int getRippleAlpha()
{
return rippleAlpha;
}
/**
* Set alpha for ripple effect color
*
* #param rippleAlpha Alpha value between 0 and 255, default is 90
*/
public void setRippleAlpha(int rippleAlpha)
{
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
/**
* Defines a callback called at the end of the Ripple effect
*/
public interface OnRippleCompleteListener {
void onComplete(MyRippleView rippleView);
}
public enum RippleType {
SIMPLE(0),
DOUBLE(1),
RECTANGLE(2);
int type;
RippleType(int type)
{
this.type = type;
}
}
}
In the layout XML file
<FrameLayout
android:background="#drawable/curved_button"
android:layout_width="match_parent"
android:layout_height="50dp">
<com.package.MyRippleView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rv_color="#color/colorAccent"
rv_centered="true">
</com.package.MyRippleView>
</FrameLayout>
Curved Shape
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item >
<shape android:shape="rectangle" >
<corners android:radius="40dip" />
<stroke android:width="1dp" android:color="#FF9A00" />
</shape>
</item>
It's possible. The easiest way is to use Carbon which does such things just like that. I was able to recreate your button using only xml and run it on Gingerbread.
<carbon.widget.Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rounded with ripple"
android:textColor="#color/carbon_amber_700"
app:carbon_cornerRadius="100dp"
app:carbon_backgroundTint="#color/carbon_white"
app:carbon_rippleColor="#40ff0000"
app:carbon_stroke="#color/carbon_amber_700"
app:carbon_strokeWidth="2dp" />
The downside is that Carbon is huge and you may not want to include it just for that one button.
If you wish to do that by yourself, you should use a path and a PorterDuff mode to clip your button to a rounded rect.
private float cornerRadius;
private Path cornersMask;
private static PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private void initCorners() {
cornersMask = new Path();
cornersMask.addRoundRect(new RectF(0, 0, getWidth(), getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
cornersMask.setFillType(Path.FillType.INVERSE_WINDING);
}
#Override
public void draw(#NonNull Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.draw(canvas);
paint.setXfermode(pdMode);
canvas.drawPath(cornersMask, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
And you should probably use ViewOutlineProvider on Lollipop to use native stuff where possible.
I am working on an Android application which should allow user to draw a circle on canvas and drag it . I have been able to draw lines and circles on it.But I cant seem to be able to drag it.
Is it possible to drag an object drawn on canvas in Android?.For example if I detect a touch within the circle, how do I delete the circle from a previous position and move it to the current position? I have tried invalidate().But it wont work if the user has drawn multiple circle and wants to move all of them.
It seems that you might have issues with handling of multi-touch / drawing. There's some usefull tutorials about it on Android Developer site and on Android Blog.
Based on this I was able to create an example which I think quite similar to that You're trying to achieve (without complete circle drawing - circles get generated by single touch):
public class CirclesDrawingView extends View {
private static final String TAG = "CirclesDrawingView";
/** Main bitmap */
private Bitmap mBitmap = null;
private Rect mMeasuredRect;
/** Stores data about single circle */
private static class CircleArea {
int radius;
int centerX;
int centerY;
CircleArea(int centerX, int centerY, int radius) {
this.radius = radius;
this.centerX = centerX;
this.centerY = centerY;
}
#Override
public String toString() {
return "Circle[" + centerX + ", " + centerY + ", " + radius + "]";
}
}
/** Paint to draw circles */
private Paint mCirclePaint;
private final Random mRadiusGenerator = new Random();
// Radius limit in pixels
private final static int RADIUS_LIMIT = 100;
private static final int CIRCLES_LIMIT = 3;
/** All available circles */
private HashSet<CircleArea> mCircles = new HashSet<CircleArea>(CIRCLES_LIMIT);
private SparseArray<CircleArea> mCirclePointer = new SparseArray<CircleArea>(CIRCLES_LIMIT);
/**
* Default constructor
*
* #param ct {#link android.content.Context}
*/
public CirclesDrawingView(final Context ct) {
super(ct);
init(ct);
}
public CirclesDrawingView(final Context ct, final AttributeSet attrs) {
super(ct, attrs);
init(ct);
}
public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) {
super(ct, attrs, defStyle);
init(ct);
}
private void init(final Context ct) {
// Generate bitmap used for background
mBitmap = BitmapFactory.decodeResource(ct.getResources(), R.drawable.up_image);
mCirclePaint = new Paint();
mCirclePaint.setColor(Color.BLUE);
mCirclePaint.setStrokeWidth(40);
mCirclePaint.setStyle(Paint.Style.FILL);
}
#Override
public void onDraw(final Canvas canv) {
// background bitmap to cover all area
canv.drawBitmap(mBitmap, null, mMeasuredRect, null);
for (CircleArea circle : mCircles) {
canv.drawCircle(circle.centerX, circle.centerY, circle.radius, mCirclePaint);
}
}
#Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
CircleArea touchedCircle;
int xTouch;
int yTouch;
int pointerId;
int actionIndex = event.getActionIndex();
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// it's the first pointer, so clear all existing pointers data
clearCirclePointer();
xTouch = (int) event.getX(0);
yTouch = (int) event.getY(0);
// check if we've touched inside some circle
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
mCirclePointer.put(event.getPointerId(0), touchedCircle);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.w(TAG, "Pointer down");
// It secondary pointers, so obtain their ids and check circles
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
// check if we've touched inside some circle
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
mCirclePointer.put(pointerId, touchedCircle);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
invalidate();
handled = true;
break;
case MotionEvent.ACTION_MOVE:
final int pointerCount = event.getPointerCount();
Log.w(TAG, "Move");
for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
// Some pointer has moved, search it by pointer id
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
touchedCircle = mCirclePointer.get(pointerId);
if (null != touchedCircle) {
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
}
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
clearCirclePointer();
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
// not general pointer was up
pointerId = event.getPointerId(actionIndex);
mCirclePointer.remove(pointerId);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
handled = true;
break;
default:
// do nothing
break;
}
return super.onTouchEvent(event) || handled;
}
/**
* Clears all CircleArea - pointer id relations
*/
private void clearCirclePointer() {
Log.w(TAG, "clearCirclePointer");
mCirclePointer.clear();
}
/**
* Search and creates new (if needed) circle based on touch area
*
* #param xTouch int x of touch
* #param yTouch int y of touch
*
* #return obtained {#link CircleArea}
*/
private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);
if (null == touchedCircle) {
touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT);
if (mCircles.size() == CIRCLES_LIMIT) {
Log.w(TAG, "Clear all circles, size is " + mCircles.size());
// remove first circle
mCircles.clear();
}
Log.w(TAG, "Added circle " + touchedCircle);
mCircles.add(touchedCircle);
}
return touchedCircle;
}
/**
* Determines touched circle
*
* #param xTouch int x touch coordinate
* #param yTouch int y touch coordinate
*
* #return {#link CircleArea} touched circle or null if no circle has been touched
*/
private CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touched = null;
for (CircleArea circle : mCircles) {
if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
touched = circle;
break;
}
}
return touched;
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mMeasuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
}
Activity contains only setContentView(R.layout.main) there main.xml is the following:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/scroller">
<com.example.TestApp.CirclesDrawingView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
In my app Draw paint in free hand on Map view but searching lot of information finally got from rectangle shape draw on mapview but i want in place of rectangle draw free hand like zigzag how to change my code Any help please..
MapOverlay.java
public class MapOverlay extends Overlay {
private float x1,y1,x2,y2;
private GeoPoint p1=null,p2=null;
private MapExampleActivity mv = null;
private Paint paint = new Paint();
private Path path = new Path();
private boolean isUp = false;
//constructor receiving the initial point
public MapOverlay(MapExampleActivity mapV,float x,float y){
paint.setStrokeWidth(2.0f);
x1 = x;
y1 = y;
mv = mapV;
p1 = mapV.getMapView().getProjection().fromPixels((int)x1,(int)y1);
}
//override draw method to add our custom drawings
#Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
if(p1 != null && p2 != null){
//get the 2 geopoints defining the area and transform them to pixels
//this way if we move or zoom the map rectangle will follow accordingly
Point screenPts1 = new Point();
mapView.getProjection().toPixels(p1, screenPts1);
Point screenPts2 = new Point();
mapView.getProjection().toPixels(p2, screenPts2);
//draw inner rectangle
paint.setColor(Color.BLUE);
// paint.setStyle(Style.FILL);
canvas.drawPath(path, paint);
canvas.drawRect(screenPts1.x, screenPts1.y, screenPts2.x, screenPts2.y, paint);
//draw outline rectangle
// paint.setColor(Color.YELLOW);
paint.setStyle(Style.STROKE);
// canvas.drawRect(screenPts1.x, screenPts1.y, screenPts2.x, screenPts2.y, paint);
canvas.drawPath(path, paint);
}
return true;
}
#Override
public boolean onTouchEvent(MotionEvent e, MapView mapView) {
if(mv.isEditMode() && !isUp){
if(e.getAction() == MotionEvent.ACTION_DOWN){
x1 = y1 = 0;
x1 = e.getX();
y1 = e.getY();
p1 = mapView.getProjection().fromPixels((int)x1,(int)y1);
}
//here we constantly change geopoint p2 as we move out finger
if(e.getAction() == MotionEvent.ACTION_MOVE){
x2 = e.getX();
y2 = e.getY();
p2 = mapView.getProjection().fromPixels((int)x2,(int)y2);
}
//---when user lifts his finger---
if (e.getAction() == MotionEvent.ACTION_UP) {
isUp = true;
}
return true;
}
return false;
}
}
using this i able to draw like this rectangle shapes and draw up to again you click the toggle button(possible to draw multiple times)
i want draw lines instead of rectangle like below image(draw multiple times).
finally i found this link this link provide rectangle shape draw http://n3vrax.wordpress.com/2011/08/13/drawing-overlays-on-android-map-view/
just change rectangle to free draw any idea please....
You can free hand draw a line using the code bellow:
Code
public class HandDrawOverlay extends Overlay {
private boolean editMode = false;
private boolean isTouched = false;
private Paint paint = new Paint();
private Point screenPt1 = new Point();
private Point screenPt2 = new Point();
private ArrayList<GeoPoint> points = null;
public HandDrawOverlay(){
paint.setStrokeWidth(2.0f);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLUE);
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if(points != null && points.size() > 1){
mapView.getProjection().toPixels(points.get(0), screenPt1);
for(int i=1; i<points.size();i++){
mapView.getProjection().toPixels(points.get(i), screenPt2);
canvas.drawLine(screenPt1.x, screenPt1.y, screenPt2.x, screenPt2.y, paint);
screenPt1.set(screenPt2.x, screenPt2.y);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent e, MapView mapView) {
if(editMode){
int x = (int)e.getX();
int y = (int)e.getY();
GeoPoint geoP = mapView.getProjection().fromPixels(x,y);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouched = true;
points = new ArrayList<GeoPoint>();
points.add(geoP);
break;
case MotionEvent.ACTION_MOVE:
if(isTouched)
points.add(geoP);
break;
case MotionEvent.ACTION_UP:
if(isTouched)
points.add(geoP);
isTouched = false;
break;
}
mapView.invalidate();
return true;
}
return false;
}
/**
* #return the editMode
*/
public boolean isEditMode() {
return editMode;
}
/**
* #param editMode the editMode to set
*/
public void setEditMode(boolean editMode) {
this.editMode = editMode;
}
}
to use
HandDrawOverlay handDrawOverlay;
handDrawOverlay = new HandDrawOverlay();
mapView.getOverlays().add(handDrawOverlay);
//Set edit mode to true to start drwaing
handDrawOverlay.setEditMode(true);
//Set edit mode to true to stop drwaing
handDrawOverlay.setEditMode(false);
Note
This is a full functioning example to help you starting. However, you should optimize the code to make it more efficient (i.e. using Path to store the drawing path in onDraw(), reducing the number of points recorded in onTouch(), etc.).
Enjoy it.
How the onTap listener works in android map view even if the map view is moved.
actually i am rotating map view based on user direction.
i am getting bearing angle from sesor manager.
if angle is 0 the onTap works correctly on Pin.If the angle is changed say 20 onTap not working exactly on pin it works beside the pin after some space .
I taken on Rotate View inside that i taken mapview .Now i rotation rotateview after bearing changes.
RotateView setting bearing code
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
//if mHeading is zero its work otherwise onTap position is changing
canvas.rotate(mHeading, getWidth()/2, getHeight());
mCanvas.delegate = canvas;
super.dispatchDraw(mCanvas);
canvas.restore();
}
Overlay class
public class SonarOverlay1 extends ItemizedOverlay<OverlayItem>
{
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
private Context mContext;
public SonarOverlay1(Drawable defaultMarker,Context context) {
super(boundCenter(defaultMarker));
this.mContext=context;
populate();
}
#Override
protected OverlayItem createItem(int arg0) {
return mOverlays.get(arg0);
}
public void addOverlay(OverlayItem item) {
mOverlays.add(item);
populate();
}
#Override
public int size() {
//Log.e("overlay size..",""+mOverlays.size());
return mOverlays.size();
}
#Override
protected boolean onTap(int index) {
// TODO Auto-generated method stub
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
}
Here while rotating map we have to rotate touch evnts also along with map using onDispatchTouch method
see below code
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
float[] coords = new float[] {
event.getX(), event.getY()
};
adjustCoords(coords, getRotation());
MotionEvent evt = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), event
.getAction(), coords[0], coords[1], event.getPressure(), event.getSize(), event
.getMetaState(), event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
event.getEdgeFlags());
return super.dispatchTouchEvent(evt);
}
protected void adjustCoords(float[] coords, float deg) {
float x = coords[0];
float y = coords[1];
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// convert to radians
float rad = (float) ((deg * Math.PI) / 180F);
float s = (float) Math.sin(rad);
float c = (float) Math.cos(rad);
// translate point back to origin:
x -= centerX;
y -= centerY;
// apply rotation
float tmpX = x * c - y * s;
float tmpY = x * s + y * c;
x = tmpX;
y = tmpY;
// translate point back:
x += centerX;
y += centerY;
coords[0] = x;
coords[1] = y;
}
Please refer below link
How to manage overlays in MapViewCompassDemo in android sdk samples