仿iOS日历、飞书日历、Google日历的日模式

仿iOS日历、飞书日历、Google日历的日模式,24H内事件可自由上下拖动、自由拉伸。

以下是效果图:

具体实现比较简单,代码如下:



import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

//4、Java to c#
public class MultiRectView extends View {
    private Paint paintRect;
    private Paint borderPaint ;
    private Paint paintLine;
    private Paint paintDragText;
    private List<RectF> rectangles;
    private List<Map> rectanglesMapList;
    private RectF currentRect; // 当前操作的矩形
    private RectF preRect; // 操作的前一个矩形
    private int currentRectIndex = 0;// 当前被选中的矩形
    private float rectWidth = 200f; // 矩形宽度
    private float rectHeight = 100f; // 矩形高度
    private int currentHandle; // 记录当前操作的控制点
    private float lastX, lastY; // 手指最后位置
    private float preX, preY; // 手指起始位置
    private TextPaint textPaint;
    private Paint shadowPaint; // 用于阴影效果

    // 控制点
    private static final int NONE = -1;
    private static final int LEFT_TOP = 0;
    private static final int RIGHT_TOP = 1;
    private static final int LEFT_BOTTOM = 2;
    private static final int RIGHT_BOTTOM = 3;
    private static final int MOVE_RECT = 4;

    // 刻度设置
    private final int SCALE_INTERVAL = 30; // 30分钟
    private final int TOTAL_SCALES = 24 * 4; // 24小时,每小时4个15分钟刻度
    // 设置最小高度
    private final int MIN_RECT_HEIGHT = 100;
    long lineWidth = 6;
    long lineLen = 30;
    private String[] hours;
    private GestureDetector gestureDetector;
    private Context mContext;
    private onclickListener onclickListener;
    private onLongclicklistener onLongclickListener;
    private long longPressStartTime = 0;//长按时间
    private boolean isDragging = false;
    private ScrollView scrollView;
    private int mScaleWidth = 30;//上下间距
    private int mScaleLineX = 140;//横线距离左侧的起始距离点
    private int mScaleLineText = 30;//刻度的起始点
    private int mScaleRectToLeft = 150;//矩形距离左侧间距
    private List<String> texts; // 存储每个矩形内的文本
    private Canvas mCanvas;
    private String topText = "";
    private String bottomText = "";
    int topScale = 0;
    int bottomScale = 0;
    private RectF rectMove;
    private boolean isCancleClickStatus = true;
    private boolean isLongPressStatus = false;//长按状态
    //计时器,计时点击时长
    Timer timer;
    TimerTask timerTask;
    private final int LONGPRESSTIME = 500;//长按超过0.5秒,触发长按事件
    private float cornerRadius = 15f; // 圆角半径
    private List<ActiviesDataModel> activiesDataModelListl;
    private float offsetAmount = 20; // 偏移量,具体根据需求调整

