mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 21:33:59 +00:00
[android] Add current speed widget
Signed-off-by: TobiPeterG <codeberg.sadness522@passmail.net>
This commit is contained in:
@@ -26,6 +26,7 @@ import app.organicmaps.sdk.util.StringUtils;
|
|||||||
import app.organicmaps.util.UiUtils;
|
import app.organicmaps.util.UiUtils;
|
||||||
import app.organicmaps.util.Utils;
|
import app.organicmaps.util.Utils;
|
||||||
import app.organicmaps.util.WindowInsetUtils;
|
import app.organicmaps.util.WindowInsetUtils;
|
||||||
|
import app.organicmaps.widget.CurrentSpeedView;
|
||||||
import app.organicmaps.widget.LanesView;
|
import app.organicmaps.widget.LanesView;
|
||||||
import app.organicmaps.widget.SpeedLimitView;
|
import app.organicmaps.widget.SpeedLimitView;
|
||||||
import app.organicmaps.widget.menu.NavMenu;
|
import app.organicmaps.widget.menu.NavMenu;
|
||||||
@@ -51,6 +52,8 @@ public class NavigationController implements TrafficManager.TrafficCallback, Nav
|
|||||||
private final LanesView mLanesView;
|
private final LanesView mLanesView;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final SpeedLimitView mSpeedLimit;
|
private final SpeedLimitView mSpeedLimit;
|
||||||
|
@NonNull
|
||||||
|
private final CurrentSpeedView mCurrentSpeed;
|
||||||
|
|
||||||
private final MapButtonsViewModel mMapButtonsViewModel;
|
private final MapButtonsViewModel mMapButtonsViewModel;
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ public class NavigationController implements TrafficManager.TrafficCallback, Nav
|
|||||||
mLanesView = topFrame.findViewById(R.id.lanes);
|
mLanesView = topFrame.findViewById(R.id.lanes);
|
||||||
|
|
||||||
mSpeedLimit = topFrame.findViewById(R.id.nav_speed_limit);
|
mSpeedLimit = topFrame.findViewById(R.id.nav_speed_limit);
|
||||||
|
mCurrentSpeed = topFrame.findViewById(R.id.nav_current_speed);
|
||||||
|
|
||||||
// Show a blank view below the navbar to hide the menu content
|
// Show a blank view below the navbar to hide the menu content
|
||||||
final View navigationBarBackground = mFrame.findViewById(R.id.nav_bottom_sheet_nav_bar);
|
final View navigationBarBackground = mFrame.findViewById(R.id.nav_bottom_sheet_nav_bar);
|
||||||
@@ -128,7 +132,7 @@ public class NavigationController implements TrafficManager.TrafficCallback, Nav
|
|||||||
|
|
||||||
mLanesView.setLanes(info.lanes);
|
mLanesView.setLanes(info.lanes);
|
||||||
|
|
||||||
updateSpeedLimit(info);
|
updateSpeedWidgets(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePedestrian(@NonNull RoutingInfo info)
|
private void updatePedestrian(@NonNull RoutingInfo info)
|
||||||
@@ -136,6 +140,7 @@ public class NavigationController implements TrafficManager.TrafficCallback, Nav
|
|||||||
mNextTurnDistance.setText(Utils.formatDistance(mFrame.getContext(), info.distToTurn));
|
mNextTurnDistance.setText(Utils.formatDistance(mFrame.getContext(), info.distToTurn));
|
||||||
|
|
||||||
info.pedestrianTurnDirection.setTurnDrawable(mNextTurnImage);
|
info.pedestrianTurnDirection.setTurnDrawable(mNextTurnImage);
|
||||||
|
updateSpeedWidgets(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateNorth()
|
public void updateNorth()
|
||||||
@@ -261,16 +266,18 @@ public class NavigationController implements TrafficManager.TrafficCallback, Nav
|
|||||||
RoutingController.get().cancel();
|
RoutingController.get().cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpeedLimit(@NonNull final RoutingInfo info)
|
private void updateSpeedWidgets(@NonNull final RoutingInfo info)
|
||||||
{
|
{
|
||||||
final Location location = MwmApplication.from(mFrame.getContext()).getLocationHelper().getSavedLocation();
|
final Location location = MwmApplication.from(mFrame.getContext()).getLocationHelper().getSavedLocation();
|
||||||
if (location == null)
|
if (location == null)
|
||||||
{
|
{
|
||||||
mSpeedLimit.setSpeedLimit(0, false);
|
mSpeedLimit.setSpeedLimit(-1, false);
|
||||||
|
mCurrentSpeed.setCurrentSpeed(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int fSpeedLimit = StringUtils.nativeFormatSpeed(info.speedLimitMps);
|
final int fSpeedLimit = StringUtils.nativeFormatSpeed(info.speedLimitMps);
|
||||||
final boolean speedLimitExceeded = fSpeedLimit < StringUtils.nativeFormatSpeed(location.getSpeed());
|
final boolean speedLimitExceeded = fSpeedLimit < StringUtils.nativeFormatSpeed(location.getSpeed());
|
||||||
mSpeedLimit.setSpeedLimit(fSpeedLimit, speedLimitExceeded);
|
mSpeedLimit.setSpeedLimit(fSpeedLimit, speedLimitExceeded);
|
||||||
|
mCurrentSpeed.setCurrentSpeed(location.getSpeed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package app.organicmaps.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public abstract class BaseSignView extends View
|
||||||
|
{
|
||||||
|
private float mBorderWidthRatio = 0.1f;
|
||||||
|
protected void setBorderWidthRatio(float ratio) {
|
||||||
|
mBorderWidthRatio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float mBorderInsetRatio = 0f;
|
||||||
|
protected void setBorderInsetRatio(float ratio) {
|
||||||
|
mBorderInsetRatio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// colors
|
||||||
|
protected int mBackgroundColor;
|
||||||
|
protected int mBorderColor;
|
||||||
|
protected int mAlertColor;
|
||||||
|
protected int mTextColor;
|
||||||
|
protected int mTextAlertColor;
|
||||||
|
|
||||||
|
// paints
|
||||||
|
protected final Paint mBackgroundPaint;
|
||||||
|
protected final Paint mBorderPaint;
|
||||||
|
protected final Paint mTextPaint;
|
||||||
|
|
||||||
|
// geometry
|
||||||
|
protected float mWidth;
|
||||||
|
protected float mHeight;
|
||||||
|
protected float mRadius;
|
||||||
|
protected float mBorderWidth;
|
||||||
|
protected float mBorderRadius;
|
||||||
|
|
||||||
|
public BaseSignView(Context ctx, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(ctx, attrs);
|
||||||
|
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setColors(int backgroundColor,
|
||||||
|
int borderColor,
|
||||||
|
int alertColor,
|
||||||
|
int textColor,
|
||||||
|
int textAlertColor)
|
||||||
|
{
|
||||||
|
mBackgroundColor = backgroundColor;
|
||||||
|
mBorderColor = borderColor;
|
||||||
|
mAlertColor = alertColor;
|
||||||
|
mTextColor = textColor;
|
||||||
|
mTextAlertColor = textAlertColor;
|
||||||
|
|
||||||
|
mBackgroundPaint.setColor(mBackgroundColor);
|
||||||
|
mBorderPaint.setColor(mBorderColor);
|
||||||
|
mTextPaint.setColor(mTextColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
float px = getPaddingLeft() + getPaddingRight();
|
||||||
|
float py = getPaddingTop() + getPaddingBottom();
|
||||||
|
mWidth = w - px;
|
||||||
|
mHeight = h - py;
|
||||||
|
mRadius = Math.min(mWidth, mHeight) / 2f;
|
||||||
|
mBorderWidth = mRadius * mBorderWidthRatio;
|
||||||
|
// subtract half the stroke PLUS the extra inset
|
||||||
|
float gap = mRadius * mBorderInsetRatio;
|
||||||
|
mBorderRadius = mRadius - (mBorderWidth / 2f) - gap;
|
||||||
|
configureTextSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(@NonNull Canvas canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas);
|
||||||
|
String str = getValueString();
|
||||||
|
if (str == null) return;
|
||||||
|
|
||||||
|
float cx = mWidth / 2f;
|
||||||
|
float cy = mHeight / 2f;
|
||||||
|
|
||||||
|
// background & border
|
||||||
|
boolean alert = isAlert();
|
||||||
|
mBackgroundPaint.setColor(alert ? mAlertColor : mBackgroundColor);
|
||||||
|
canvas.drawCircle(cx, cy, mRadius, mBackgroundPaint);
|
||||||
|
if (!alert)
|
||||||
|
{
|
||||||
|
mBorderPaint.setStrokeWidth(mBorderWidth);
|
||||||
|
mBorderPaint.setColor(mBorderColor);
|
||||||
|
canvas.drawCircle(cx, cy, mBorderRadius, mBorderPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// text
|
||||||
|
mTextPaint.setColor(alert ? mTextAlertColor : mTextColor);
|
||||||
|
drawValueString(canvas, cx, cy, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(@NonNull MotionEvent e)
|
||||||
|
{
|
||||||
|
float cx = mWidth / 2f, cy = mHeight / 2f;
|
||||||
|
float dx = e.getX() - cx, dy = e.getY() - cy;
|
||||||
|
if (dx*dx + dy*dy <= mRadius*mRadius)
|
||||||
|
{
|
||||||
|
performClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performClick()
|
||||||
|
{
|
||||||
|
super.performClick();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawValueString(Canvas c, float cx, float cy, String str)
|
||||||
|
{
|
||||||
|
Rect b = new Rect();
|
||||||
|
mTextPaint.getTextBounds(str, 0, str.length(), b);
|
||||||
|
float y = cy - b.exactCenterY();
|
||||||
|
c.drawText(str, cx, y, mTextPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configureTextSize()
|
||||||
|
{
|
||||||
|
String text = getValueString();
|
||||||
|
if (text == null) return;
|
||||||
|
float textRadius = mBorderRadius - mBorderWidth;
|
||||||
|
float maxSz = 2f * textRadius;
|
||||||
|
float maxSz2 = maxSz * maxSz;
|
||||||
|
float lo = 0f, hi = maxSz, sz = maxSz;
|
||||||
|
Rect b = new Rect();
|
||||||
|
while (lo <= hi)
|
||||||
|
{
|
||||||
|
sz = (lo + hi) / 2f;
|
||||||
|
mTextPaint.setTextSize(sz);
|
||||||
|
mTextPaint.getTextBounds(text, 0, text.length(), b);
|
||||||
|
float area = b.width()*b.width() + b.height()*b.height();
|
||||||
|
if (area <= maxSz2) lo = sz + 1f;
|
||||||
|
else hi = sz - 1f;
|
||||||
|
}
|
||||||
|
mTextPaint.setTextSize(Math.max(1f, sz));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** child must return the string to draw, or null if nothing */
|
||||||
|
@Nullable
|
||||||
|
protected abstract String getValueString();
|
||||||
|
|
||||||
|
/** child decides if this is in “alert” state */
|
||||||
|
protected abstract boolean isAlert();
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package app.organicmaps.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.organicmaps.R;
|
||||||
|
import app.organicmaps.sdk.util.StringUtils;
|
||||||
|
|
||||||
|
public class CurrentSpeedView extends BaseSignView
|
||||||
|
{
|
||||||
|
private double mSpeedMps = -1.0;
|
||||||
|
private String mSpeedStr = "--";
|
||||||
|
|
||||||
|
public CurrentSpeedView(Context ctx, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(ctx, attrs);
|
||||||
|
|
||||||
|
setBorderWidthRatio(0.1f);
|
||||||
|
setBorderInsetRatio(0.05f);
|
||||||
|
|
||||||
|
try (TypedArray a = ctx.getTheme()
|
||||||
|
.obtainStyledAttributes(attrs, R.styleable.CurrentSpeedView /* reuse same attrs or define new */ , 0, 0))
|
||||||
|
{
|
||||||
|
int bg = a.getColor(R.styleable.CurrentSpeedView_currentSpeedBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
||||||
|
int bd = a.getColor(R.styleable.CurrentSpeedView_currentSpeedBorderColor, DefaultValues.BORDER_COLOR);
|
||||||
|
int tc = a.getColor(R.styleable.CurrentSpeedView_currentSpeedTextColor, DefaultValues.TEXT_COLOR);
|
||||||
|
setColors(bg, bd, 0, tc, 0);
|
||||||
|
|
||||||
|
if (isInEditMode())
|
||||||
|
{
|
||||||
|
mSpeedMps = a.getInt(R.styleable.CurrentSpeedView_currentSpeedEditModeCurrentSpeed, 50);
|
||||||
|
mSpeedStr = Integer.toString((int)mSpeedMps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentSpeed(double mps)
|
||||||
|
{
|
||||||
|
mSpeedMps = mps;
|
||||||
|
if (mps < 0)
|
||||||
|
{
|
||||||
|
mSpeedStr = "--";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Pair<String,String> su = StringUtils.nativeFormatSpeedAndUnits(mps);
|
||||||
|
mSpeedStr = su.first;
|
||||||
|
}
|
||||||
|
requestLayout();
|
||||||
|
configureTextSize();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected String getValueString()
|
||||||
|
{
|
||||||
|
return mSpeedStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isAlert()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface DefaultValues
|
||||||
|
{
|
||||||
|
int BACKGROUND_COLOR = 0xFFFFFFFF;
|
||||||
|
int BORDER_COLOR = 0xFF000000;
|
||||||
|
int TEXT_COLOR = 0xFF000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,225 +3,135 @@ package app.organicmaps.widget;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import androidx.annotation.ColorInt;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.organicmaps.R;
|
import app.organicmaps.R;
|
||||||
|
|
||||||
public class SpeedLimitView extends View
|
public class SpeedLimitView extends BaseSignView
|
||||||
{
|
{
|
||||||
private interface DefaultValues
|
private int mSpeedLimit = -1;
|
||||||
{
|
|
||||||
@ColorInt
|
|
||||||
int BACKGROUND_COLOR = Color.WHITE;
|
|
||||||
@ColorInt
|
|
||||||
int BORDER_COLOR = Color.RED;
|
|
||||||
@ColorInt
|
|
||||||
int ALERT_COLOR = Color.RED;
|
|
||||||
@ColorInt
|
|
||||||
int TEXT_COLOR = Color.BLACK;
|
|
||||||
@ColorInt
|
|
||||||
int TEXT_ALERT_COLOR = Color.WHITE;
|
|
||||||
|
|
||||||
float BORDER_WIDTH_RATIO = 0.1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private final int mBackgroundColor;
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private final int mBorderColor;
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private final int mAlertColor;
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private final int mTextColor;
|
|
||||||
|
|
||||||
@ColorInt
|
|
||||||
private final int mTextAlertColor;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final Paint mSignBackgroundPaint;
|
|
||||||
@NonNull
|
|
||||||
private final Paint mSignBorderPaint;
|
|
||||||
@NonNull
|
|
||||||
private final Paint mTextPaint;
|
|
||||||
|
|
||||||
private float mWidth;
|
|
||||||
private float mHeight;
|
|
||||||
private float mBackgroundRadius;
|
|
||||||
private float mBorderRadius;
|
|
||||||
private float mBorderWidth;
|
|
||||||
|
|
||||||
private int mSpeedLimit = 0;
|
|
||||||
@NonNull
|
|
||||||
private String mSpeedLimitStr = "0";
|
|
||||||
private boolean mAlert = false;
|
private boolean mAlert = false;
|
||||||
|
private String mSpeedStr = "-1";
|
||||||
|
private final int unlimitedBorderColor;
|
||||||
|
private final int unlimitedStripeColor;
|
||||||
|
|
||||||
public SpeedLimitView(Context context, @Nullable AttributeSet attrs)
|
public SpeedLimitView(Context ctx, @Nullable AttributeSet attrs)
|
||||||
{
|
{
|
||||||
super(context, attrs);
|
super(ctx, attrs);
|
||||||
|
|
||||||
try (TypedArray data = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SpeedLimitView, 0, 0))
|
setBorderWidthRatio(0.2f);
|
||||||
|
setBorderInsetRatio(0.05f);
|
||||||
|
|
||||||
|
try (TypedArray a = ctx.getTheme()
|
||||||
|
.obtainStyledAttributes(attrs, R.styleable.SpeedLimitView, 0, 0))
|
||||||
{
|
{
|
||||||
mBackgroundColor =
|
int bg = a.getColor(R.styleable.SpeedLimitView_speedLimitBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
||||||
data.getColor(R.styleable.SpeedLimitView_speedLimitBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
int bd = a.getColor(R.styleable.SpeedLimitView_speedLimitBorderColor, DefaultValues.BORDER_COLOR);
|
||||||
mBorderColor = data.getColor(R.styleable.SpeedLimitView_speedLimitBorderColor, DefaultValues.BORDER_COLOR);
|
int al = a.getColor(R.styleable.SpeedLimitView_speedLimitAlertColor, DefaultValues.ALERT_COLOR);
|
||||||
mAlertColor = data.getColor(R.styleable.SpeedLimitView_speedLimitAlertColor, DefaultValues.ALERT_COLOR);
|
int tc = a.getColor(R.styleable.SpeedLimitView_speedLimitTextColor, DefaultValues.TEXT_COLOR);
|
||||||
mTextColor = data.getColor(R.styleable.SpeedLimitView_speedLimitTextColor, DefaultValues.TEXT_COLOR);
|
int tac = a.getColor(R.styleable.SpeedLimitView_speedLimitTextAlertColor, DefaultValues.TEXT_ALERT_COLOR);
|
||||||
mTextAlertColor =
|
setColors(bg, bd, al, tc, tac);
|
||||||
data.getColor(R.styleable.SpeedLimitView_speedLimitTextAlertColor, DefaultValues.TEXT_ALERT_COLOR);
|
|
||||||
|
unlimitedBorderColor = a.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedBorderColor, DefaultValues.UNLIMITED_BORDER_COLOR);
|
||||||
|
unlimitedStripeColor = a.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedStripeColor, DefaultValues.UNLIMITED_STRIPE_COLOR);
|
||||||
|
|
||||||
if (isInEditMode())
|
if (isInEditMode())
|
||||||
{
|
{
|
||||||
mSpeedLimit = data.getInt(R.styleable.SpeedLimitView_speedLimitEditModeSpeedLimit, 60);
|
mSpeedLimit = a.getInt(R.styleable.SpeedLimitView_speedLimitEditModeSpeedLimit, 60);
|
||||||
mSpeedLimitStr = Integer.toString(mSpeedLimit);
|
mAlert = a.getBoolean(R.styleable.SpeedLimitView_speedLimitEditModeAlert, false);
|
||||||
mAlert = data.getBoolean(R.styleable.SpeedLimitView_speedLimitEditModeAlert, false);
|
mSpeedStr = Integer.toString(mSpeedLimit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mSignBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
public void setSpeedLimit(int limit, boolean alert)
|
||||||
mSignBackgroundPaint.setColor(mBackgroundColor);
|
|
||||||
|
|
||||||
mSignBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
mSignBorderPaint.setColor(mBorderColor);
|
|
||||||
mSignBorderPaint.setStrokeWidth(mBorderWidth);
|
|
||||||
mSignBorderPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
|
|
||||||
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
mTextPaint.setColor(mTextColor);
|
|
||||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSpeedLimit(final int speedLimit, boolean alert)
|
|
||||||
{
|
{
|
||||||
final boolean speedLimitChanged = mSpeedLimit != speedLimit;
|
if (mSpeedLimit != limit)
|
||||||
|
{
|
||||||
mSpeedLimit = speedLimit;
|
mSpeedLimit = limit;
|
||||||
|
mSpeedStr = Integer.toString(limit);
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
mAlert = alert;
|
mAlert = alert;
|
||||||
|
|
||||||
if (speedLimitChanged)
|
|
||||||
{
|
|
||||||
mSpeedLimitStr = Integer.toString(mSpeedLimit);
|
|
||||||
configureTextSize();
|
configureTextSize();
|
||||||
}
|
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(@NonNull Canvas canvas)
|
protected String getValueString()
|
||||||
{
|
{
|
||||||
|
return (mSpeedLimit > 0 ? mSpeedStr : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isAlert()
|
||||||
|
{
|
||||||
|
return mAlert;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
float cx = mWidth/2f, cy = mHeight/2f;
|
||||||
|
|
||||||
|
if (mSpeedLimit == 0)
|
||||||
|
{
|
||||||
|
// background
|
||||||
|
mBackgroundPaint.setColor(mBackgroundColor);
|
||||||
|
canvas.drawCircle(cx, cy, mRadius, mBackgroundPaint);
|
||||||
|
|
||||||
|
// black border
|
||||||
|
mBorderPaint.setColor(unlimitedBorderColor);
|
||||||
|
mBorderPaint.setStrokeWidth(mBorderWidth);
|
||||||
|
canvas.drawCircle(cx, cy, mBorderRadius, mBorderPaint);
|
||||||
|
|
||||||
|
// draw 5 diagonal stripes
|
||||||
|
drawUnlimitedStripes(canvas, cx, cy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// delegate to BaseSignView’s onDraw
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
final boolean validSpeedLimit = mSpeedLimit > 0;
|
|
||||||
if (!validSpeedLimit)
|
|
||||||
return;
|
|
||||||
|
|
||||||
final float cx = mWidth / 2;
|
|
||||||
final float cy = mHeight / 2;
|
|
||||||
|
|
||||||
drawSign(canvas, cx, cy, mAlert);
|
|
||||||
drawText(canvas, cx, cy, mAlert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawSign(@NonNull Canvas canvas, float cx, float cy, boolean alert)
|
private void drawUnlimitedStripes(Canvas c, float cx, float cy)
|
||||||
{
|
{
|
||||||
if (alert)
|
Paint stripe = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
mSignBackgroundPaint.setColor(mAlertColor);
|
stripe.setColor(unlimitedStripeColor);
|
||||||
else
|
stripe.setStrokeWidth(mBorderWidth * 0.4f);
|
||||||
mSignBackgroundPaint.setColor(mBackgroundColor);
|
|
||||||
|
|
||||||
canvas.drawCircle(cx, cy, mBackgroundRadius, mSignBackgroundPaint);
|
float r = mRadius * 0.8f; // shorten to 80% of full radius
|
||||||
if (!alert)
|
float diag = (float)(1/Math.sqrt(2));
|
||||||
|
float dx = -diag, dy = +diag;
|
||||||
|
float px = -dy, py = +dx; // perpendicular
|
||||||
|
float step = r * 0.15f; // spacing
|
||||||
|
|
||||||
|
for (int i = -2; i <= 2; i++)
|
||||||
{
|
{
|
||||||
mSignBorderPaint.setStrokeWidth(mBorderWidth);
|
float ox = px * step * i;
|
||||||
canvas.drawCircle(cx, cy, mBorderRadius, mSignBorderPaint);
|
float oy = py * step * i;
|
||||||
|
float sx = cx + dx * r + ox;
|
||||||
|
float sy = cy + dy * r + oy;
|
||||||
|
float ex = cx - dx * r + ox;
|
||||||
|
float ey = cy - dy * r + oy;
|
||||||
|
c.drawLine(sx, sy, ex, ey, stripe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawText(@NonNull Canvas canvas, float cx, float cy, boolean alert)
|
|
||||||
|
private interface DefaultValues
|
||||||
{
|
{
|
||||||
if (alert)
|
int BACKGROUND_COLOR = 0xFFFFFFFF;
|
||||||
mTextPaint.setColor(mTextAlertColor);
|
int BORDER_COLOR = 0xFFFF0000;
|
||||||
else
|
int ALERT_COLOR = 0xFFFF0000;
|
||||||
mTextPaint.setColor(mTextColor);
|
int TEXT_COLOR = 0xFF000000;
|
||||||
|
int TEXT_ALERT_COLOR = 0xFFFFFFFF;
|
||||||
final Rect textBounds = new Rect();
|
int UNLIMITED_BORDER_COLOR = 0xFF000000;
|
||||||
mTextPaint.getTextBounds(mSpeedLimitStr, 0, mSpeedLimitStr.length(), textBounds);
|
int UNLIMITED_STRIPE_COLOR = 0xFF000000;
|
||||||
final float textY = cy - textBounds.exactCenterY();
|
|
||||||
canvas.drawText(mSpeedLimitStr, cx, textY, mTextPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(@NonNull MotionEvent event)
|
|
||||||
{
|
|
||||||
final float cx = mWidth / 2;
|
|
||||||
final float cy = mHeight / 2;
|
|
||||||
if (Math.pow(event.getX() - cx, 2) + Math.pow(event.getY() - cy, 2) <= Math.pow(mBackgroundRadius, 2))
|
|
||||||
{
|
|
||||||
performClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performClick()
|
|
||||||
{
|
|
||||||
super.performClick();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
|
||||||
{
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
|
||||||
|
|
||||||
final float paddingX = (float) (getPaddingLeft() + getPaddingRight());
|
|
||||||
final float paddingY = (float) (getPaddingTop() + getPaddingBottom());
|
|
||||||
|
|
||||||
mWidth = (float) w - paddingX;
|
|
||||||
mHeight = (float) h - paddingY;
|
|
||||||
mBackgroundRadius = Math.min(mWidth, mHeight) / 2;
|
|
||||||
mBorderWidth = mBackgroundRadius * 2 * DefaultValues.BORDER_WIDTH_RATIO;
|
|
||||||
mBorderRadius = mBackgroundRadius - mBorderWidth / 2;
|
|
||||||
configureTextSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply binary search to determine the optimal text size that fits within the circular boundary.
|
|
||||||
private void configureTextSize()
|
|
||||||
{
|
|
||||||
final String text = mSpeedLimitStr;
|
|
||||||
final float textRadius = mBorderRadius - mBorderWidth;
|
|
||||||
final float textMaxSize = 2 * textRadius;
|
|
||||||
final float textMaxSizeSquared = (float) Math.pow(textMaxSize, 2);
|
|
||||||
|
|
||||||
float lowerBound = 0;
|
|
||||||
float upperBound = textMaxSize;
|
|
||||||
float textSize = textMaxSize;
|
|
||||||
final Rect textBounds = new Rect();
|
|
||||||
|
|
||||||
while (lowerBound <= upperBound)
|
|
||||||
{
|
|
||||||
textSize = (lowerBound + upperBound) / 2;
|
|
||||||
mTextPaint.setTextSize(textSize);
|
|
||||||
mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
|
|
||||||
|
|
||||||
if (Math.pow(textBounds.width(), 2) + Math.pow(textBounds.height(), 2) <= textMaxSizeSquared)
|
|
||||||
lowerBound = textSize + 1;
|
|
||||||
else
|
|
||||||
upperBound = textSize - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextPaint.setTextSize(Math.max(1, textSize));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,12 +123,24 @@
|
|||||||
app:lanesEditModeLanesCount="10"
|
app:lanesEditModeLanesCount="10"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<app.organicmaps.widget.SpeedLimitView
|
<app.organicmaps.widget.CurrentSpeedView
|
||||||
android:id="@+id/nav_speed_limit"
|
android:id="@+id/nav_current_speed"
|
||||||
style="@style/MwmWidget.SpeedLimit"
|
style="@style/MwmWidget.CurrentSpeed"
|
||||||
android:layout_width="60dp"
|
android:layout_width="60dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_margin="@dimen/margin_half"
|
android:layout_margin="@dimen/margin_half"
|
||||||
app:layout_constraintStart_toEndOf="@id/nav_next_turn_container"
|
app:layout_constraintStart_toEndOf="@id/nav_next_turn_container"
|
||||||
app:layout_constraintTop_toBottomOf="@id/street_frame"/>
|
app:layout_constraintTop_toBottomOf="@id/street_frame"/>
|
||||||
|
|
||||||
|
<app.organicmaps.widget.SpeedLimitView
|
||||||
|
android:id="@+id/nav_speed_limit"
|
||||||
|
style="@style/MwmWidget.SpeedLimit"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_margin="@dimen/margin_half"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/nav_next_turn_container"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/nav_current_speed"
|
||||||
|
android:translationY="-21dp"
|
||||||
|
android:translationX="-12dp"
|
||||||
|
android:translationZ="1dp"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -124,13 +124,26 @@
|
|||||||
app:lanesEditModeLanesCount="5"
|
app:lanesEditModeLanesCount="5"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<app.organicmaps.widget.SpeedLimitView
|
<app.organicmaps.widget.CurrentSpeedView
|
||||||
android:id="@+id/nav_speed_limit"
|
android:id="@+id/nav_current_speed"
|
||||||
style="@style/MwmWidget.SpeedLimit"
|
style="@style/MwmWidget.CurrentSpeed"
|
||||||
android:layout_width="60dp"
|
android:layout_width="60dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_margin="@dimen/margin_half"
|
android:layout_margin="@dimen/margin_half"
|
||||||
app:layout_constraintEnd_toEndOf="@id/nav_next_turn_container"
|
app:layout_constraintEnd_toEndOf="@id/nav_next_turn_container"
|
||||||
app:layout_constraintStart_toStartOf="@id/nav_next_turn_container"
|
app:layout_constraintStart_toStartOf="@id/nav_next_turn_container"
|
||||||
app:layout_constraintTop_toBottomOf="@id/nav_next_turn_container"/>
|
app:layout_constraintTop_toBottomOf="@id/nav_next_turn_container"/>
|
||||||
|
|
||||||
|
<app.organicmaps.widget.SpeedLimitView
|
||||||
|
android:id="@+id/nav_speed_limit"
|
||||||
|
style="@style/MwmWidget.SpeedLimit"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_margin="@dimen/margin_half"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/nav_next_turn_container"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/nav_next_turn_container"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/nav_current_speed"
|
||||||
|
android:translationY="-23dp"
|
||||||
|
android:translationX="-23dp"
|
||||||
|
android:translationZ="1dp"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
<declare-styleable name="SpeedLimitView">
|
<declare-styleable name="SpeedLimitView">
|
||||||
<attr name="speedLimitBackgroundColor" format="color" />
|
<attr name="speedLimitBackgroundColor" format="color" />
|
||||||
<attr name="speedLimitBorderColor" format="color" />
|
<attr name="speedLimitBorderColor" format="color" />
|
||||||
|
<attr name="speedLimitUnlimitedBorderColor" format="color" />
|
||||||
|
<attr name="speedLimitUnlimitedStripeColor" format="color" />
|
||||||
<attr name="speedLimitAlertColor" format="color" />
|
<attr name="speedLimitAlertColor" format="color" />
|
||||||
<attr name="speedLimitTextColor" format="color" />
|
<attr name="speedLimitTextColor" format="color" />
|
||||||
<attr name="speedLimitTextAlertColor" format="color" />
|
<attr name="speedLimitTextAlertColor" format="color" />
|
||||||
@@ -11,6 +13,14 @@
|
|||||||
<attr name="speedLimitEditModeAlert" format="boolean" />
|
<attr name="speedLimitEditModeAlert" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="CurrentSpeedView">
|
||||||
|
<attr name="currentSpeedBackgroundColor" format="color" />
|
||||||
|
<attr name="currentSpeedBorderColor" format="color" />
|
||||||
|
<attr name="currentSpeedTextColor" format="color" />
|
||||||
|
<!-- These values are used only in edit mode -->
|
||||||
|
<attr name="currentSpeedEditModeCurrentSpeed" format="integer" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="LanesView">
|
<declare-styleable name="LanesView">
|
||||||
<attr name="lanesActiveLaneTintColor" format="reference" />
|
<attr name="lanesActiveLaneTintColor" format="reference" />
|
||||||
<attr name="lanesInactiveLaneTintColor" format="color" />
|
<attr name="lanesInactiveLaneTintColor" format="color" />
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<item name="speedLimitBorderColor">@color/base_red</item>
|
<item name="speedLimitBorderColor">@color/base_red</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="MwmWidget.CurrentSpeed">
|
||||||
|
<item name="speedLimitBorderColor">@color/black_primary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="MwmWidget.FrameLayout"/>
|
<style name="MwmWidget.FrameLayout"/>
|
||||||
|
|
||||||
<style name="MwmWidget.Downloader"/>
|
<style name="MwmWidget.Downloader"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user