I've been searching a lot, but I can't get a solution for my problem. I can't use android:rotation as I want this app to be compatible with Android API under 11 version.
My problem is something similar to this: Rotating a view in Android
I've done this:
#Override
public void draw(Canvas canvas) {
canvas.save();
Drawable d = getDrawable();
canvas.rotate(-10, d.getIntrinsicWidth() / 2, d.getIntrinsicHeight() / 2);
super.draw(canvas);
canvas.restore();
}
The ImageView is part of a RelativeLayout where I place the image and some text.
But the image is cropped in the edges. How can I avoid that?
You simply have to add:
android:clipChildren="false"
to the parent ViewGroup.
For Example:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:clipChildren="false">
<com.yourcompany.views.RotateImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#android:color/white"
android:src="#drawable/ic_launcher" />
</FrameLayout>
RotateImageView:
public class RotateImageView extends ImageView {
#Override
public void draw(Canvas canvas) {
canvas.save();
int height = canvas.getHeight();
int width = canvas.getWidth();
canvas.rotate(45, height / 2, width / 2);
super.draw(canvas);
canvas.restore();
}
}
Which provides a clean solution before API Level 11
Try adding your ImageView as a child view of a FrameLayout and set the FrameLayouts width and height the same as that of your ImageView. View my similar answer here. Original answer relayed from a post by Pavlo Viazovskyy here. Hope this helps!
Related
I'm placing a very wide image inside a HorizontalScrollView.
The ImageView/ScrollView height is dynamic as I've set the height to 0dp and added constraints. Since the ImageView's scale type is fitStart and adjustViewBounds is true - the image's width is being resized.
XML:
<HorizontalScrollView
android:id="#+id/mapScrollView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toTopOf="#id/playButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.constraint.ConstraintLayout
android:id="#+id/mapLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="#+id/mapImage"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:scaleType="fitStart"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.gigi.testmap.activities.quest.QuestMapPathView
android:id="#+id/mapLinesView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#id/mapImage"
app:layout_constraintEnd_toEndOf="#id/mapImage"
app:layout_constraintStart_toStartOf="#id/mapImage"
app:layout_constraintTop_toTopOf="#id/mapImage" />
</android.support.constraint.ConstraintLayout>
</HorizontalScrollView>
I'm trying to get the ImageView width (total width, visible & invisible part). Getting the ScrollView total width will also help.
My goal is to place buttons on top of the map in positions calculated according to width & height of the rendered ImageView.
I'm loading the image using Glide:
final ImageView mapImage = mActivity.findViewById(R.id.mapImage);
Glide.with(mActivity).load(R.drawable.fullmap).into(mapImage);
mapImage.getViewTreeObserver().addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
mapImage.getWidth(); // --> returns 0
mapImage.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
I've tried getting the width using tree view observer's global layout listener but all I got is 0. The height is returned correctly though.
Any help will be much appreciated,
Thank you very much.
I have no experience with Glide.
You are setting the all views / containers android:layout_height="0dp"
try first to change it to any other arbitrary value or wrap_content.
you did not attach the code of how you are trying to get the height.
Have you tryed mapImage.getHeight()
For Bitmap, I used to get this way
Glide.with(mContext())
.asBitmap()
.load(path)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap,
Transition<? super Bitmap> transition) {
int w = bitmap.getWidth();
int h = bitmap.getHeight()
}
});
It seems that the image is not fully rendered once the tree view global layout listener is first called (I guess it's because the image width is pretty big) - What I did was to have a check for the image width and only if greater than 0, remove the listener and continue with my code.
final ImageView mapImage = mActivity.findViewById(R.id.mapImage);
Glide.with(mActivity).load(R.drawable.fullmap).into(mapImage);
mapImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (mapImage.getWidth() > 0) {
mapImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
//continue width related code here
}
}
});
Maybe not the prefect solution and there is a small delay until the image shows up - but it is ok for my use case.
I want to give the map a nice looking rounded corners as the two boxes below it have.
I can't do it with the map fragment it self because there is not a background property to a
fragment.
setting the map inside a layout and setting it background to a rounded shape didn't help me
as well and this is the result:
I could merge the map but this would make it smaller and i would like to avoid it.
EDIT:
#Ryan this is the new result #2:
I guess this is not bad, no even close to the corners on the other boxes,
but still not bad with a little more work a could get somewhere close i just dont have a normal image editor.
but one thing that still bothers me now is the separation between the "Location" Textview and the map it's self. could i painted the patch in other way so that there was now distance? this is how i did it:
Well I have finally figured this out:
this is what i used for the patch:
Thanks.
I know it's an old post, but you can try using Cards like so:
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="350dp"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="20dp"
app:cardCornerRadius="12dp"
app:cardElevation="12dp">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>
I haven't tried this, but I'd put a view with rounded corners and a transparent middle on top of the mapView / mapFragment.
That is, put the mapFragment and the rounded corner view in a FrameLayout with both filling the FrameLayout, then make the middle of the rounded corner view transparent.
For further clarification, you could do it in a layout as follows:-
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="#+id/mapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/rounded_background"
android:orientation="vertical" >
</LinearLayout>
</FrameLayout>
The rounded_background is a 9-patch with rounded corners and a transparent middle. E.g.
Hope that helps,
Ryan
The easiest way is to wrap the map fragment inside a FrameLayout along with an ImageView. The Imageview would display a rounded rectangle on top of the map fragment. In its simplest form you will see the map fragment inside the rounded rectangle with its corners sticking out of the rounded rectangle because the map view itself is not rounded. To overcome this visual oddity simply apply a layout_margin value on map fragment. The value should be equal to the rectangle's border width.
<FrameLayout
android:id="#+id/map_container"
android:layout_width="wrap_content"
android:layout_height="340dp" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/map_bg_box" />
</FrameLayout>
The rectangle drawable is defined as an xml shape as below
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="3dp"
android:color="#ff000000" />
<corners android:bottomRightRadius="7dp" android:bottomLeftRadius="7dp"
android:topLeftRadius="7dp" android:topRightRadius="7dp"/>
</shape>
Notice the stroke width of the rectangle is 3dp that is exactly the same value we applied to the layout_margin property of the map fragment. The result is a nicely round cornered map fragment as shown in the screenshot below
Wrap the map fragment in this layout:
package com.example.yourpackage;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
* Just extend any Layout you like/need
*/
public class RoundedLayout extends RelativeLayout {
private Path mPathCorners = new Path();
private Path mPathCircle = new Path();
private float mCornerRadius;
/**
* border path
*/
private Path mPathCornersBorder = new Path();
private Path mPathCircleBorder = new Path();
private int mBorderWidth = 0;
private int mBorderHalf;
private boolean mShowBorder = false;
private int mBorderColor = 0xFFFF7700;
private float mDensity = 1.0f;
/**
* Rounded corners or circle shape
*/
private boolean mIsCircleShape = false;
private Paint mPaint = new Paint();
private float dpFromPx(final float px) {
return px / mDensity;
}
private float pxFromDp(final float dp) {
return dp * mDensity;
}
public RoundedLayout(Context context) {
this(context, null);
}
public RoundedLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundedLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDensity = getResources().getDisplayMetrics().density;
// just a default for corner radius
mCornerRadius = pxFromDp(25f);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBorderColor);
setBorderWidth(Math.round(pxFromDp(2f)));
}
/**
* Switch to circle or rectangle shape
*
* #param useCircle
*/
public void setShapeCircle(boolean useCircle) {
mIsCircleShape = useCircle;
invalidate();
}
/**
* change corner radius
*
* #param radius
*/
public void setCornerRadius(int radius) {
mCornerRadius = radius;
invalidate();
}
public void showBorder(boolean show) {
mShowBorder = show;
invalidate();
}
public void setBorderWidth(int width) {
mBorderWidth = width;
mBorderHalf = Math.round(mBorderWidth / 2);
if (mBorderHalf == 0) {
mBorderHalf = 1;
}
mPaint.setStrokeWidth(mBorderWidth);
updateCircleBorder();
updateRectangleBorder();
invalidate();
}
public void setBorderColor(int color) {
mBorderColor = color;
mPaint.setColor(color);
invalidate();
}
// helper reusable vars, just IGNORE
private float halfWidth, halfHeight, centerX, centerY;
private RectF rect = new RectF(0, 0, 0, 0);
private RectF rectBorder = new RectF(0, 0, 0, 0);
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// just calculate both shapes, is not heavy
// rounded corners path
rect.left = 0;
rect.top = 0;
rect.right = w;
rect.bottom = h;
mPathCorners.reset();
mPathCorners.addRoundRect(rect, mCornerRadius, mCornerRadius, Path.Direction.CW);
mPathCorners.close();
// circle path
halfWidth = w / 2f;
halfHeight = h / 2f;
centerX = halfWidth;
centerY = halfHeight;
mPathCircle.reset();
mPathCircle.addCircle(centerX, centerY, Math.min(halfWidth, halfHeight), Path.Direction.CW);
mPathCircle.close();
updateRectangleBorder();
updateCircleBorder();
}
// helper reusable var, just IGNORE
private int save;
#Override
protected void dispatchDraw(Canvas canvas) {
save = canvas.save();
canvas.clipPath(mIsCircleShape ? mPathCircle : mPathCorners);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
if (mShowBorder) {
canvas.drawPath(mIsCircleShape ? mPathCircleBorder : mPathCornersBorder, mPaint);
}
}
private void updateCircleBorder() {
// border path for circle
mPathCircleBorder.reset();
mPathCircleBorder.addCircle(centerX, centerY, Math.min(halfWidth - mBorderHalf,
halfHeight - mBorderHalf), Path.Direction.CW);
mPathCircleBorder.close();
}
private void updateRectangleBorder() {
// border path for rectangle
rectBorder.left = rect.left + mBorderHalf;
rectBorder.top = rect.top + mBorderHalf;
rectBorder.right = rect.right - mBorderHalf;
rectBorder.bottom = rect.bottom - mBorderHalf;
mPathCornersBorder.reset();
mPathCornersBorder.addRoundRect(rectBorder, mCornerRadius - mBorderHalf, mCornerRadius -
mBorderHalf, Path.Direction.CW);
mPathCornersBorder.close();
}
}
In layout will be like this:
<com.example.yourpackage.RoundedLayout
android:id="#+id/maplayout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="20dp">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="200dp"
tools:context="com.example.yourpackage.MapsMarkerActivity"/>
</com.example.yourpackage.RoundedLayout>
In code can be like this for a round shape with border:
RoundedLayout rl = (RoundedLayout) findViewById(R.id.maplayout);
rl.setShapeCircle(true);
rl.showBorder(true);
rl.setBorderWidth(2);
This layout can be used to shape any view.
It's incredible how google is incapable of making competent (usable) complete demos for it's android API.
For other people looking into this, I just tackled this using GoogleMap.snapshot and manipulating the bitmap result with this stack over flow answer:
How to make an ImageView with rounded corners?
Mind you this is only valid if you are going to have a static map that is not going to be interacted with.
Make sure you take the snap shot after the map is loaded.
I updated the image view helper code to draw with path to support rounding only some corners. ie. If you want to round only 2 of the corners.
You just need the path round rect function that takes a float[]
I show a progress bar until I get a callback from GoogleMap loaded listener than I take the snapshot.
If you take your snapshot too early you will get can't create bitmap with 0 width and height error.
Hope this helps someone looking for rounded corners or other weird shape in static map snapshot.
If you are only trying to target API 21 (Lollipop) and higher
This is the easiest way possible.
parentView.setClipToOutline(true);
Result
Following #Nouman_Hanif post I ended up with a solution that looks quite good.
map_rounded_corner_overlay.xml:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="1dp"
android:color="#color/white" />
<corners android:radius="<your_desired_view_corner_radius>"/>
</shape>
My map xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_margin="1dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/map_rounded_corner_overlay" />
</RelativeLayout>
I have a list of posts and most of them are pictures (simply put it is posts just like G+ or FB apps). Each post entry has an image aspect ratio, so I can set image height based on it's width even before image was loaded from server, so card layout wouldn't change on load.
The problem is layout_width="match_parent" set for both card and post image. When I get width of cardview it is zero. So i can't calculate height.
For now the only solution I see is to take width of parent container (RecyclerView) and deduct all paddings, but it doesn't look like a good solution.
Is there any other way to do it?
Here is an example of adapter code
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
....
int width = holder.itemView.getWidth();
....
//do some calculations
}
Layouts (without irrelevant parts)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:foreground="?android:attr/selectableItemBackground"
card_view:cardBackgroundColor="#ffffff"
card_view:cardCornerRadius="3dp">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="#+id/includedPost"
layout="#layout/post_details" />
</RelativeLayout>
</android.support.v7.widget.CardView>
includedPost:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/postImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/commenterImage"
android:minHeight="120dp"
android:scaleType="centerCrop" />
</RelativeLayout>
when onBind or onViewAttachedToWindow is called, the child is not measured yet so you cannot get the width. Even if these calls were made after child is measured, what you are trying to do would not be a good practice because changing height will require a new measurement.
If you are using LinearLayoutManager, it will give the full width to the child (expect RecyclerView padding and child's margins). It is not great but OK to derive your height from there.
Another (more flexible) approach here is to create a custom ImageView that keeps your aspect ratio. When onBind is called, you'll set the desired aspect ratio of your custom ImageView.
When on measure is called, it will measure depending on your aspect ratio.
class MyImageView extends ImageVIew {
....
private float mScale = 1f;
public void setScale(int scale) {
mScale = scale;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
setMeasuredDimension(width, width * mScale);
}
}
So in your onBind method, you call setScale on the ImageView depending on your w/h ratio.
I have not tested but this approach should work as desired.
You can use Picasso Transformation to achieve this.
FitToTargetViewTransformation class:
import android.graphics.Bitmap;
import android.view.View;
import com.squareup.picasso.Transformation;
/**
* Picasso Transformation class to fit image to target View size
*/
public class FitToTargetViewTransformation implements Transformation {
private View view;
public FitToTargetViewTransformation(View view) {
this.view = view;
}
#Override
public Bitmap transform(Bitmap source) {
int targetWidth = view.getWidth();
double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
int targetHeight = (int) (targetWidth * aspectRatio);
if (source.getHeight() >= source.getWidth()) {
return source;
}
Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
if (result != source) {
// Same bitmap is returned if sizes are the same
source.recycle();
}
return result;
}
#Override
public String key() {
return "transformation" + " desiredWidth";
}
}
And somewhere in onBindViewHolder you do something like this:
Picasso.with(context)
.load(AVATAR_ENDPOINT)
.transform(new FitToTargetViewTransformation(feedViewHolder.icAvatar))
.into(feedViewHolder.icAvatar);
I'm new on Android, and I am having trouble with the following:
I have an image of an empty test-tube (png or bmp).
And I need to draw lines on top of it to make the illusion that its being filled in with liquid.
I really don't know how to proceed. I have read google's documentation about animations, but that didn't help much.
I'd appreciate if you guys could give me some suggestions of how it can be done, and point to some tutorials/documentation that can help me.
Thanks in advance.
UPDATE:
The tube is not retangular, the bottom is oval.
I think I need to make the liquid fall into the test tube, then paint line by line, starting from the bottom. And I have to check for the borders of the tube (right and lef black pixels).
Any ideas of how this can be done?
UPDATE 2:
Here is the tube image: http://i61.tinypic.com/2nw0eb9.png
You can use a SurfaceView to draw what ever you want:
Basicly, you lock the surface's canvas by
Canvas canvas = mSurfaceView.getHolder().lockCanvas();
Then, use the canvas's methods to draw on it. canvas.drawBitmap, canvas.drawLine etc..
When you're finished lock the canvas with mSurfaceView.getHolder().unlockCanvasAndPost(canvas); and you're done.
here's an example from a quick google search:
http://android-coding.blogspot.co.il/2011/05/drawing-on-surfaceview.html
Best way to do this would be with a custom View. Make a new class, that extends View, then in its onDraw method first draw the picture, then draw your animations. If you want to do it by hand, you can do something like this:
private class TestTubeView extends View {
private int top = 0;
private Paint myPaint;
public MyView(Context context) {
super(context);
myPaint = new Paint();
myPaint.setColor(getResources().getColor(R.color.blue));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//First draw your bitmap
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.my_testtube), 0, 0, myPaint); //might need to use a different paint
//Then your "animation" as a static image, that has its position set from a variable, in this case "y" and "x"
canvas.drawRect(0, top, getWidth(), getHeight(), myPaint);
}
//In this method update your variables, that define the positions of your animated lines / bubbles
public boolean updateAnimation() {
top++;
invalidate();
//So it stops animationg
return top > getHeight();
}
}
Then in your layout you put it in like a normal view:
<com.example.TestTubeView
android:id="#+id/my_testtube"
android:layout_width="20dp"
android:layout_height="200dp" />
And then you animate it with a self-repeating Runnable:
final MyView testTube = findViewById(R.id.my_testtube);
final Handler myHandler = new Handler();
myHandler.post(new Runnable() {
#Override
public void run() {
if(testTube.updateAnimation()){
myHandler.postDelayed(this, 200);
}
}
});
You'll have to play around with sizes / heights and things like that though. Another way of doing this is with an ObjectAnimatior
Tube Drawable: (this is for test purposes. you will use your tube image)
tube.xml (drawable folder)
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:width="5dp" android:color="#ffccffff" />
<solid android:color="#00000000" />
</shape>
tube_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/orangeJuiceLinearLayout"
android:layout_width="140dp"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:orientation="vertical"
android:background="#fff58225">
</LinearLayout>
<ImageView
android:id="#+id/tubeImageView"
android:layout_width="140dp"
android:layout_height="220dp"
android:layout_gravity="center"
android:background="#drawable/tube"
android:onClick="fillJuice"
android:clickable="true" />
</FrameLayout>
</LinearLayout>
TubeAcivity.java
public class TubeActivity extends Activity {
LinearLayout orangeLL;
ImageView tubeIV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tube_activity);
orangeLL = (LinearLayout)findViewById(R.id.orangeJuiceLinearLayout);
tubeIV = (ImageView)findViewById(R.id.tubeImageView);
}
public void fillJuice(View view) {
ValueAnimator va = ValueAnimator.ofInt(0, tubeIV.getMeasuredHeight());
va.setDuration(1500);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
orangeLL.getLayoutParams().height = value.intValue();
orangeLL.requestLayout();
}
});
va.start();
}
}
I want to give the map a nice looking rounded corners as the two boxes below it have.
I can't do it with the map fragment it self because there is not a background property to a
fragment.
setting the map inside a layout and setting it background to a rounded shape didn't help me
as well and this is the result:
I could merge the map but this would make it smaller and i would like to avoid it.
EDIT:
#Ryan this is the new result #2:
I guess this is not bad, no even close to the corners on the other boxes,
but still not bad with a little more work a could get somewhere close i just dont have a normal image editor.
but one thing that still bothers me now is the separation between the "Location" Textview and the map it's self. could i painted the patch in other way so that there was now distance? this is how i did it:
Well I have finally figured this out:
this is what i used for the patch:
Thanks.
I know it's an old post, but you can try using Cards like so:
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="350dp"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="20dp"
app:cardCornerRadius="12dp"
app:cardElevation="12dp">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>
I haven't tried this, but I'd put a view with rounded corners and a transparent middle on top of the mapView / mapFragment.
That is, put the mapFragment and the rounded corner view in a FrameLayout with both filling the FrameLayout, then make the middle of the rounded corner view transparent.
For further clarification, you could do it in a layout as follows:-
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="#+id/mapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/rounded_background"
android:orientation="vertical" >
</LinearLayout>
</FrameLayout>
The rounded_background is a 9-patch with rounded corners and a transparent middle. E.g.
Hope that helps,
Ryan
The easiest way is to wrap the map fragment inside a FrameLayout along with an ImageView. The Imageview would display a rounded rectangle on top of the map fragment. In its simplest form you will see the map fragment inside the rounded rectangle with its corners sticking out of the rounded rectangle because the map view itself is not rounded. To overcome this visual oddity simply apply a layout_margin value on map fragment. The value should be equal to the rectangle's border width.
<FrameLayout
android:id="#+id/map_container"
android:layout_width="wrap_content"
android:layout_height="340dp" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:name="com.google.android.gms.maps.SupportMapFragment"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/map_bg_box" />
</FrameLayout>
The rectangle drawable is defined as an xml shape as below
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="3dp"
android:color="#ff000000" />
<corners android:bottomRightRadius="7dp" android:bottomLeftRadius="7dp"
android:topLeftRadius="7dp" android:topRightRadius="7dp"/>
</shape>
Notice the stroke width of the rectangle is 3dp that is exactly the same value we applied to the layout_margin property of the map fragment. The result is a nicely round cornered map fragment as shown in the screenshot below
Wrap the map fragment in this layout:
package com.example.yourpackage;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
* Just extend any Layout you like/need
*/
public class RoundedLayout extends RelativeLayout {
private Path mPathCorners = new Path();
private Path mPathCircle = new Path();
private float mCornerRadius;
/**
* border path
*/
private Path mPathCornersBorder = new Path();
private Path mPathCircleBorder = new Path();
private int mBorderWidth = 0;
private int mBorderHalf;
private boolean mShowBorder = false;
private int mBorderColor = 0xFFFF7700;
private float mDensity = 1.0f;
/**
* Rounded corners or circle shape
*/
private boolean mIsCircleShape = false;
private Paint mPaint = new Paint();
private float dpFromPx(final float px) {
return px / mDensity;
}
private float pxFromDp(final float dp) {
return dp * mDensity;
}
public RoundedLayout(Context context) {
this(context, null);
}
public RoundedLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundedLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDensity = getResources().getDisplayMetrics().density;
// just a default for corner radius
mCornerRadius = pxFromDp(25f);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBorderColor);
setBorderWidth(Math.round(pxFromDp(2f)));
}
/**
* Switch to circle or rectangle shape
*
* #param useCircle
*/
public void setShapeCircle(boolean useCircle) {
mIsCircleShape = useCircle;
invalidate();
}
/**
* change corner radius
*
* #param radius
*/
public void setCornerRadius(int radius) {
mCornerRadius = radius;
invalidate();
}
public void showBorder(boolean show) {
mShowBorder = show;
invalidate();
}
public void setBorderWidth(int width) {
mBorderWidth = width;
mBorderHalf = Math.round(mBorderWidth / 2);
if (mBorderHalf == 0) {
mBorderHalf = 1;
}
mPaint.setStrokeWidth(mBorderWidth);
updateCircleBorder();
updateRectangleBorder();
invalidate();
}
public void setBorderColor(int color) {
mBorderColor = color;
mPaint.setColor(color);
invalidate();
}
// helper reusable vars, just IGNORE
private float halfWidth, halfHeight, centerX, centerY;
private RectF rect = new RectF(0, 0, 0, 0);
private RectF rectBorder = new RectF(0, 0, 0, 0);
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// just calculate both shapes, is not heavy
// rounded corners path
rect.left = 0;
rect.top = 0;
rect.right = w;
rect.bottom = h;
mPathCorners.reset();
mPathCorners.addRoundRect(rect, mCornerRadius, mCornerRadius, Path.Direction.CW);
mPathCorners.close();
// circle path
halfWidth = w / 2f;
halfHeight = h / 2f;
centerX = halfWidth;
centerY = halfHeight;
mPathCircle.reset();
mPathCircle.addCircle(centerX, centerY, Math.min(halfWidth, halfHeight), Path.Direction.CW);
mPathCircle.close();
updateRectangleBorder();
updateCircleBorder();
}
// helper reusable var, just IGNORE
private int save;
#Override
protected void dispatchDraw(Canvas canvas) {
save = canvas.save();
canvas.clipPath(mIsCircleShape ? mPathCircle : mPathCorners);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
if (mShowBorder) {
canvas.drawPath(mIsCircleShape ? mPathCircleBorder : mPathCornersBorder, mPaint);
}
}
private void updateCircleBorder() {
// border path for circle
mPathCircleBorder.reset();
mPathCircleBorder.addCircle(centerX, centerY, Math.min(halfWidth - mBorderHalf,
halfHeight - mBorderHalf), Path.Direction.CW);
mPathCircleBorder.close();
}
private void updateRectangleBorder() {
// border path for rectangle
rectBorder.left = rect.left + mBorderHalf;
rectBorder.top = rect.top + mBorderHalf;
rectBorder.right = rect.right - mBorderHalf;
rectBorder.bottom = rect.bottom - mBorderHalf;
mPathCornersBorder.reset();
mPathCornersBorder.addRoundRect(rectBorder, mCornerRadius - mBorderHalf, mCornerRadius -
mBorderHalf, Path.Direction.CW);
mPathCornersBorder.close();
}
}
In layout will be like this:
<com.example.yourpackage.RoundedLayout
android:id="#+id/maplayout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="20dp">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="200dp"
tools:context="com.example.yourpackage.MapsMarkerActivity"/>
</com.example.yourpackage.RoundedLayout>
In code can be like this for a round shape with border:
RoundedLayout rl = (RoundedLayout) findViewById(R.id.maplayout);
rl.setShapeCircle(true);
rl.showBorder(true);
rl.setBorderWidth(2);
This layout can be used to shape any view.
It's incredible how google is incapable of making competent (usable) complete demos for it's android API.
For other people looking into this, I just tackled this using GoogleMap.snapshot and manipulating the bitmap result with this stack over flow answer:
How to make an ImageView with rounded corners?
Mind you this is only valid if you are going to have a static map that is not going to be interacted with.
Make sure you take the snap shot after the map is loaded.
I updated the image view helper code to draw with path to support rounding only some corners. ie. If you want to round only 2 of the corners.
You just need the path round rect function that takes a float[]
I show a progress bar until I get a callback from GoogleMap loaded listener than I take the snapshot.
If you take your snapshot too early you will get can't create bitmap with 0 width and height error.
Hope this helps someone looking for rounded corners or other weird shape in static map snapshot.
If you are only trying to target API 21 (Lollipop) and higher
This is the easiest way possible.
parentView.setClipToOutline(true);
Result
Following #Nouman_Hanif post I ended up with a solution that looks quite good.
map_rounded_corner_overlay.xml:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="1dp"
android:color="#color/white" />
<corners android:radius="<your_desired_view_corner_radius>"/>
</shape>
My map xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_margin="1dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/map_rounded_corner_overlay" />
</RelativeLayout>