    public MultiRectView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public MultiRectView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public MultiRectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int reHeight = resolveSize(heightSize, heightMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, reHeight);
    }

    public void setScrollView(ScrollView scrollView1) {
        this.scrollView = scrollView1;
    }

    private void init() {
        texts = new ArrayList<>();
        rectangles = new ArrayList<>();
        rectanglesMapList = new ArrayList<>();

        // 初始化填充画笔
        paintRect = new Paint();
        paintRect.setAntiAlias(true);
        paintRect.setStyle(Paint.Style.FILL);
        paintRect.setColor(getResources().getColor(R.color.purple_200));
        paintRect.setStrokeCap(Paint.Cap.ROUND);
        paintRect.setStrokeWidth(3);//控制矩形宽高

        // 初始化边框画笔
        borderPaint = new Paint();
        borderPaint.setAntiAlias(true);
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setColor(getResources().getColor(R.color.purple_300)); // 设置边框颜色为黄色
        borderPaint.setStrokeWidth(3); // 设置边框宽度

        //划线属性
        paintLine = new Paint();
        paintLine.setColor(Color.RED);

        //动态显示时间标签的画笔属性
        paintDragText = new Paint();
        paintDragText.setColor(Color.GREEN);
        paintDragText.setTextSize(30);

        //左侧时间的画笔属性
        textPaint = new TextPaint();
        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(32); // 设置文本大小

        //阴影
        shadowPaint = new Paint();
        shadowPaint.setAntiAlias(true);
        shadowPaint.setStyle(Paint.Style.FILL); // 设为填充样式
        shadowPaint.setColor(getResources().getColor(R.color.purple_300)); // 阴影主体的颜色
        shadowPaint.setShadowLayer(10f, 0f, 0f, getResources().getColor(R.color.black2)); // 设置阴影外圈渐变效果

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawScale(canvas); // 绘制刻度线、时间刻度

        for (int i = 0; i < rectangles.size(); i++) {
            if(i != currentRectIndex){// 绘制未选中的矩形
                RectF rect = rectangles.get(i);
                // 检查重叠并调整位置
//                if (isOverlapping(rect, i)) {
//                    rect.left -= offsetAmount; // 向左偏移
//                }
                drawRectAndText(canvas, rect, i);//画矩形框、内部写字
                drawCurrentScale(canvas, rect, i);//绘制显示实时刻度
            }
        }
        // 绘制当前选中的矩形,确保其在最上层
        if (currentRectIndex != -1) {
            RectF selectedRect = rectangles.get(currentRectIndex);
            drawRectAndText(canvas, selectedRect, currentRectIndex,true); // 画选中的矩形框、内部写字
            drawCurrentScale(canvas, selectedRect,currentRectIndex); // 绘制选中的矩形显示实时刻度
        }
        this.mCanvas = canvas;
    }

    private boolean isOverlapping(RectF currentRect, int currentIndex) {
        for (int i = 0; i < rectangles.size(); i++) {
            if (i != currentIndex) {
                RectF otherRect = rectangles.get(i);
                if (RectF.intersects(currentRect, otherRect)) {
                    return true; // 矩形重叠
                }
            }
        }
        return false; // 矩形不重叠
    }


    private void drawScale(Canvas canvas) {
        // 绘制15分钟刻度线
        for (int n = 0; n <= TOTAL_SCALES; n++) {
            float y = n * mScaleWidth; // 计算刻度Y坐标

            // 计算并绘制时间标签(15分钟为单位)
            if (n % 4 == 0) { // 但每小时显示并绘制一次
                int hour = n / 4;
                if ((n / 4 + "").contains("24")) {
                    hour = 0;
                }
                if (y == 0) {//第一根线防止画不全
                    y = 15;
                }
                String timeText = String.format("%02d:00", hour);
                canvas.drawText(timeText, mScaleLineX - 100, y + 10, textPaint); // 绘制时间文本
                canvas.drawLine(mScaleLineX, y, getWidth() - 30, y + 20, paintLine); // 绘制刻度线
            }

        }

    }

    private String finalStartTime = "";
    private String finalEndTime = "";

    private void drawCurrentScale(Canvas canvas, RectF rect, int index) {//绘制显示实时的刻度
        // 计算矩形边界的时间刻度值
        topScale = (int) Math.round(rect.top / (mScaleWidth)); // 计算矩形上边界的刻度
        bottomScale = Math.round(rect.bottom / (mScaleWidth)); // 计算矩形下边界的刻度

        if (currentRect != rect) {//只有当前点击的矩形参与显示
            return;
        }
        boolean isLongPressStatus = (activiesDataModelListl.get(index)).isLongPressStatus();
        if (isLongPressStatus) {
            finalStartTime = "";
            finalEndTime = "";
            // 确保边界值在可用范围内
            if (topScale >= 0 && topScale < TOTAL_SCALES) {
                rectMove = rect;
                topText = String.format("%02d:%02d", topScale / 4, (topScale % 4) * 15);
                finalStartTime = topText;
                //  canvas.drawText(topText, mScaleLineX + 10, rect.top + 20, textPaint); // 绘制上边界刻度
                canvas.drawText(topText, mScaleLineX - 100, rect.top + 20, paintDragText); // 绘制上边界刻度
            }

            if (bottomScale >= 0 && bottomScale < TOTAL_SCALES) {
                bottomText = String.format(" %02d:%02d", bottomScale / 4, (bottomScale % 4) * 15);
                finalEndTime = bottomText;
                canvas.drawText(bottomText, mScaleLineX - 100, rect.bottom - 10, paintDragText); // 绘制下边界刻度
                //canvas.drawText(bottomText, mScaleLineX + 10, rect.bottom - 10, textPaint); // 绘制下边界刻度
            }

            if (!TextUtils.isEmpty(finalStartTime) && !TextUtils.isEmpty(finalEndTime)) {//更新调整后的时间
                activiesDataModelListl.get(index).setStart_time(finalStartTime);
                activiesDataModelListl.get(index).setEnd_time(finalEndTime);
            }
        } else {

        }

    }

    private void drawRectAndText(Canvas canvas, RectF rect, int index) {

        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paintRect); // 绘制圆角矩形-填充
        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, borderPaint); // 绘制圆角矩形-边框
        boolean isLongPressStatus = (activiesDataModelListl.get(index)).isLongPressStatus();
        //绘制矩形的四个边角
        if (isLongPressStatus) {
            if (currentRect != null && currentRect == rect) {
                // lt h 左上角
                //  canvas.drawLine(rect.left, rect.top, rect.left + lineLen, rect.top, paintRect);
                // lt v
                //    canvas.drawLine(rect.left, rect.top, rect.left, rect.top + lineLen, paintRect);

                // rt h 右上角
                //  canvas.drawLine(rect.right - lineLen, rect.top, rect.right, rect.top, paintRect);
                // rt v
                //   canvas.drawLine(rect.right, rect.top, rect.right, rect.top + lineLen, paintRect);

                // lb h左下角
                //   canvas.drawLine(rect.left, rect.bottom, rect.left + lineLen, rect.bottom, paintRect);
                // lb v
                //       canvas.drawLine(rect.left, rect.bottom - lineLen, rect.left, rect.bottom, paintRect);

                // rb h 右下角
                //   canvas.drawLine(rect.right - lineLen, rect.bottom, rect.right, rect.bottom, paintRect);
                // rb v
                //  canvas.drawLine(rect.right, rect.bottom - lineLen, rect.right, rect.bottom, paintRect);

                drawShadowEffect(canvas, rect); // 绘制阴影效果
            }

        }

        ActiviesDataModel activiesDataModel = activiesDataModelListl.get(index);
        String name = activiesDataModel.getPop_name();
        String address = activiesDataModel.getPop_address();
        int popdb_id = activiesDataModel.getPopdb_id();
        String startTime = activiesDataModel.getStart_time();
        String endTime = activiesDataModel.getEnd_time();
        String value = name + " \n " + address + " \n " + startTime + " -- " + endTime;
        // 绘制矩形内的文本
        StaticLayout staticLayout = new StaticLayout(value, textPaint,
                (int) rect.width(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        canvas.save();
        canvas.translate(rect.left, rect.top); // 移动画布到矩形的左上角
        staticLayout.draw(canvas);
        canvas.restore();

    }

    private void drawRectAndText(Canvas canvas, RectF rect, int index,boolean isInUp) {

        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paintRect); // 绘制圆角矩形-填充
        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, borderPaint); // 绘制圆角矩形-边框
        boolean isLongPressStatus = (activiesDataModelListl.get(index)).isLongPressStatus();
        //绘制矩形的四个边角
        if (isLongPressStatus) {
            if (currentRect != null && currentRect == rect) {
                // lt h 左上角
                //  canvas.drawLine(rect.left, rect.top, rect.left + lineLen, rect.top, paintRect);
                // lt v
                //    canvas.drawLine(rect.left, rect.top, rect.left, rect.top + lineLen, paintRect);

                // rt h 右上角
                //  canvas.drawLine(rect.right - lineLen, rect.top, rect.right, rect.top, paintRect);
                // rt v
                //   canvas.drawLine(rect.right, rect.top, rect.right, rect.top + lineLen, paintRect);

                // lb h左下角
                //   canvas.drawLine(rect.left, rect.bottom, rect.left + lineLen, rect.bottom, paintRect);
                // lb v
                //       canvas.drawLine(rect.left, rect.bottom - lineLen, rect.left, rect.bottom, paintRect);

                // rb h 右下角
                //   canvas.drawLine(rect.right - lineLen, rect.bottom, rect.right, rect.bottom, paintRect);
                // rb v
                //  canvas.drawLine(rect.right, rect.bottom - lineLen, rect.right, rect.bottom, paintRect);

                drawShadowEffect(canvas, rect); // 绘制阴影效果
            }

        }

        ActiviesDataModel activiesDataModel = activiesDataModelListl.get(index);
        String name = activiesDataModel.getPop_name();
        String address = activiesDataModel.getPop_address();
        int popdb_id = activiesDataModel.getPopdb_id();
        String startTime = activiesDataModel.getStart_time();
        String endTime = activiesDataModel.getEnd_time();
        String value = name + " \n " + address + " \n " + startTime + " -- " + endTime;
        // 绘制矩形内的文本
        StaticLayout staticLayout = new StaticLayout(value, textPaint,
                (int) rect.width(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        canvas.save();
        canvas.translate(rect.left, rect.top); // 移动画布到矩形的左上角
        staticLayout.draw(canvas);
        canvas.restore();

    }


    private void drawShadowEffect(Canvas canvas, RectF rect) {
        // 绘制阴影
        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, shadowPaint); // 绘制圆角矩形
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float currentX = event.getX();
        float currentY = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 检查是否按下了控制点
                longPressStartTime = 0;
                for (int i = 0; i < rectangles.size(); i++) {
                    RectF rect = rectangles.get(i);

                    if (isInHandle(currentX, currentY, rect, LEFT_TOP)) {
                        currentRect = rect;
                        currentHandle = LEFT_TOP;
                    } else if (isInHandle(currentX, currentY, rect, RIGHT_TOP)) {
                        currentRect = rect;
                        currentHandle = RIGHT_TOP;
                    } else if (isInHandle(currentX, currentY, rect, LEFT_BOTTOM)) {
                        currentRect = rect;
                        currentHandle = LEFT_BOTTOM;
                    } else if (isInHandle(currentX, currentY, rect, RIGHT_BOTTOM)) {
                        currentRect = rect;
                        currentHandle = RIGHT_BOTTOM;
                    } else if (rect.contains(currentX, currentY)) {
                        // 检测是否按下矩形
                        currentRectIndex = i;
                        longPressStartTime = System.currentTimeMillis(); // 记录长按开始时间
                        currentRect = rect;
                        currentHandle = MOVE_RECT;

                        if (preRect != currentRect) {//前后两个矩形不是同一个
                            //  isLongPressStatus = false;
                        }

                        timer = new Timer();
                        timerTask = new TimerTask() {
                            @Override
                            public void run() {
                                //长按逻辑触发

                                if(currentRectIndex!=-1){
                                    activiesDataModelListl.get(currentRectIndex).setLongPressStatus(true);
                                    // 长按触摸区域,禁止父容器 (ScrollView) 拦截触摸事件
                                    if (scrollView != null) {
                                        scrollView.requestDisallowInterceptTouchEvent(true);
                                    }
                                }

                                invalidate(); // 刷新视图
                            }
                        };
                        timer.schedule(timerTask, LONGPRESSTIME, 1000 * 60 * 60 * 24);

                    } else {
                        // activiesDataModelListl.get(i).setLongPressStatus(false);
                    }
                }
                lastX = currentX;
                lastY = currentY;
                preX = currentX;
                preY = currentY;
                isCancleClickStatus = true;
                try {
                    boolean longPressStatus = activiesDataModelListl.get(currentRectIndex).isLongPressStatus();
                    if (currentRect != null && longPressStatus) {
                        // 触摸区域在矩形内,禁止父容器 (ScrollView) 拦截触摸事件
                        if (scrollView != null) {
                            scrollView.requestDisallowInterceptTouchEvent(true);
                        }

                    }
                } catch (Exception e) {
                    e.getMessage();
                }

                break;

            case MotionEvent.ACTION_MOVE:
                if (currentRect != null) {
                    float dx = currentX - lastX;
                    float dy = currentY - lastY;
                    boolean isLongPressStatus = false;
                    if(currentRectIndex!=-1){
                        isLongPressStatus = (activiesDataModelListl.get(currentRectIndex)).isLongPressStatus();
                    }
                    switch (currentHandle) {
                        case LEFT_TOP:
                            //  currentRect.left += dx;//只允许上下拉伸 禁止左右拉伸
                            if (isLongPressStatus) {
                                currentRect.top += dy;
                            }

                            break;
                        case RIGHT_TOP:
                            //   currentRect.right += dx;
                            if (isLongPressStatus) {
                                currentRect.top += dy;
                            }

                            break;
                        case LEFT_BOTTOM:
                            //  currentRect.left += dx;
                            if (isLongPressStatus) {
                                currentRect.bottom += dy;
                            }

                            break;
                        case RIGHT_BOTTOM:
                            //    currentRect.right += dx;
                            if (isLongPressStatus) {
                                currentRect.bottom += dy;
                            }

                            break;
                        case MOVE_RECT:
                            if (currentRectIndex != -1) {
                                long pressDuration = System.currentTimeMillis() - longPressStartTime;
                                if (isLongPressStatus) {//长按的时间长度
                                    //     isDragging = true; // 开始拖动
                                    // 计算拖动距离
                                    RectF rect = rectangles.get(currentRectIndex);
                                    rect.offset(0, (int) dy);//dx=0 只允许Y轴滑动 不允许x轴左右滑动

                                }
                            }
                            break;
                    }

                    for (int n = 0; n < rectangles.size(); n++) {
                        RectF rect = rectangles.get(n);
                        boolean isLongPressStatusTemp = (activiesDataModelListl.get(n)).isLongPressStatus();
                        if (rect != currentRect) {
                            activiesDataModelListl.get(n).setLongPressStatus(false);
                        }
                    }

                    //取消计时
                    timerTask.cancel();
                    timer.cancel();

                    // 更新最后位置
                    lastX = currentX;
                    lastY = currentY;
                    invalidate(); // 刷新视图

                }
                break;

            case MotionEvent.ACTION_UP:
                if (currentRect != null) {
                    float dx = currentX - preX;
                    float dy = currentY - preY;
                    boolean isLongPressStatus = false;
                    if(currentRectIndex!=-1){
                        isLongPressStatus = (activiesDataModelListl.get(currentRectIndex)).isLongPressStatus();
                    }

                    if (dx == 0 && dy == 00) {//此处应该用绝对值来判断一个较小的数值
                        if (!isLongPressStatus) {//不是长按状态的矩形
                            boolean isHasTrue = false;
                            for (int n = 0; n < rectangles.size(); n++) {
                                RectF rect = rectangles.get(n);
                                boolean isLongPressStatusTemp = (activiesDataModelListl.get(n)).isLongPressStatus();
                                if (isLongPressStatusTemp) {
                                    isHasTrue = true;//检查是否存在点击状态的矩形
                                }

                                if (rect != currentRect) {
                                    activiesDataModelListl.get(n).setLongPressStatus(false);
                                }
                            }

                            invalidate(); // 刷新视图

                            if (isHasTrue) {

                            } else {//如果没有选中状态矩形 则允许回调点击事件
                                if (onclickListener != null) {
                                    onclickListener.onclick(currentRectIndex + "--" + activiesDataModelListl.get(currentRectIndex).getPop_name());
                                }
                            }

                        } else {//点击的是长按状态的矩形

                        }
                    }

                    preRect = currentRect;
                } else {//点击在非矩形区域
                    isCancleClickStatus = false;
                    //   isLongPressStatus = false;
                    try {
                        (activiesDataModelListl.get(currentRectIndex)).setLongPressStatus(false);
                    } catch (Exception e) {
                        e.getMessage();
                    }

                    invalidate(); // 刷新视图
                }
                // currentRectIndex = -1; // 取消触摸
                currentRect = null;
                currentHandle = NONE;
                //取消计时
                timerTask.cancel();
                timer.cancel();

                try {
                    boolean longPressStatusUp = activiesDataModelListl.get(currentRectIndex).isLongPressStatus();

                    // 恢复父容器 (ScrollView) 拦截触摸事件
                    if (scrollView != null && !longPressStatusUp) {
                        scrollView.requestDisallowInterceptTouchEvent(false);
                    }
                } catch (Exception e) {
                    e.getMessage();
                }

                break;
            case MotionEvent.ACTION_CANCEL:
                currentHandle = NONE;
                currentRectIndex = -1; // 取消触摸
                isLongPressStatus = false;
                //  (activiesDataModelListl.get(currentRectIndex)).setLongPressStatus(false);
                // 恢复父容器 (ScrollView) 拦截触摸事件
                if (scrollView != null) {
                    scrollView.requestDisallowInterceptTouchEvent(false);
                }
                break;
        }
        return true;
    }

    private boolean isInHandle(float x, float y, RectF rect, int handle) {
        float handleSize = 60f; // 控制点的尺寸
        switch (handle) {
            case LEFT_TOP:
                return x >= rect.left - handleSize / 2 && x <= rect.left + handleSize / 2 &&
                        y >= rect.top - handleSize / 2 && y <= rect.top + handleSize / 2;
            case RIGHT_TOP:
                return x >= rect.right - handleSize / 2 && x <= rect.right + handleSize / 2 &&
                        y >= rect.top - handleSize / 2 && y <= rect.top + handleSize / 2;
            case LEFT_BOTTOM:
                return x >= rect.left - handleSize / 2 && x <= rect.left + handleSize / 2 &&
                        y >= rect.bottom - handleSize / 2 && y <= rect.bottom + handleSize / 2;
            case RIGHT_BOTTOM:
                return x >= rect.right - handleSize / 2 && x <= rect.right + handleSize / 2 &&
                        y >= rect.bottom - handleSize / 2 && y <= rect.bottom + handleSize / 2;
            default:
                return false;
        }
    }

    public void addRectangle(float left, float top, int tags, String textC) {
        //rectWidth = getWidth()-400;
        RectF rect = new RectF(left, top, 1010, top + rectHeight);
        Map<Integer, RectF> map = new HashMap<Integer, RectF>();
        map.put(tags, rect);
        texts.add(textC);
        rectanglesMapList.add(map);
        rectangles.add(rect);
        invalidate(); // 刷新视图
    }


    public void addRectangle(ArrayList<ActiviesDataModel> list) {
        this.activiesDataModelListl = list;
        for (int i = 0; i < activiesDataModelListl.size(); i++) {
            ActiviesDataModel activiesDataModel = activiesDataModelListl.get(i);
            String name = activiesDataModel.getPop_name();
            String address = activiesDataModel.getPop_address();
            int popdb_id = activiesDataModel.getPopdb_id();
            String startTime = activiesDataModel.getStart_time();
            String endTime = activiesDataModel.getEnd_time();


            // 需要根据具体的业务逻辑计算 left 和 top
            float top_start = formatTimeToYValues(startTime);
            float bootom_end = formatTimeToYValues(endTime);
            float rect_height = bootom_end - top_start;
            float rect_width = this.getWidth() - mScaleRectToLeft;
            RectF rect = new RectF(mScaleRectToLeft, top_start, 1010, top_start + rect_height);
            Map<Integer, RectF> map = new HashMap<>();
            map.put(i, rect);
            texts.add(name + " \n " + address + " \n " + startTime + " \n " + endTime);
            rectanglesMapList.add(map);
            rectangles.add(rect);
        }

        invalidate(); // 刷新视图
    }


    // 转换刻度值为时间字符串
    private float formatTimeToYValues(String time_value) {
        float y_value = 0;
        String closesTime = findClosestTimeRange(time_value);
        // 绘制15分钟刻度线
        for (int n = 0; n <= TOTAL_SCALES; n++) {
            float y = n * mScaleWidth; // 计算刻度Y坐标

            int hour = n / 4;
            int minute = (n % 4) * 15; // 每个刻度15分钟
            if ((n / 4 + "").contains("24")) {
                hour = 0;
            }
            String timeHMText = String.format("%02d:%02d", hour, minute);
            if (closesTime.equals(timeHMText)) {
                y_value = y;
                break;
            }

        }

        return y_value;
    }

    public String findClosestTimeRange(String time) {
        String[] parts = time.split(":");
        int hour = Integer.parseInt(parts[0]);
        int minute = Integer.parseInt(parts[1]);

        int totalMinutes = hour * 60 + minute;

        // 找到最接近的15分钟的倍数
        int closestMultiple = Math.round((float) totalMinutes / 15) * 15;

        // 将最接近的总分钟数转换回HH:MM格式
        int closestHour = closestMultiple / 60;
        int closestMinute = closestMultiple % 60;

        return String.format("%02d:%02d", closestHour, closestMinute);
    }

    public void setonclicklistener(onclickListener listener) {
        this.onclickListener = listener;
    }

    public void setonLongclicklistener(onLongclicklistener listener) {
        this.onLongclickListener = listener;
    }

    public interface onclickListener {

        void onclick(String value);
    }

    public interface onLongclicklistener {
        void onLongclick(String value);
    }

}

