import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.Toast; import com.iec.hydropath10.R; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; public class TimeRulerViewV3 extends View { private List<Long> timeNodes; // 时间节点集合 private static final int TICK_HEIGHT_MAJOR = 40; private static final int TICK_HEIGHT_MINOR = 20; private static final int TEXT_SIZE = 30; private static final int BORDER_WIDTH = 5; private int tickIntervalMinutes=0; private static final long MINIMUM_TIME_RANGE = 15 * 60 * 1000; // 15 minutes private Date startTime; private Date endTime; private float lastX; private long currentTime; private int scaleRulerColor;//刻度尺 private int scaleMarkColor;//刻度标 private int hourColor;//时针颜色 private int MINUTECOLOR;//分针颜色; private int RulerColor; //尺身边框颜色 private Bitmap bitmap; //摄像头 /** * 动画部分 */ private ObjectAnimator animator; private Paint paint; private float translationX; public void setRulerColor(int rulerColor) { RulerColor = rulerColor; } public void setMINUTECOLOR(int MINUTECOLOR) { this.MINUTECOLOR = MINUTECOLOR; } public void setHourColor(int hourColor) { this.hourColor = hourColor; } public void setScaleRulerColor(int scaleRulerColor) { this.scaleRulerColor = scaleRulerColor; } public void setScaleMarkColor(int scaleMarkColor) { this.scaleMarkColor = scaleMarkColor; } private ScaleGestureDetector scaleGestureDetector; public TimeRulerViewV3(Context context) { super(context); init(); } public TimeRulerViewV3(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TimeRulerViewV3(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(); paint.setAntiAlias(true); // 启用抗锯齿 paint.setTextSize(TEXT_SIZE); paint.setTextAlign(Paint.Align.LEFT); currentTime = System.currentTimeMillis(); startTime = new Date(currentTime); endTime = new Date(currentTime + 3 * 60 * 60 * 1000); // 3 hours from now scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); animator = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f); animator.setDuration(1500); // 动画持续时间为1秒 animator.setInterpolator(new AccelerateDecelerateInterpolator()); timeNodes = new ArrayList<>(); // 启动动画 animator.start(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 5; // 缩小为原始尺寸的 1/2 bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.camer, options); // 替换为你的图片资源 post(new Runnable() { @Override public void run() { startAnimation(); } }); } public void setStartTime(Date startTime) { this.startTime = startTime; invalidate(); } public void setEndTime(Date endTime) { this.endTime = endTime; invalidate(); } public void setTickIntervalMinutes(int tickIntervalMinutes) { this.tickIntervalMinutes=tickIntervalMinutes; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int viewWidth = getWidth(); int viewHeight = getHeight(); int usableWidth = viewWidth - BORDER_WIDTH; int totalMinutes = (int) ((endTime.getTime() - startTime.getTime()) / (1000 * 60)); float pixelsPerMinute = (float) usableWidth / totalMinutes; canvas.drawBitmap(bitmap, translationX, 50, paint); Paint paint = new Paint(); paint.setTextSize(TEXT_SIZE); paint.setTextAlign(Paint.Align.CENTER); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(BORDER_WIDTH); paint.setColor(RulerColor); canvas.drawRect(BORDER_WIDTH / 2f, BORDER_WIDTH / 2f, viewWidth - BORDER_WIDTH / 2f, getHeight()-100, paint); Calendar calendar = Calendar.getInstance(); calendar.setTime(startTime); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); while (calendar.getTime().before(endTime)) { int minutesFromStart = (int) ((calendar.getTime().getTime() - startTime.getTime()) / (1000 * 60)); int x = (int) (minutesFromStart * pixelsPerMinute); x += BORDER_WIDTH / 2; boolean isMajorTick = calendar.get(Calendar.MINUTE) == 0; int num = calendar.get(Calendar.HOUR); int tickHeight = isMajorTick ? TICK_HEIGHT_MAJOR : TICK_HEIGHT_MINOR; if (x >= BORDER_WIDTH / 2 && x <= viewWidth - BORDER_WIDTH / 2) { paint.setColor(scaleMarkColor); if (isMajorTick) { paint.setColor(hourColor); paint.setStrokeWidth(2); canvas.drawLine(x, getHeight()-100, x, 0, paint); String timeText = sdf.format(calendar.getTime()); paint.setColor(scaleRulerColor); paint.setStrokeWidth(5); canvas.drawText(timeText, x, viewHeight - tickHeight - 10, paint); }else { paint.setColor(MINUTECOLOR); paint.setStrokeWidth(2); canvas.drawLine(x, getHeight()-100, x, getHeight()-120, paint); } } calendar.add(Calendar.MINUTE, tickIntervalMinutes); } // 绘制时间节点图表 drawChartAtTimeNodes(canvas); } private long lastClickTime = 0; private float startX; // 记录手指按下时的x坐标 private float startY; // 记录手指按下时的y坐标 private boolean isMoving = false; // 记录是否正在滑动 private static final long DOUBLE_CLICK_TIME_DELTA = 300; // 双击间隔时间(以毫秒为单位) @Override public boolean onTouchEvent(MotionEvent event) { scaleGestureDetector.onTouchEvent(event); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: lastX = event.getX(); startX = event.getX(); startY = event.getY(); isMoving = false; // 每次按下时重置isMoving状态 lastClickTime = System.currentTimeMillis(); break; case MotionEvent.ACTION_MOVE: if (Math.abs(event.getX() - startX) > 10 || Math.abs(event.getY() - startY) > 10) { isMoving = true; } if (!scaleGestureDetector.isInProgress()) { if (event.getPointerCount() == 1) { if (startTime.getTime() < currentTime) { // 如果开始时间已经是最初的开始时间,则禁止滑动 startTime.setTime(currentTime); break; } float currentX = event.getX(); float deltaX = currentX - lastX; lastX = currentX; // 计算时间偏移量 long timeOffset = (long) (-deltaX / getWidth() * (endTime.getTime() - startTime.getTime())); // 更新开始时间和结束时间 startTime.setTime(startTime.getTime() + timeOffset); endTime.setTime(endTime.getTime() + timeOffset); // 单指滑动 invalidate(); }else if (event.getPointerCount() == 2){ float currentX = event.getX(); float deltaX = currentX - lastX; lastX = currentX; if (deltaX > 0) { // 向右滑动 if (startTime.getTime() == currentTime) { // 如果开始时间已经是最初的开始时间,则禁止滑动 break; } long timeOffset = (long) (-deltaX / getWidth() * (endTime.getTime() - startTime.getTime())); long newStartTime = startTime.getTime() + timeOffset; if (newStartTime <= currentTime) { startTime.setTime(currentTime); endTime.setTime(currentTime + endTime.getTime() - startTime.getTime()); } else { startTime.setTime(newStartTime); } } else if (deltaX < 0) { // 向左滑动 long timeOffset = (long) (-deltaX / getWidth() * (endTime.getTime() - startTime.getTime())); long newEndTime = endTime.getTime() + timeOffset; if (newEndTime <= currentTime) { endTime.setTime(currentTime); startTime.setTime(currentTime - (endTime.getTime() - startTime.getTime())); } else { startTime.setTime(startTime.getTime() + timeOffset); endTime.setTime(newEndTime); } } // 重新绘制视图 invalidate(); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: long clickTime = System.currentTimeMillis(); float x = event.getX(); float y = event.getY(); // 检查点击位置是否在时间节点附近 if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) { // 双击事件 } else { if (!isMoving) { checkTimeNodeClicked(x, y); } } break; } return true; } // 检查点击位置是否在时间节点附近 private void checkTimeNodeClicked(float x, float y) { // 计算点击位置对应的时间 int totalMinutes = (int) ((endTime.getTime() - startTime.getTime()) / (1000 * 60)); float pixelsPerMinute = (float) getWidth() / totalMinutes; long clickedTime = startTime.getTime() + (long) ((x / getWidth()) * totalMinutes * 60 * 1000); // 遍历时间节点,查找是否有点击的时间节点 boolean timeNodeFound = false; for (Long timeNode : timeNodes) { long difference = Math.abs(timeNode - clickedTime); if (difference <= 15 * 60 * 1000) { // 15 minutes // 计算时间节点对应的 x 坐标 int minutesFromStart = (int) ((timeNode - startTime.getTime()) / (1000 * 60)); int xCoordinate = (int) (minutesFromStart * pixelsPerMinute); // 判断点击位置是否在时间节点附近 if (Math.abs(x - xCoordinate) <= 60 && Math.abs(y - getHeight() / 2) <= 60) { // 点击到了时间节点,执行相应的操作,例如显示提示信息等 // 此处可以根据需要进行处理 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); String currentTimeString = sdf.format(timeNode); showToast("任务节点:"+currentTimeString); timeNodeFound = true; break; } } } // 如果没有点击到时间节点,则显示当前时间 // 如果没有点击到时间节点,则显示时间轴上点击位置对应的时间 if (!timeNodeFound) { // 将点击位置对应的时间转换为 Date 对象 Date clickedDate = new Date(clickedTime); // 格式化时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); String clickedTimeString = sdf.format(clickedDate); // 显示时间轴上点击位置对应的时间 showToast("创建任务时间:" + clickedTimeString); } } // 显示提示信息 private void showToast(String message) { Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); long totalMilliseconds = endTime.getTime() - startTime.getTime(); long scaledMilliseconds = (long) (totalMilliseconds / scaleFactor); if (scaledMilliseconds < MINIMUM_TIME_RANGE) { scaledMilliseconds = MINIMUM_TIME_RANGE; } long newEndTimeMilliseconds = startTime.getTime() + scaledMilliseconds; endTime.setTime(newEndTimeMilliseconds); invalidate(); return true; } } private void startAnimation() { // 创建值动画,从左边界到0 ValueAnimator animator = ValueAnimator.ofFloat(0, getWidth()-60); animator.setDuration(5000); // 动画持续时间为1秒 animator.setInterpolator(new AccelerateDecelerateInterpolator()); // 添加动画监听器,在动画更新时更新translationX的值 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { translationX = (float) animation.getAnimatedValue(); invalidate(); // 重绘视图 } }); // 启动动画 animator.start(); } // 设置时间节点集合 public void setTimeNodes(List<Long> timeNodes) { this.timeNodes = timeNodes; invalidate(); } // 绘制时间节点图表 private void drawChartAtTimeNodes(Canvas canvas) { for (Long timeNode : timeNodes) { drawChartAtSpecificTime(canvas, timeNode); } } // 绘制特定时间的图表 private void drawChartAtSpecificTime(Canvas canvas, long specificTimeInMillis) { // 检查特定时间是否在可见范围内 if (specificTimeInMillis < startTime.getTime() || specificTimeInMillis > endTime.getTime()) { return; } // 计算特定时间对应的 x 坐标 int totalMinutes = (int) ((endTime.getTime() - startTime.getTime()) / (1000 * 60)); int minutesFromStart = (int) ((specificTimeInMillis - startTime.getTime()) / (1000 * 60)); float pixelsPerMinute = (float) getWidth() / totalMinutes; int x = (int) (minutesFromStart * pixelsPerMinute); // 假设您要绘制一个圆形图表 Paint paint = new Paint(); paint.setTextSize(30); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.FILL); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault()); String timeText = sdf.format(specificTimeInMillis); canvas.drawText(""+timeText, x, getHeight()/2, paint); } }
支持多种手势操作时间控件,可自行改进满足业务需求