在位置任务数据项里,添加普通沙漏模拟控件雏形,沙漏控件用于任务定时触发场景。
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Mon Nov 10 00:04:08 GMT 2025
|
#Mon Nov 10 00:53:37 GMT 2025
|
||||||
stageCount=18
|
stageCount=18
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.17
|
publishVersion=15.0.17
|
||||||
buildCount=36
|
buildCount=39
|
||||||
baseBetaVersion=15.0.18
|
baseBetaVersion=15.0.18
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
package cc.winboll.studio.positions.views;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
|
* @Date 2025/11/10 08:29
|
||||||
|
* @Describe 沙漏计时器控件
|
||||||
|
*/
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ClipDrawable;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 沙漏视图类(Java 7语法,修复ProgressDrawable和setHeight问题)
|
||||||
|
*/
|
||||||
|
public class HourglassView extends LinearLayout {
|
||||||
|
public static final String TAG = "HourglassView";
|
||||||
|
// 数据模型
|
||||||
|
private String hourglassId;
|
||||||
|
private int hour; // 小时
|
||||||
|
private int minute; // 分钟
|
||||||
|
private boolean isEnabled; // 开关状态
|
||||||
|
|
||||||
|
// 控件引用
|
||||||
|
private EditText etHour;
|
||||||
|
private EditText etMinute;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private Switch switchControl;
|
||||||
|
|
||||||
|
// 样式参数
|
||||||
|
private int textSize = 16;
|
||||||
|
private int padding = 8;
|
||||||
|
private int progressColor = 0xFF2196F3; // 进度条颜色
|
||||||
|
private int progressBgColor = 0xFFE0E0E0; // 进度条背景色
|
||||||
|
private int textColor = 0xFF333333;
|
||||||
|
private int editTextWidth = 40; // 输入框宽度(dp)
|
||||||
|
private int progressHeight = 8; // 进度条高度(dp,新增参数)
|
||||||
|
|
||||||
|
public HourglassView(Context context) {
|
||||||
|
super(context);
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HourglassView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图布局
|
||||||
|
*/
|
||||||
|
private void initView() {
|
||||||
|
setOrientation(HORIZONTAL);
|
||||||
|
setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
setPadding(dp2px(padding), dp2px(padding), dp2px(padding), dp2px(padding));
|
||||||
|
|
||||||
|
// 1. 左侧时间输入区域(水平布局)
|
||||||
|
LinearLayout inputLayout = new LinearLayout(getContext());
|
||||||
|
inputLayout.setOrientation(HORIZONTAL);
|
||||||
|
inputLayout.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
LayoutParams inputParams = new LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
inputParams.setMargins(0, 0, dp2px(padding * 2), 0);
|
||||||
|
addView(inputLayout, inputParams);
|
||||||
|
|
||||||
|
// 小时输入框
|
||||||
|
etHour = createNumberEditText();
|
||||||
|
etHour.setHint("时");
|
||||||
|
etHour.setFilters(new InputFilter[]{new InputFilter.LengthFilter(2)});
|
||||||
|
inputLayout.addView(etHour, getEditTextParams());
|
||||||
|
|
||||||
|
// 分隔符
|
||||||
|
TextView divider = new TextView(getContext());
|
||||||
|
divider.setText(":");
|
||||||
|
divider.setTextSize(textSize);
|
||||||
|
divider.setTextColor(textColor);
|
||||||
|
LayoutParams dividerParams = new LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
dividerParams.setMargins(dp2px(padding / 2), 0, dp2px(padding / 2), 0);
|
||||||
|
inputLayout.addView(divider, dividerParams);
|
||||||
|
|
||||||
|
// 分钟输入框
|
||||||
|
etMinute = createNumberEditText();
|
||||||
|
etMinute.setHint("分");
|
||||||
|
etMinute.setFilters(new InputFilter[]{new InputFilter.LengthFilter(2)});
|
||||||
|
inputLayout.addView(etMinute, getEditTextParams());
|
||||||
|
|
||||||
|
// 2. 中间进度条(修复:通过LayoutParams设置高度,替代setHeight)
|
||||||
|
progressBar = new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
|
||||||
|
progressBar.setProgressDrawable(createProgressDrawable()); // 传入Drawable类型
|
||||||
|
|
||||||
|
// 修复核心:用LayoutParams设置进度条高度(兼容低版本)
|
||||||
|
LayoutParams progressParams = new LayoutParams(
|
||||||
|
0,
|
||||||
|
dp2px(progressHeight), // 直接在布局参数中设置高度(dp转px)
|
||||||
|
1.0f
|
||||||
|
);
|
||||||
|
progressParams.setMargins(0, 0, dp2px(padding * 2), 0);
|
||||||
|
addView(progressBar, progressParams);
|
||||||
|
|
||||||
|
// 3. 右侧开关
|
||||||
|
switchControl = new Switch(getContext());
|
||||||
|
switchControl.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(android.widget.CompoundButton buttonView, boolean isChecked) {
|
||||||
|
isEnabled = isChecked;
|
||||||
|
// 开关状态控制输入框是否可编辑
|
||||||
|
etHour.setEnabled(!isChecked);
|
||||||
|
etMinute.setEnabled(!isChecked);
|
||||||
|
// 更新进度条(仅在开关开启时生效)
|
||||||
|
if (isChecked) {
|
||||||
|
updateProgressBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addView(switchControl);
|
||||||
|
|
||||||
|
// 初始状态
|
||||||
|
isEnabled = false;
|
||||||
|
etHour.setEnabled(true);
|
||||||
|
etMinute.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建数字输入框
|
||||||
|
*/
|
||||||
|
private EditText createNumberEditText() {
|
||||||
|
EditText editText = new EditText(getContext());
|
||||||
|
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
|
editText.setTextSize(textSize);
|
||||||
|
editText.setTextColor(textColor);
|
||||||
|
editText.setGravity(Gravity.CENTER);
|
||||||
|
editText.setSingleLine(true);
|
||||||
|
editText.setBackgroundResource(android.R.drawable.edit_text); // 默认输入框背景
|
||||||
|
return editText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取输入框布局参数
|
||||||
|
*/
|
||||||
|
private LayoutParams getEditTextParams() {
|
||||||
|
LayoutParams params = new LayoutParams(
|
||||||
|
dp2px(editTextWidth),
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
params.setMargins(0, 0, dp2px(padding), 0);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复核心:创建ProgressDrawable(返回Drawable类型,而非Paint)
|
||||||
|
* 用LayerDrawable实现「背景+进度」的双层进度条
|
||||||
|
*/
|
||||||
|
private Drawable createProgressDrawable() {
|
||||||
|
// 1. 进度条背景(灰色)
|
||||||
|
ColorDrawable bgDrawable = new ColorDrawable(progressBgColor);
|
||||||
|
// 2. 进度条前景(主题色)
|
||||||
|
ColorDrawable progressDrawable = new ColorDrawable(progressColor);
|
||||||
|
// 3. 用ClipDrawable包裹前景,实现进度裁剪
|
||||||
|
ClipDrawable clipDrawable = new ClipDrawable(progressDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
|
||||||
|
|
||||||
|
// 4. 组合成LayerDrawable(顺序:背景在下,进度在上)
|
||||||
|
Drawable[] layers = new Drawable[]{bgDrawable, clipDrawable};
|
||||||
|
LayerDrawable layerDrawable = new LayerDrawable(layers);
|
||||||
|
|
||||||
|
// 5. 设置进度条的层级ID(必须与系统ProgressBar的ID匹配)
|
||||||
|
layerDrawable.setId(0, android.R.id.background);
|
||||||
|
layerDrawable.setId(1, android.R.id.progress);
|
||||||
|
|
||||||
|
return layerDrawable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新进度条(总时间 = 小时*60 + 分钟,单位:分钟)
|
||||||
|
*/
|
||||||
|
private void updateProgressBar() {
|
||||||
|
try {
|
||||||
|
// 获取输入的时间(为空时默认0)
|
||||||
|
int inputHour = TextUtils.isEmpty(etHour.getText().toString().trim())
|
||||||
|
? 0 : Integer.parseInt(etHour.getText().toString().trim());
|
||||||
|
int inputMinute = TextUtils.isEmpty(etMinute.getText().toString().trim())
|
||||||
|
? 0 : Integer.parseInt(etMinute.getText().toString().trim());
|
||||||
|
|
||||||
|
// 校验时间合法性(小时0-99,分钟0-59)
|
||||||
|
inputHour = Math.max(0, Math.min(99, inputHour));
|
||||||
|
inputMinute = Math.max(0, Math.min(59, inputMinute));
|
||||||
|
|
||||||
|
// 计算总分钟数(进度条最大值)
|
||||||
|
int totalMinutes = inputHour * 60 + inputMinute;
|
||||||
|
totalMinutes = Math.max(1, totalMinutes); // 最小1分钟,避免进度条无长度
|
||||||
|
|
||||||
|
// 更新进度条
|
||||||
|
progressBar.setMax(totalMinutes);
|
||||||
|
progressBar.setProgress(totalMinutes); // 初始显示满进度,可根据实际需求修改
|
||||||
|
|
||||||
|
// 更新数据模型
|
||||||
|
this.hour = inputHour;
|
||||||
|
this.minute = inputMinute;
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 输入非法时重置进度条
|
||||||
|
progressBar.setMax(0);
|
||||||
|
progressBar.setProgress(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp转px(适配不同设备)
|
||||||
|
*/
|
||||||
|
private int dp2px(int dp) {
|
||||||
|
return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------- 数据模型 getter/setter -------------------
|
||||||
|
public String getHourglassId() {
|
||||||
|
return hourglassId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHourglassId(String hourglassId) {
|
||||||
|
this.hourglassId = hourglassId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHour() {
|
||||||
|
return hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHour(int hour) {
|
||||||
|
this.hour = Math.max(0, Math.min(99, hour)); // 限制范围
|
||||||
|
etHour.setText(String.valueOf(this.hour));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinute() {
|
||||||
|
return minute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinute(int minute) {
|
||||||
|
this.minute = Math.max(0, Math.min(59, minute)); // 限制范围
|
||||||
|
etMinute.setText(String.valueOf(this.minute));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
isEnabled = enabled;
|
||||||
|
switchControl.setChecked(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动更新进度条(外部调用)
|
||||||
|
*/
|
||||||
|
public void refreshProgress() {
|
||||||
|
if (isEnabled) {
|
||||||
|
updateProgressBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工具类:判断字符串是否为空(Java7无TextUtils.isEmpty,手动实现)
|
||||||
|
private static class TextUtils {
|
||||||
|
public static boolean isEmpty(CharSequence str) {
|
||||||
|
return str == null || str.length() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -483,6 +483,12 @@ public class PositionTaskListView extends LinearLayout {
|
|||||||
final EditText etEditDistance = dialogView.findViewById(R.id.et_edit_distance);
|
final EditText etEditDistance = dialogView.findViewById(R.id.et_edit_distance);
|
||||||
Button btnCancel = dialogView.findViewById(R.id.btn_dialog_cancel);
|
Button btnCancel = dialogView.findViewById(R.id.btn_dialog_cancel);
|
||||||
Button btnSave = dialogView.findViewById(R.id.btn_dialog_save);
|
Button btnSave = dialogView.findViewById(R.id.btn_dialog_save);
|
||||||
|
HourglassView hourglassView = dialogView.findViewById(R.id.hourglassView);
|
||||||
|
hourglassView.setHourglassId("hourglass_001");
|
||||||
|
hourglassView.setHour(1);
|
||||||
|
hourglassView.setMinute(30);
|
||||||
|
hourglassView.setEnabled(false); // 开启开关
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 绑定外层对话框内的控件
|
// 绑定外层对话框内的控件
|
||||||
|
|||||||
@@ -65,6 +65,16 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<cc.winboll.studio.positions.views.HourglassView
|
||||||
|
android:id="@+id/hourglassView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -77,7 +87,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="开始时间"
|
android:text="开始时间"
|
||||||
android:id="@+id/btn_select_time"/>
|
android:id="@+id/btn_select_time"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_selected_time"
|
android:id="@+id/tv_selected_time"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -85,7 +95,6 @@
|
|||||||
android:text="Text"
|
android:text="Text"
|
||||||
android:layout_weight="1.0"/>
|
android:layout_weight="1.0"/>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
Reference in New Issue
Block a user