外面传入数据调用即可:

MultiRectView multiRectView = findViewById(R.id.multiRectView);
        multiRectView.setonclicklistener(this);
        multiRectView.setonLongclicklistener(this);
        multiRectView.setScrollView(scrollView);


        ActiviesDataModel activiesDataModel1 = new ActiviesDataModel();
        activiesDataModel1.setPop_name(" POP Name1 万客隆精选超市");
        activiesDataModel1.setPop_address("南京东路1024弄");
        activiesDataModel1.setPopdb_id(215639);
        activiesDataModel1.setStart_time("06:40");
        activiesDataModel1.setEnd_time("08:30");

        ActiviesDataModel activiesDataModel2 = new ActiviesDataModel();
        activiesDataModel2.setPop_name(" POP Name2 京东自营超市");
        activiesDataModel2.setPop_address("南京东路1024弄");
        activiesDataModel2.setPopdb_id(215638);
        activiesDataModel2.setStart_time("09:10");
        activiesDataModel2.setEnd_time("11:50");

        ActiviesDataModel activiesDataModel3 = new ActiviesDataModel();
        activiesDataModel3.setPop_name(" POP Name3 拼多多百亿补贴超市");
        activiesDataModel3.setPop_address("南京东路1024弄");
        activiesDataModel3.setPopdb_id(215637);
        activiesDataModel3.setStart_time("12:40");
        activiesDataModel3.setEnd_time("14:30");

        ActiviesDataModel activiesDataModel4 = new ActiviesDataModel();
        activiesDataModel4.setPop_name(" POP Name4 淘宝天猫超市");
        activiesDataModel4.setPop_address("南京东路1024弄");
        activiesDataModel4.setPopdb_id(215636);
        activiesDataModel4.setStart_time("15:40");
        activiesDataModel4.setEnd_time("17:30");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/935489.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网易云信荣获“HarmonyOS NEXT SDK星河奖”

