generated from Grigo/AndroidTemplate
Created ship vectors (not added yet)
Created menu Created udp support Created DockWidgets for compass and SOG/COG
This commit is contained in:
@@ -53,6 +53,12 @@ public abstract class BaseDockWidget extends FrameLayout {
|
||||
}
|
||||
protected OnDockResizeListener dockResizeListener;
|
||||
|
||||
// Интерфейс для уведомления об изменении состояния docked
|
||||
public interface OnDockStateChangeListener {
|
||||
void onDockStateChanged(boolean isDocked, boolean isTop);
|
||||
}
|
||||
protected OnDockStateChangeListener dockStateChangeListener;
|
||||
|
||||
public BaseDockWidget(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
@@ -246,12 +252,23 @@ public abstract class BaseDockWidget extends FrameLayout {
|
||||
dockHeightPx = newHeight;
|
||||
setLayoutParams(lp);
|
||||
|
||||
// Если закреплен снизу, нужно также изменить позицию Y
|
||||
if (!dockTop) {
|
||||
// Корректируем позицию Y в зависимости от позиции закрепления
|
||||
if (dockTop) {
|
||||
// Если закреплен сверху, позиция Y всегда должна быть 0
|
||||
setY(0);
|
||||
} else {
|
||||
// Если закреплен снизу, позиция Y должна быть (parentHeight - newHeight)
|
||||
float newY = ((ViewGroup) getParent()).getHeight() - newHeight;
|
||||
setY(newY);
|
||||
}
|
||||
|
||||
// Перепозиционируем все docked виджеты после изменения размера
|
||||
ViewGroup parent = (ViewGroup) getParent();
|
||||
if (parent != null) {
|
||||
repositionAllDockedWidgets(parent);
|
||||
}
|
||||
|
||||
// Уведомляем об изменении размера
|
||||
if (dockResizeListener != null) {
|
||||
dockResizeListener.onDockResize(newHeight);
|
||||
}
|
||||
@@ -362,9 +379,13 @@ public abstract class BaseDockWidget extends FrameLayout {
|
||||
float endX = targetX;
|
||||
float endY = targetY;
|
||||
|
||||
// Если доким в нижнюю часть, корректируем позицию Y
|
||||
if (docked && !top) {
|
||||
endY = parentHeight - dockHeight;
|
||||
// Если доким, вычисляем правильную позицию с учетом других docked виджетов
|
||||
if (docked) {
|
||||
endX = 0;
|
||||
endY = calculateDockPosition(top);
|
||||
} else {
|
||||
// При переходе в movable режим сбрасываем размер до дефолтного
|
||||
dockHeightPx = 0;
|
||||
}
|
||||
|
||||
// Сохраняем финальные значения для использования в lambda и inner class
|
||||
@@ -414,6 +435,11 @@ public abstract class BaseDockWidget extends FrameLayout {
|
||||
morphAnimator.start();
|
||||
this.isDocked = docked;
|
||||
isMorphing = true;
|
||||
|
||||
// Уведомляем об изменении состояния docked
|
||||
if (dockStateChangeListener != null) {
|
||||
dockStateChangeListener.onDockStateChanged(docked, top);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDocked() {
|
||||
@@ -432,10 +458,101 @@ public abstract class BaseDockWidget extends FrameLayout {
|
||||
this.dockResizeListener = listener;
|
||||
}
|
||||
|
||||
public void setOnDockStateChangeListener(OnDockStateChangeListener listener) {
|
||||
this.dockStateChangeListener = listener;
|
||||
}
|
||||
|
||||
protected float dp(float dp) {
|
||||
return dp * getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет правильную позицию для докинга с учетом других docked виджетов
|
||||
*/
|
||||
protected float calculateDockPosition(boolean dockTop) {
|
||||
ViewGroup parent = (ViewGroup) getParent();
|
||||
if (parent == null) return 0;
|
||||
|
||||
int dockHeight = (int) dp(DEFAULT_DOCK_HEIGHT_DP);
|
||||
float y = 0;
|
||||
|
||||
if (dockTop) {
|
||||
// Доким сверху - начинаем с позиции 0
|
||||
y = 0;
|
||||
|
||||
// Проверяем другие виджеты сверху
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
if (child != this && child instanceof BaseDockWidget) {
|
||||
BaseDockWidget otherWidget = (BaseDockWidget) child;
|
||||
if (otherWidget.isDocked() && otherWidget.isDockTop()) {
|
||||
// Если другой виджет уже docked сверху, ставим наш под ним
|
||||
y = otherWidget.getY() + otherWidget.getHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Доким снизу - начинаем с нижней позиции
|
||||
y = parent.getHeight() - dockHeight;
|
||||
|
||||
// Проверяем другие виджеты снизу
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
if (child != this && child instanceof BaseDockWidget) {
|
||||
BaseDockWidget otherWidget = (BaseDockWidget) child;
|
||||
if (otherWidget.isDocked() && !otherWidget.isDockTop()) {
|
||||
// Если другой виджет уже docked снизу, ставим наш над ним
|
||||
y = otherWidget.getY() - dockHeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Перепозиционирует все docked виджеты, чтобы они прижались к краям
|
||||
*/
|
||||
public static void repositionAllDockedWidgets(ViewGroup parent) {
|
||||
if (parent == null) return;
|
||||
|
||||
// Собираем все docked виджеты сверху
|
||||
java.util.List<BaseDockWidget> topWidgets = new java.util.ArrayList<>();
|
||||
java.util.List<BaseDockWidget> bottomWidgets = new java.util.ArrayList<>();
|
||||
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
if (child instanceof BaseDockWidget) {
|
||||
BaseDockWidget widget = (BaseDockWidget) child;
|
||||
if (widget.isDocked()) {
|
||||
if (widget.isDockTop()) {
|
||||
topWidgets.add(widget);
|
||||
} else {
|
||||
bottomWidgets.add(widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Перепозиционируем виджеты сверху
|
||||
float currentY = 0;
|
||||
for (BaseDockWidget widget : topWidgets) {
|
||||
widget.setY(currentY);
|
||||
currentY += widget.getHeight();
|
||||
}
|
||||
|
||||
// Перепозиционируем виджеты снизу
|
||||
currentY = parent.getHeight();
|
||||
for (int i = bottomWidgets.size() - 1; i >= 0; i--) {
|
||||
BaseDockWidget widget = bottomWidgets.get(i);
|
||||
currentY -= widget.getHeight();
|
||||
widget.setY(currentY);
|
||||
}
|
||||
}
|
||||
|
||||
// Абстрактные методы для переопределения в наследниках
|
||||
protected abstract void onDrawDock(Canvas canvas);
|
||||
protected abstract void onDrawCircle(Canvas canvas);
|
||||
|
||||
@@ -80,7 +80,7 @@ public class CompassView extends BaseDockWidget {
|
||||
// Прямая шкала (dock-режим)
|
||||
@Override
|
||||
protected void onDrawDock(Canvas canvas) {
|
||||
Log.d(TAG, "onDrawDock called, width=" + getWidth() + ", height=" + getHeight());
|
||||
// Log.d(TAG, "onDrawDock called, width=" + getWidth() + ", height=" + getHeight());
|
||||
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
@@ -192,13 +192,13 @@ public class CompassView extends BaseDockWidget {
|
||||
// Круглый компас (draggable-режим)
|
||||
@Override
|
||||
protected void onDrawCircle(Canvas canvas) {
|
||||
Log.d(TAG, "onDrawCircle called, width=" + getWidth() + ", height=" + getHeight());
|
||||
//Log.d(TAG, "onDrawCircle called, width=" + getWidth() + ", height=" + getHeight());
|
||||
|
||||
float w = getWidth();
|
||||
float h = getHeight();
|
||||
|
||||
if (w <= 0 || h <= 0) {
|
||||
Log.w(TAG, "Invalid dimensions: width=" + w + ", height=" + h);
|
||||
// Log.w(TAG, "Invalid dimensions: width=" + w + ", height=" + h);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.grigowashere.aismap.view;
|
||||
|
||||
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.util.Log;
|
||||
import com.grigowashere.aismap.models.Vessel;
|
||||
|
||||
public class CoordinatesDockWidget extends BaseDockWidget {
|
||||
private static final String TAG = "CoordinatesDockWidget";
|
||||
|
||||
// Цвета
|
||||
private static final int BACKGROUND_COLOR = 0xE6000000; // Полупрозрачный черный
|
||||
private static final int TEXT_COLOR = 0xFFFFFFFF; // Белый
|
||||
private static final int ACCENT_COLOR = 0xFF4CAF50; // Зеленый
|
||||
private static final int WARNING_COLOR = 0xFFFF9800; // Оранжевый
|
||||
private static final int ERROR_COLOR = 0xFFF44336; // Красный
|
||||
|
||||
// Кисти
|
||||
private Paint backgroundPaint;
|
||||
private Paint textPaint;
|
||||
private Paint accentPaint;
|
||||
private Paint warningPaint;
|
||||
private Paint errorPaint;
|
||||
|
||||
// Данные для отображения
|
||||
private Vessel vessel;
|
||||
private String coordinatesText = "Координаты: --";
|
||||
private String sogText = "SOG: --";
|
||||
private String cogText = "COG: --";
|
||||
|
||||
public CoordinatesDockWidget(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CoordinatesDockWidget(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Инициализируем кисти
|
||||
backgroundPaint = new Paint();
|
||||
backgroundPaint.setColor(BACKGROUND_COLOR);
|
||||
backgroundPaint.setStyle(Paint.Style.FILL);
|
||||
backgroundPaint.setAntiAlias(true);
|
||||
|
||||
textPaint = new Paint();
|
||||
textPaint.setColor(TEXT_COLOR);
|
||||
textPaint.setTextSize(dp(14));
|
||||
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
textPaint.setAntiAlias(true);
|
||||
|
||||
accentPaint = new Paint();
|
||||
accentPaint.setColor(ACCENT_COLOR);
|
||||
accentPaint.setTextSize(dp(14));
|
||||
accentPaint.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
accentPaint.setAntiAlias(true);
|
||||
|
||||
warningPaint = new Paint();
|
||||
warningPaint.setColor(WARNING_COLOR);
|
||||
warningPaint.setTextSize(dp(14));
|
||||
warningPaint.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
warningPaint.setAntiAlias(true);
|
||||
|
||||
errorPaint = new Paint();
|
||||
errorPaint.setColor(ERROR_COLOR);
|
||||
errorPaint.setTextSize(dp(14));
|
||||
errorPaint.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
errorPaint.setAntiAlias(true);
|
||||
|
||||
// Устанавливаем фон для видимости (как в CompassView)
|
||||
setBackgroundColor(android.graphics.Color.argb(200, 0, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет данные судна
|
||||
*/
|
||||
public void updateVessel(Vessel vessel) {
|
||||
Log.d(TAG, "updateVessel called with vessel: " + (vessel != null ? "not null" : "null"));
|
||||
if (vessel != null) {
|
||||
Log.d(TAG, "Vessel data: lat=" + vessel.getLatitude() +
|
||||
", lon=" + vessel.getLongitude() +
|
||||
", speed=" + vessel.getSpeed() +
|
||||
", course=" + vessel.getCourse());
|
||||
}
|
||||
this.vessel = vessel;
|
||||
updateDisplayText();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет текст для отображения
|
||||
*/
|
||||
private void updateDisplayText() {
|
||||
if (vessel == null) {
|
||||
coordinatesText = "Координаты: --";
|
||||
sogText = "SOG: --";
|
||||
cogText = "COG: --";
|
||||
return;
|
||||
}
|
||||
|
||||
// Координаты
|
||||
if (vessel.getLatitude() != 0 && vessel.getLongitude() != 0) {
|
||||
coordinatesText = String.format("📍 %.6f, %.6f",
|
||||
vessel.getLatitude(), vessel.getLongitude());
|
||||
} else {
|
||||
coordinatesText = "📍 Координаты: --";
|
||||
}
|
||||
|
||||
// SOG (Speed Over Ground)
|
||||
if (vessel.getSpeed() > 0) {
|
||||
sogText = String.format("⚡ SOG: %.1f уз", vessel.getSpeed());
|
||||
} else {
|
||||
sogText = "⚡ SOG: --";
|
||||
}
|
||||
|
||||
// COG (Course Over Ground)
|
||||
if (vessel.getCourse() > 0) {
|
||||
cogText = String.format("🧭 COG: %.1f°", vessel.getCourse());
|
||||
} else {
|
||||
cogText = "🧭 COG: --";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDrawDock(Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
Log.d(TAG, "onDrawDock called, width=" + width + ", height=" + height);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
Log.w(TAG, "Invalid dimensions: width=" + width + ", height=" + height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Рисуем фон
|
||||
canvas.drawRect(0, 0, width, height, backgroundPaint);
|
||||
|
||||
// Вычисляем позиции для текста
|
||||
float textSize = dp(14);
|
||||
float lineHeight = textSize * 1.2f;
|
||||
float startY = (height - (lineHeight * 3)) / 2 + textSize;
|
||||
|
||||
// Определяем цвета в зависимости от качества данных
|
||||
Paint coordinatesPaint = getCoordinatesPaint();
|
||||
Paint sogPaint = getSOGPaint();
|
||||
Paint cogPaint = getCOGPaint();
|
||||
|
||||
// Рисуем тестовый заголовок для проверки видимости
|
||||
Paint testPaint = new Paint();
|
||||
testPaint.setColor(android.graphics.Color.WHITE);
|
||||
testPaint.setTextSize(dp(16));
|
||||
testPaint.setTypeface(android.graphics.Typeface.DEFAULT_BOLD);
|
||||
testPaint.setAntiAlias(true);
|
||||
canvas.drawText("КООРДИНАТЫ", dp(16), dp(20), testPaint);
|
||||
|
||||
// Рисуем текст
|
||||
canvas.drawText(coordinatesText, dp(16), startY, coordinatesPaint);
|
||||
canvas.drawText(sogText, dp(16), startY + lineHeight, sogPaint);
|
||||
canvas.drawText(cogText, dp(16), startY + lineHeight * 2, cogPaint);
|
||||
|
||||
// Рисуем разделительную линию сверху, если закреплен снизу
|
||||
if (!isDockTop()) {
|
||||
Paint linePaint = new Paint();
|
||||
linePaint.setColor(ACCENT_COLOR);
|
||||
linePaint.setStrokeWidth(dp(2));
|
||||
canvas.drawLine(0, 0, width, 0, linePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDrawCircle(Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int centerX = width / 2;
|
||||
int centerY = height / 2;
|
||||
int radius = Math.min(width, height) / 2 - (int)dp(8);
|
||||
|
||||
// Рисуем фон
|
||||
canvas.drawCircle(centerX, centerY, radius, backgroundPaint);
|
||||
|
||||
// Рисуем рамку
|
||||
Paint borderPaint = new Paint();
|
||||
borderPaint.setColor(ACCENT_COLOR);
|
||||
borderPaint.setStyle(Paint.Style.STROKE);
|
||||
borderPaint.setStrokeWidth(dp(3));
|
||||
borderPaint.setAntiAlias(true);
|
||||
canvas.drawCircle(centerX, centerY, radius, borderPaint);
|
||||
|
||||
// Вычисляем позиции для текста в круге
|
||||
float textSize = dp(12);
|
||||
float lineHeight = textSize * 1.3f;
|
||||
float startY = centerY - lineHeight;
|
||||
|
||||
// Определяем цвета
|
||||
Paint coordinatesPaint = getCoordinatesPaint();
|
||||
Paint sogPaint = getSOGPaint();
|
||||
Paint cogPaint = getCOGPaint();
|
||||
|
||||
// Центрируем текст
|
||||
Rect textBounds = new Rect();
|
||||
|
||||
// Координаты
|
||||
coordinatesPaint.getTextBounds(coordinatesText, 0, coordinatesText.length(), textBounds);
|
||||
canvas.drawText(coordinatesText, centerX - textBounds.width() / 2f, startY, coordinatesPaint);
|
||||
|
||||
// SOG
|
||||
sogPaint.getTextBounds(sogText, 0, sogText.length(), textBounds);
|
||||
canvas.drawText(sogText, centerX - textBounds.width() / 2f, startY + lineHeight, sogPaint);
|
||||
|
||||
// COG
|
||||
cogPaint.getTextBounds(cogText, 0, cogText.length(), textBounds);
|
||||
canvas.drawText(cogText, centerX - textBounds.width() / 2f, startY + lineHeight * 2, cogPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет цвет для отображения координат
|
||||
*/
|
||||
private Paint getCoordinatesPaint() {
|
||||
if (vessel == null || vessel.getLatitude() == 0 || vessel.getLongitude() == 0) {
|
||||
return errorPaint;
|
||||
}
|
||||
|
||||
// Проверяем точность GPS
|
||||
if (vessel.getAccuracy() > 0) {
|
||||
if (vessel.getAccuracy() <= 5) {
|
||||
return accentPaint; // Высокая точность - зеленый
|
||||
} else if (vessel.getAccuracy() <= 20) {
|
||||
return warningPaint; // Средняя точность - оранжевый
|
||||
} else {
|
||||
return errorPaint; // Низкая точность - красный
|
||||
}
|
||||
}
|
||||
|
||||
return textPaint; // По умолчанию - белый
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет цвет для отображения SOG
|
||||
*/
|
||||
private Paint getSOGPaint() {
|
||||
if (vessel == null || vessel.getSpeed() <= 0) {
|
||||
return errorPaint;
|
||||
}
|
||||
|
||||
return accentPaint; // Если есть данные о скорости - зеленый
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет цвет для отображения COG
|
||||
*/
|
||||
private Paint getCOGPaint() {
|
||||
if (vessel == null || vessel.getCourse() <= 0) {
|
||||
return errorPaint;
|
||||
}
|
||||
|
||||
return accentPaint; // Если есть данные о курсе - зеленый
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает высоту виджета в dock-режиме
|
||||
*/
|
||||
public int getDockHeight() {
|
||||
if (isDocked()) {
|
||||
return getHeight();
|
||||
}
|
||||
return (int) dp(DEFAULT_DOCK_HEIGHT_DP);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user