近日&#xff0c;鸿蒙生态伙伴 SDK 开发者论坛在北京举行。 网易云信凭借在融合通信领域的技术创新和鸿蒙生态贡献&#xff0c;荣获鸿蒙生态“HarmonyOS NEXT SDK星河奖”。 会上&#xff0c;华为鸿蒙正式推出 SDK 生态繁荣伙伴支持计划&#xff0c;旨在为 SDK 领域伙伴和开发…

Electromagnetic Tracking Smart Car based on STM32F401 or GD32F450ZGT6

Electromagnetic Smart Car1 基于梁山派的电磁循迹智能车的主控芯片来自立创梁山派板载的国产兆易创新GD32F450ZGT6&#xff0c;整车采用模块化开发&#xff0c;由电源模块、L298N驱动模块、电磁循迹模块、梁山派、调试模块和显示模块组成。 嵌入式软件开发环境是&#xff1a;K…

Windows下Docker Desktop+k8s安装和部署程序

Windows下Docker Desktopk8s安装和部署程序 一、安装Docker DesktopKubernetes 1.需要安装windows版的docker 安装 Docker Desktop&#xff0c;启用Hyper-V、虚拟机平台和容器 https://www.docker.com/get-started/ 2.启用Kubernetes 打开Docker-Desktop&#xff0c;启用…

网络原理03

回顾 应用层&#xff1a;应用程序&#xff0c;数据具体如何使用 传输层&#xff1a;关注起点和终点 网络层&#xff1a;关注路径规划 数据链路层&#xff1a;关注相邻节点的转发 物理层&#xff1a;硬件设备 应用层 应用程序 在应用层&#xff0c;很多时候&#xff0c;…

HTTP 状态码大全

常见状态码 200 OK # 客户端请求成功 400 Bad Request # 客户端请求有语法错误 不能被服务器所理解 401 Unauthorized # 请求未经授权 这个状态代码必须和WWW- Authenticate 报头域一起使用 403 Forbidden # 服务器收到请求但是拒绝提供服务 404 Not Found # 请求资源不存…

Ajax--实现检测用户名是否存在功能

目录 &#xff08;一&#xff09;什么是Ajax &#xff08;二&#xff09;同步交互与异步交互 &#xff08;三&#xff09;AJAX常见应用情景 &#xff08;四&#xff09;AJAX的优缺点 &#xff08;五&#xff09;使用jQuery实现AJAX 1.使用JQuery中的ajax方法实现步骤&#xf…

unique_ptr自定义删除器,_Compressed_pair利用偏特化减少存储的一些设计思路

主要是利用偏特化&#xff0c; 如果自定义删除器是空类&#xff08;没有成员变量&#xff0c;可以有成员函数&#xff09;&#xff1a; _Compressed_pair会继承删除器&#xff08;删除器作为基类&#xff09;&#xff0c;但_Compressed_pair里不保存删除器对象&#xff0c;只…

AGCRN论文解读

一、创新点 传统GCN只能基于静态预定义图建模全局共享模式&#xff0c;而AGCRN通过两种GCN的增强模块&#xff08;NAPL、DAGG&#xff09;实现了更精细的节点特性学习和图结构生成。 1 节点自适应参数学习模块&#xff08;NAPL&#xff09; 传统GCN通过共享参数&#xff08;权重…

使用观测云排查数据库死锁故障

故障发现 核心应用 pod 发生重启&#xff0c;同时接收到对应使用者反馈业务问题&#xff0c;开始排查。 观测云排查现场 1、根据重启应用信息&#xff0c;查询 APM 执行数据库 update 操作大量报错&#xff0c;执行时间在 5min 以上。 分析 APM 链路异常&#xff0c;发现是触…

UNIX数据恢复—UNIX系统常见故障问题和数据恢复方案

UNIX系统常见故障表现&#xff1a; 1、存储结构出错&#xff1b; 2、数据删除&#xff1b; 3、文件系统格式化&#xff1b; 4、其他原因数据丢失。 UNIX系统常见故障解决方案&#xff1a; 1、检测UNIX系统故障涉及的设备是否存在硬件故障&#xff0c;如果存在硬件故障&#xf…

npm或yarn包配置地址源

三种方法 1.配置.npmrc 文件 在更目录新增.npmrc文件 然后写入需要访问的包的地址 2.直接yarn.lock文件里面修改地址 简单粗暴 3.yarn install 的时候添加参数 设置包的仓库地址 yarn config set registry https://registry.yarnpkg.com 安装&#xff1a;yarn install 注意…

opencv——图片矫正

图像矫正 图像矫正的原理是透视变换&#xff0c;下面来介绍一下透视变换的概念。 听名字有点熟&#xff0c;我们在图像旋转里接触过仿射变换&#xff0c;知道仿射变换是把一个二维坐标系转换到另一个二维坐标系的过程&#xff0c;转换过程坐标点的相对位置和属性不发生变换&a…

【学习】企业通过CMMI认证,还需要申请CSMM资质吗

​ 企业通过CMMI认证之后&#xff0c;是否还有必要申请CSMM资质&#xff1f;这是一个值得软件企业深思的问题。虽然CMMI和CSMM都在组织的软件过程改进和认证方面发挥着重要作用&#xff0c;但它们各自拥有自己的特点在。企业需要根据自身发展需求来选择适合的认证方式。首先我…

OpenHarmony-3.HDF input子系统(5)

HDF input 子系统OpenHarmony-4.0-Release 1.Input 概述 输入设备是用户与计算机系统进行人机交互的主要装置之一&#xff0c;是用户与计算机或者其他设备通信的桥梁。常见的输入设备有键盘、鼠标、游戏杆、触摸屏等。本文档将介绍基于 HDF_Input 模型的触摸屏器件 IC 为 GT91…

BurpSuite之移动端流量抓包

学习视频来自B站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记只是方便学习&#xff0c;以下内容只涉及学习内容&#xff0c;切莫逾越法律红线。 安全见闻&#xff0c;包含了各种网络安全&#xff0c;网络技术&#xff0c;旨在明白自己的渺小&#xff0c;知识的广博&a…

Any2Policy: Learning Visuomotor Policy with Any-Modality(类似AnyGPT)

发表时间&#xff1a;NeurIPS 2024 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId2598959255168534016&noteId2598960522854466816 作者单位&#xff1a;Midea Group Motivation&#xff1a;Current robotic learning methodologies often focus…

QTreeView 与 QTreeWidget 例子

1. 先举个例子 1班有3个学生&#xff1a;张三、李四、王五 4个学生属性&#xff1a;语文 数学 英语 性别。 语文 数学 英语使用QDoubleSpinBox* 编辑&#xff0c;范围为0到100,1位小数 性别使用QComboBox* 编辑&#xff0c;选项为&#xff1a;男、女 实现效果&#xff1a; 2…

计算机视觉与医学的结合:推动医学领域研究的新机遇

目录 引言医学领域面临的发文难题计算机视觉与医学的结合&#xff1a;发展趋势计算机视觉结合医学的研究方向高区位参考文章结语 引言 计算机视觉&#xff08;Computer Vision, CV&#xff09;技术作为人工智能的重要分支&#xff0c;已经在多个领域取得了显著的应用成果&…

谷粒商城—分布式基础

1. 整体介绍 1)安装vagrant 2)安装Centos7 $ vagrant init centos/7 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on…

麒麟系统+达梦数据库+MybatisPlus+Redis+SpringBoot

环境准备 麒麟系统 在麒麟系统官网进行下载镜像 这里选择的是麒麟V10桌面版&#xff0c;使用虚拟机启动 修改root密码 # 启动到单用户模式 init 1 # 修改 root 密码 passwd root # 重启 reboot达梦数据库准备 进入达梦官网 我这里选择的是达梦数据库管理系统DM8开发版 下…