Commit 309f64be authored by 姜天宇's avatar 姜天宇

feat:增加纠错功能;学习页增加重置背景功能

parent 622bd51f
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
\ No newline at end of file
...@@ -68,13 +68,16 @@ public class USBCameraHelper { ...@@ -68,13 +68,16 @@ public class USBCameraHelper {
synchronized (mSyncFrame) { synchronized (mSyncFrame) {
count ++; count ++;
int len = buffer.capacity(); int len = buffer.capacity();
if (frame != null){
frame = null;
}
frame = new byte[len]; frame = new byte[len];
buffer.get(frame); buffer.get(frame);
if (count % frameInterval == 0){ if (count % frameInterval == 0){
count = 0; count = 0;
if (onImageAnalyzeListener != null){ if (onImageAnalyzeListener != null){
onImageAnalyzeListener.onAnalyzed(encodeYuvToJpeg(frame, 640, 480, ImageFormat.NV21)); // onImageAnalyzeListener.onAnalyzed(encodeYuvToJpeg(frame, 640, 480, ImageFormat.NV21));
// onImageAnalyzeListener.onAnalyzed(nv21ToBitmap.nv21ToBitmap(frame, 640, 480)); onImageAnalyzeListener.onAnalyzed(nv21ToBitmap.nv21ToBitmap(frame, 640, 480));
// onImageAnalyzeListener.onAnalyzed(BitmapFactory.decodeByteArray(frame, 0, frame.length)); // onImageAnalyzeListener.onAnalyzed(BitmapFactory.decodeByteArray(frame, 0, frame.length));
} }
} }
......
...@@ -89,5 +89,7 @@ dependencies { ...@@ -89,5 +89,7 @@ dependencies {
api 'androidx.drawerlayout:drawerlayout:1.2.0' api 'androidx.drawerlayout:drawerlayout:1.2.0'
api 'com.github.jenly1314.AppUpdater:app-updater:1.2.0' api 'com.github.jenly1314.AppUpdater:app-updater:1.2.0'
// eventbus
api 'org.greenrobot:eventbus:3.1.1'
} }
\ No newline at end of file
...@@ -20,7 +20,7 @@ import java.util.List; ...@@ -20,7 +20,7 @@ import java.util.List;
* 抽屉菜单适配器 * 抽屉菜单适配器
* @author dizi * @author dizi
*/ */
public class DrawerMenuAdapter extends BaseDiffRecyclerViewAdapter<DrawerMenuAdapter.DrawerMenuViewHolder, DrawerMenuItemVO, DrawerMenuDiffUtils> { public class DrawerMenuAdapter extends BaseDiffRecyclerViewAdapter<DrawerMenuItemVO, DrawerMenuDiffUtils> {
public DrawerMenuAdapter(Context mContext, List<DrawerMenuItemVO> mList) { public DrawerMenuAdapter(Context mContext, List<DrawerMenuItemVO> mList) {
super(mContext, mList); super(mContext, mList);
...@@ -32,12 +32,13 @@ public class DrawerMenuAdapter extends BaseDiffRecyclerViewAdapter<DrawerMenuAda ...@@ -32,12 +32,13 @@ public class DrawerMenuAdapter extends BaseDiffRecyclerViewAdapter<DrawerMenuAda
} }
@Override @Override
protected int getItemLayoutResId() { protected int getLayoutResIdByViewType(int viewType) {
return R.layout.item_navi_menu; return R.layout.item_navi_menu;
} }
@Override @Override
protected DrawerMenuViewHolder createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener) { protected DrawerMenuViewHolder createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener, int viewType) {
return new DrawerMenuViewHolder(itemView, onItemClickListener); return new DrawerMenuViewHolder(itemView, onItemClickListener);
} }
......
...@@ -16,7 +16,7 @@ import java.util.List; ...@@ -16,7 +16,7 @@ import java.util.List;
* 带有数据比较器的RecyclerView适配器 * 带有数据比较器的RecyclerView适配器
* @author dizi * @author dizi
*/ */
public abstract class BaseDiffRecyclerViewAdapter<VH extends BaseViewHolder<T>, T extends BaseDiffUtilModel, DF extends BaseDiffUtil<T>> extends BaseRecyclerViewAdapter<VH, T>{ public abstract class BaseDiffRecyclerViewAdapter<T extends BaseDiffUtilModel, DF extends BaseDiffUtil<T>> extends BaseRecyclerViewAdapter<T>{
/** /**
* 旧的数据源 * 旧的数据源
*/ */
......
...@@ -16,7 +16,7 @@ import java.util.List; ...@@ -16,7 +16,7 @@ import java.util.List;
* 基础列表适配器 * 基础列表适配器
* @author dizi * @author dizi
*/ */
public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> extends RecyclerView.Adapter<VH> { public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<BaseViewHolder<T>> {
protected Context mContext; protected Context mContext;
/** /**
...@@ -39,13 +39,13 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e ...@@ -39,13 +39,13 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e
@NonNull @NonNull
@Override @Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public BaseViewHolder<T> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(getItemLayoutResId(), parent, false); View itemView = LayoutInflater.from(mContext).inflate(getLayoutResIdByViewType(viewType), parent, false);
return createViewHolder(itemView, mOnItemClickListener); return createViewHolder(itemView, mOnItemClickListener, viewType);
} }
@Override @Override
public void onBindViewHolder(@NonNull VH holder, int position) { public void onBindViewHolder(@NonNull BaseViewHolder<T> holder, int position) {
T item = mList.get(position); T item = mList.get(position);
holder.bind(item); holder.bind(item);
} }
...@@ -60,11 +60,16 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e ...@@ -60,11 +60,16 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e
} }
} }
public void setData(List<T> mList) {
this.mList = mList;
}
/** /**
* 获取item的布局ID * 根据viewType获取对应布局
* @param viewType
* @return * @return
*/ */
protected abstract int getItemLayoutResId(); protected abstract int getLayoutResIdByViewType(int viewType);
/** /**
* 由子类new出ViewHolder * 由子类new出ViewHolder
...@@ -72,5 +77,5 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e ...@@ -72,5 +77,5 @@ public abstract class BaseRecyclerViewAdapter<VH extends BaseViewHolder<T>, T> e
* @param onItemClickListener * @param onItemClickListener
* @return * @return
*/ */
protected abstract VH createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener); protected abstract BaseViewHolder<T> createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener, int viewType);
} }
package com.wmdigit.common.base.model;
import com.wmdigit.common.enums.MessageType;
/**
* EventBus得消息基类
* @author dizi
*/
public class BaseMessage<T>{
private MessageType type;
private T payload;
public BaseMessage(MessageType type, T payload) {
this.type = type;
this.payload = payload;
}
public T getPayload() {
return payload;
}
}
...@@ -88,6 +88,18 @@ public abstract class BaseMvvmFragment<VM extends BaseViewModel, DB extends View ...@@ -88,6 +88,18 @@ public abstract class BaseMvvmFragment<VM extends BaseViewModel, DB extends View
}); });
} }
/**
* 从当前活动中获取共享的ViewModel实例
* 该方法使用泛型来允许获取不同类型的ViewModel,但要求这些类型继承自BaseViewModel
* 通过在活动级别共享ViewModel,确保在活动内的各个片段间共享数据
*
* @param viewModelClass ViewModel类的类型,用于指定所需ViewModel的具体类型
* @return 返回指定类型的ViewModel实例,该实例在当前活动中共享
*/
protected <AVM extends BaseViewModel> AVM getSharedViewModelFromActivity(Class<AVM> viewModelClass){
return new ViewModelProvider(requireActivity()).get(viewModelClass);
}
/** /**
* 初始化View * 初始化View
*/ */
......
package com.wmdigit.common.enums;
/**
* EventBus得消息类型枚举
* @author dizi
*/
public enum MessageType {
/**
* 展示大图
*/
SHOW_LARGE_IMAGE,
/**
* 通过图片路径删除一个向量
*/
DEL_ONE_FEATURE_BY_PATH,
/**
* 通过商品编码删除所有向量
*/
DEL_ALL_FEATURES_BY_PRODUCT_CODE
}
...@@ -4,11 +4,20 @@ import androidx.annotation.NonNull; ...@@ -4,11 +4,20 @@ import androidx.annotation.NonNull;
import com.wmdigit.common.base.diffutil.BaseDiffUtilModel; import com.wmdigit.common.base.diffutil.BaseDiffUtilModel;
import java.util.List;
/** /**
* 商品信息 * 商品信息
* @author dizi * @author dizi
*/ */
public class ProductsVO extends BaseDiffUtilModel { public class ProductsVO extends BaseDiffUtilModel {
/**
* layout类型
* 0-商品列表layout
* 1-学习记录layout
*/
private int layoutType;
/** /**
* 品名 * 品名
*/ */
...@@ -29,9 +38,14 @@ public class ProductsVO extends BaseDiffUtilModel { ...@@ -29,9 +38,14 @@ public class ProductsVO extends BaseDiffUtilModel {
* 是否被选中 * 是否被选中
*/ */
private Boolean isChecked; private Boolean isChecked;
/**
* 图片路径
*/
private List<String> imgPathList;
public ProductsVO() { public ProductsVO() {
isChecked = false; isChecked = false;
layoutType = 0;
} }
public ProductsVO(String productName, String productCode, String productMnemonicCode, String unitPrice) { public ProductsVO(String productName, String productCode, String productMnemonicCode, String unitPrice) {
...@@ -42,6 +56,14 @@ public class ProductsVO extends BaseDiffUtilModel { ...@@ -42,6 +56,14 @@ public class ProductsVO extends BaseDiffUtilModel {
this.isChecked = false; this.isChecked = false;
} }
public int getLayoutType() {
return layoutType;
}
public void setLayoutType(int layoutType) {
this.layoutType = layoutType;
}
public boolean isChecked() { public boolean isChecked() {
return isChecked; return isChecked;
} }
...@@ -82,6 +104,22 @@ public class ProductsVO extends BaseDiffUtilModel { ...@@ -82,6 +104,22 @@ public class ProductsVO extends BaseDiffUtilModel {
this.unitPrice = unitPrice; this.unitPrice = unitPrice;
} }
public Boolean getChecked() {
return isChecked;
}
public void setChecked(Boolean checked) {
isChecked = checked;
}
public List<String> getImgPathList() {
return imgPathList;
}
public void setImgPathList(List<String> imgPathArray) {
this.imgPathList = imgPathArray;
}
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
......
...@@ -31,7 +31,7 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout { ...@@ -31,7 +31,7 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout {
private TextView key0, key1, key2, key3, key4, key5, key6, key7, key8, key9, keyPoint; private TextView key0, key1, key2, key3, key4, key5, key6, key7, key8, key9, keyPoint;
private ConstraintLayout clEnglishKeyboard, clNumberKeyboard; private ConstraintLayout clEnglishKeyboard, clNumberKeyboard;
private TextView keySwitch123, keySwitchAbc; private TextView keySwitch123, keySwitchAbc;
private TextView tvKeywords, tvKeywordsHint, tvClear, tvReDetect, tvSave; private TextView tvKeywords, tvKeywordsHint, tvClear, tvReDetect, tvSave, tvResetEmptyBackground;
private static InverseBindingListener inverseBindingListener; private static InverseBindingListener inverseBindingListener;
...@@ -146,6 +146,12 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout { ...@@ -146,6 +146,12 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout {
onKeyboardClickListener.onClickSave(); onKeyboardClickListener.onClickSave();
} }
}); });
// 重置背景
tvResetEmptyBackground.setOnClickListener(v -> {
if (onKeyboardClickListener != null){
onKeyboardClickListener.onClickResetEmptyBackground();
}
});
} }
private void initView(){ private void initView(){
...@@ -199,6 +205,7 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout { ...@@ -199,6 +205,7 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout {
tvClear = findViewById(R.id.tv_clear); tvClear = findViewById(R.id.tv_clear);
tvReDetect = findViewById(R.id.tv_re_detect); tvReDetect = findViewById(R.id.tv_re_detect);
tvSave = findViewById(R.id.tv_save); tvSave = findViewById(R.id.tv_save);
tvResetEmptyBackground = findViewById(R.id.tv_reset_empty_background);
} }
public void setKeywords(String keywords){ public void setKeywords(String keywords){
...@@ -260,6 +267,11 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout { ...@@ -260,6 +267,11 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout {
* 键盘按钮点击监听 * 键盘按钮点击监听
*/ */
public interface OnKeyboardClickListener{ public interface OnKeyboardClickListener{
/**
* 点击重置背景
*/
void onClickResetEmptyBackground();
/** /**
* 点击重新识别 * 点击重新识别
*/ */
...@@ -269,5 +281,6 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout { ...@@ -269,5 +281,6 @@ public class EnglishAndNumberKeyboard extends ConstraintLayout {
* 点击保存 * 点击保存
*/ */
void onClickSave(); void onClickSave();
} }
} }
...@@ -72,13 +72,25 @@ ...@@ -72,13 +72,25 @@
style="@style/text_base.keyboard_button.red" style="@style/text_base.keyboard_button.red"
android:text="@string/empty" android:text="@string/empty"
android:textSize="@dimen/sp_32" android:textSize="@dimen/sp_32"
app:layout_constraintEnd_toStartOf="@+id/tv_re_detect" app:layout_constraintEnd_toStartOf="@+id/tv_reset_empty_background"
app:layout_constraintStart_toEndOf="@+id/gl_v_40" app:layout_constraintStart_toEndOf="@+id/gl_v_40"
app:layout_constraintTop_toTopOf="@+id/cl_search" app:layout_constraintTop_toTopOf="@+id/cl_search"
app:layout_constraintBottom_toBottomOf="@+id/cl_search" app:layout_constraintBottom_toBottomOf="@+id/cl_search"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
/> />
<!--重设背景-->
<TextView
android:id="@+id/tv_reset_empty_background"
style="@style/text_base.keyboard_button"
android:text="@string/re_take_empty_background_photo"
android:textSize="@dimen/sp_32"
app:layout_constraintStart_toEndOf="@+id/tv_clear"
app:layout_constraintEnd_toStartOf="@+id/tv_re_detect"
app:layout_constraintTop_toTopOf="@+id/cl_search"
app:layout_constraintBottom_toBottomOf="@+id/cl_search"
app:layout_constraintHorizontal_bias="0.5"/>
<!--重新识别--> <!--重新识别-->
<TextView <TextView
android:id="@+id/tv_re_detect" android:id="@+id/tv_re_detect"
...@@ -86,7 +98,7 @@ ...@@ -86,7 +98,7 @@
android:text="@string/re_detect" android:text="@string/re_detect"
android:textSize="@dimen/sp_32" android:textSize="@dimen/sp_32"
app:layout_constraintEnd_toStartOf="@+id/tv_save" app:layout_constraintEnd_toStartOf="@+id/tv_save"
app:layout_constraintStart_toEndOf="@+id/tv_clear" app:layout_constraintStart_toEndOf="@+id/tv_reset_empty_background"
app:layout_constraintTop_toTopOf="@+id/cl_search" app:layout_constraintTop_toTopOf="@+id/cl_search"
app:layout_constraintBottom_toBottomOf="@+id/cl_search" app:layout_constraintBottom_toBottomOf="@+id/cl_search"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
......
...@@ -27,7 +27,11 @@ ...@@ -27,7 +27,11 @@
<string name="save">保存</string> <string name="save">保存</string>
<string name="camera_opening">相机启动中…</string> <string name="camera_opening">相机启动中…</string>
<string name="camera_open_error">相机启动异常</string> <string name="camera_open_error">相机启动异常</string>
<string name="please_select_correct_goods_for_every_rect">请为照片中的每个框对应正确的商品</string> <string name="camera_not_open">摄像头未启动</string>
<string name="please_select_correct_goods_for_every_rect">请为照片中的每个框指定正确的商品</string>
<string name="re_take_empty_background_photo">重置背景</string>
<string name="describe_re_take_empty_background_photo">重置前,请确保移走摄像头画面中的所有物体</string>
<string name="reset_empty_background_successful">背景设置成功</string>
<string name="key_1">1</string> <string name="key_1">1</string>
<string name="key_2">2</string> <string name="key_2">2</string>
...@@ -71,6 +75,10 @@ ...@@ -71,6 +75,10 @@
<string name="key_delete_1"></string> <string name="key_delete_1"></string>
<string name="key_delete_2">退格</string> <string name="key_delete_2">退格</string>
<string name="product_list">商品列表</string>
<string name="learning_records">学习记录</string>
<string name="switch_to_error_correction_mode">纠错模式</string>
<string name="switch_to_learning_mode">学习模式</string>
<string name="no_data">暂无数据</string> <string name="no_data">暂无数据</string>
<string name="previous_page">上一页</string> <string name="previous_page">上一页</string>
<string name="next_page">下一页</string> <string name="next_page">下一页</string>
...@@ -83,4 +91,7 @@ ...@@ -83,4 +91,7 @@
<string name="title_download_remote_tool">下载远程工具</string> <string name="title_download_remote_tool">下载远程工具</string>
<string name="confirm_download_remote_tool">请确认是否下载远程工具</string> <string name="confirm_download_remote_tool">请确认是否下载远程工具</string>
<string name="upgrade_progress">下载进度:</string> <string name="upgrade_progress">下载进度:</string>
<string name="delete_success">删除成功</string>
<string name="delete_failed_empty_product_code">删除失败,商品编码为空</string>
</resources> </resources>
\ No newline at end of file
...@@ -210,7 +210,9 @@ target_link_libraries( ...@@ -210,7 +210,9 @@ target_link_libraries(
clsretri clsretri
detfea detfea
c++_shared c++_shared
-Wl,--whole-archive
wmai wmai
-Wl,--no-whole-archive
opencv opencv
image_tools image_tools
${log-lib} ${log-lib}
...@@ -232,7 +234,11 @@ target_link_libraries( ...@@ -232,7 +234,11 @@ target_link_libraries(
clsretri_plate clsretri_plate
detfea_color detfea_color
c++_shared c++_shared
-Wl,--whole-archive
wmai wmai
-Wl,--no-whole-archive
opencv opencv
image_tools image_tools
${log-lib} ${log-lib}
......
...@@ -132,4 +132,31 @@ float calculateSimilar(const float v1[], const float v2[], int size) { ...@@ -132,4 +132,31 @@ float calculateSimilar(const float v1[], const float v2[], int size) {
} }
if (mod1 == 0 || mod2 == 0) return 0; if (mod1 == 0 || mod2 == 0) return 0;
return (ret / sqrt(mod1) / sqrt(mod2) + 1) / 2.0; return (ret / sqrt(mod1) / sqrt(mod2) + 1) / 2.0;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_wmdigit_core_hnsw_Hnsw_deleteByRowId(JNIEnv *env, jobject thiz, jlong row_id) {
int temp_row_id = -1;
if (vector.empty() || idx == nullptr){
return temp_row_id;
}
// 遍历删除对应索引
for (auto it = vector.begin(); it != vector.end();) {
if (it->first == row_id){
it = vector.erase(it);
temp_row_id = row_id;
idx->Removesample(temp_row_id);
LOGD("删除了%d", temp_row_id);
break;
}
else{
it ++;
}
}
// idx为空,删除
if (idx->get_cur_element_count() == 0){
Java_com_wmdigit_core_hnsw_Hnsw_deleteAllSample(env, thiz);
}
return temp_row_id;
} }
\ No newline at end of file
package com.wmdigit.core.catering; package com.wmdigit.core.catering;
import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import com.elvishew.xlog.XLog;
import com.wmdigit.common.model.CropValueDTO; import com.wmdigit.common.model.CropValueDTO;
import com.wmdigit.core.catering.dish.DishDetection; import com.wmdigit.core.catering.dish.DishDetection;
import com.wmdigit.core.catering.model.TargetDetectResult; import com.wmdigit.core.catering.model.TargetDetectResult;
...@@ -16,10 +18,16 @@ import com.wmdigit.data.mmkv.repository.CropLocalRepository; ...@@ -16,10 +18,16 @@ import com.wmdigit.data.mmkv.repository.CropLocalRepository;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.Completable;
import io.reactivex.CompletableObserver;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
/** /**
...@@ -36,6 +44,22 @@ public class TargetDetectionRepository { ...@@ -36,6 +44,22 @@ public class TargetDetectionRepository {
*/ */
public static final int AI_MODE_PLATE_DETECTION = 1; public static final int AI_MODE_PLATE_DETECTION = 1;
private static final Map<Integer, Class<?>> DETECTION_FACTORY = new ConcurrentHashMap<>();
static {
DETECTION_FACTORY.put(AI_MODE_DISH_DETECTION, DishDetection.class);
DETECTION_FACTORY.put(AI_MODE_PLATE_DETECTION, PlateDetection.class);
}
/**
* 目标检测算法
*/
private TargetDetection targetDetection;
/**
* 初始化完成标识
*/
private final AtomicBoolean initializeComplete = new AtomicBoolean(false);
/** /**
* 单例模式 * 单例模式
*/ */
...@@ -52,35 +76,53 @@ public class TargetDetectionRepository { ...@@ -52,35 +76,53 @@ public class TargetDetectionRepository {
} }
/** /**
* 目标检测算法 * 获取是否初始化完成
* @return
*/ */
private TargetDetection targetDetection; public boolean isInitializeComplete() {
return initializeComplete.get();
}
/** /**
* 初始化算法模式 * 初始化目标检测功能
* @param aiMode * 此方法根据当前的人工智能模式初始化相应的目标检测对象
* 如果当前目标检测对象的模式与新的模式不匹配,则先释放当前对象,再创建新的目标检测对象
* 支持的模式包括菜品检测和餐具检测
*/ */
@SuppressLint("CheckResult")
public void initTargetDetection(){ public void initTargetDetection(){
int aiMode = AiLocalRepository.getInstance().getAiMode(); try {
// 释放之前初始化的算法 // 获取当前的人工智能模式
if (targetDetection != null && targetDetection.getAiMode() != aiMode){ int aiMode = AiLocalRepository.getInstance().getAiMode();
targetDetection.close(); // 释放之前初始化的算法
targetDetection = null; if (targetDetection != null && targetDetection.getAiMode() != aiMode) {
targetDetection.close();
targetDetection = null;
}
// 根据不同的AI模式创建对应的目标检测对象
Class<?> detectionClass = DETECTION_FACTORY.get(aiMode);
if (detectionClass != null) {
targetDetection = (TargetDetection) detectionClass.getDeclaredConstructor().newInstance();
} else {
XLog.e("Unsupported AI mode: " + aiMode);
return;
}
// 使用Observable异步初始化目标检测
Observable.create(emitter -> {
initializeComplete.set(false);
targetDetection.initTargetDetection();
emitter.onNext(true);
}).observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(success -> initializeComplete.set(true), error -> {
// 初始化失败时设置标志为false并记录错误日志
initializeComplete.set(false);
XLog.e(error);
});
} catch (Exception e){
XLog.e(e);
} }
switch (aiMode){
case AI_MODE_DISH_DETECTION:
targetDetection = new DishDetection();
break;
case AI_MODE_PLATE_DETECTION:
targetDetection = new PlateDetection();
break;
default:
break;
}
assert targetDetection != null;
targetDetection.initTargetDetection();
} }
/** /**
...@@ -129,6 +171,7 @@ public class TargetDetectionRepository { ...@@ -129,6 +171,7 @@ public class TargetDetectionRepository {
finalCanvas.drawBitmap(bitmap, left, top, new Paint()); finalCanvas.drawBitmap(bitmap, left, top, new Paint());
// 使用目标检测模型对处理后的图片进行推理 // 使用目标检测模型对处理后的图片进行推理
TargetDetectResult result = targetDetection.processImage(background); TargetDetectResult result = targetDetection.processImage(background);
background.recycle();
// 如果检测到目标并且存在特征向量,则返回第一个特征向量 // 如果检测到目标并且存在特征向量,则返回第一个特征向量
if (result != null && result.getFeatures() != null && result.getFeatures().length > 0){ if (result != null && result.getFeatures() != null && result.getFeatures().length > 0){
return result.getFeatures()[0]; return result.getFeatures()[0];
......
...@@ -41,6 +41,13 @@ public class Hnsw { ...@@ -41,6 +41,13 @@ public class Hnsw {
*/ */
private native void deleteAllSample(); private native void deleteAllSample();
/**
* 删除指定索引
* @param rowId
* @return
*/
private native int deleteByRowId(long rowId);
/** /**
* 同步锁 * 同步锁
*/ */
...@@ -78,6 +85,15 @@ public class Hnsw { ...@@ -78,6 +85,15 @@ public class Hnsw {
} }
} }
/**
* 删除指定特征ID
* @param featureId
* @return
*/
public int delSampleByFeatureId(long featureId){
return deleteByRowId(featureId);
}
public void deleteAll(){ public void deleteAll(){
synchronized (syncLock){ synchronized (syncLock){
deleteAllSample(); deleteAllSample();
......
...@@ -3,7 +3,6 @@ package com.wmdigit.core.hnsw; ...@@ -3,7 +3,6 @@ package com.wmdigit.core.hnsw;
import android.text.TextUtils; import android.text.TextUtils;
import com.elvishew.xlog.XLog; import com.elvishew.xlog.XLog;
import com.wmdigit.core.catering.TargetDetectionRepository;
import com.wmdigit.data.database.entity.FeaturesPO; import com.wmdigit.data.database.entity.FeaturesPO;
import com.wmdigit.data.database.entity.ProductsPO; import com.wmdigit.data.database.entity.ProductsPO;
import com.wmdigit.data.database.repository.FeaturesRepository; import com.wmdigit.data.database.repository.FeaturesRepository;
...@@ -15,7 +14,6 @@ import io.reactivex.Observable; ...@@ -15,7 +14,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
/** /**
...@@ -209,6 +207,17 @@ public class HnswRepository { ...@@ -209,6 +207,17 @@ public class HnswRepository {
} }
} }
/**
* 根据特征向量删除样本
* @param featureId
*/
public void deleteByFeatureId(long featureId){
if (!initComplete){
return;
}
hnsw.delSampleByFeatureId(featureId);
}
/** /**
* 检查是否初始化完成 * 检查是否初始化完成
* @return 如果初始化完成返回true,否则返回false * @return 如果初始化完成返回true,否则返回false
......
...@@ -126,6 +126,8 @@ public class VideoPipeRepository { ...@@ -126,6 +126,8 @@ public class VideoPipeRepository {
CropValueDTO cropValueDTO = CropLocalRepository.getInstance().getCropValue(); CropValueDTO cropValueDTO = CropLocalRepository.getInstance().getCropValue();
Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, cropValueDTO.getLeft(), cropValueDTO.getTop(), cropValueDTO.getWidth(), cropValueDTO.getHeight()); Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, cropValueDTO.getLeft(), cropValueDTO.getTop(), cropValueDTO.getWidth(), cropValueDTO.getHeight());
feedFrame(croppedBitmap); feedFrame(croppedBitmap);
croppedBitmap.recycle();
croppedBitmap = null;
} }
else { else {
feedFrame(bitmap); feedFrame(bitmap);
......
...@@ -76,4 +76,20 @@ public interface FeaturesDao { ...@@ -76,4 +76,20 @@ public interface FeaturesDao {
@Query("SELECT COUNT(*) FROM Features") @Query("SELECT COUNT(*) FROM Features")
int getCount(); int getCount();
/**
* 根据文件地址查询
* @param imgPath
* @return
*/
@Query("SELECT * FROM Features WHERE imgPath = :imgPath")
List<FeaturesPO> queryByImgPath(String imgPath);
/**
* 根据商品CODE查询
* @param productCode
* @return
*/
@Query("SELECT * FROM Features WHERE productCode = :productCode")
List<FeaturesPO> queryByProductCode(String productCode);
} }
...@@ -5,6 +5,7 @@ import androidx.room.Insert; ...@@ -5,6 +5,7 @@ import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import com.wmdigit.data.database.entity.ProductAndImgPathsPO;
import com.wmdigit.data.database.entity.ProductsPO; import com.wmdigit.data.database.entity.ProductsPO;
import java.util.List; import java.util.List;
...@@ -57,6 +58,21 @@ public interface ProductsDao { ...@@ -57,6 +58,21 @@ public interface ProductsDao {
@Query("SELECT * FROM Products WHERE productMnemonicCode LIKE :keywords OR productCode LIKE :keywords LIMIT :offset, :limit") @Query("SELECT * FROM Products WHERE productMnemonicCode LIKE :keywords OR productCode LIKE :keywords LIMIT :offset, :limit")
List<ProductsPO> getProductsByKeywords(String keywords, int offset, int limit); List<ProductsPO> getProductsByKeywords(String keywords, int offset, int limit);
/**
* 根据关键词获取产品及其图片路径列表
* 此查询通过联合Products和Features表,根据产品代码进行匹配,
* 并根据关键词(产品助记码或产品代码)进行搜索,最后对结果进行分组和分页
*
* @param keywords 搜索关键词,可以是产品助记码或产品代码
* @param offset 查询结果的起始偏移量,用于分页
* @param limit 每页查询结果的数量
* @return 包含产品及其图片路径的列表
*/
@Query("SELECT a.*, GROUP_CONCAT( b.imgPath ) AS imgPaths FROM Products a JOIN Features b ON a.productCode = b.productCode " +
"WHERE productMnemonicCode LIKE :keywords OR a.productCode LIKE :keywords " +
"GROUP BY a.productCode LIMIT :offset, :limit")
List<ProductAndImgPathsPO> getProductAndImgPathsByKeywords(String keywords, int offset, int limit);
/** /**
* 根据商品编码查询 * 根据商品编码查询
* @param productCode * @param productCode
......
package com.wmdigit.data.database.entity;
/**
* 商品信息和对应的图片地址
* @author dizi
*/
public class ProductAndImgPathsPO {
private long id;
/**
* 商品名称
*/
private String productName;
/**
* 商品编码
*/
private String productCode;
/**
* 助记码(中文名拼音缩写)
*/
private String productMnemonicCode;
/**
* 单价
*/
private String unitPrice;
/**
* 在售状态,1:在售 0:下架
*/
private int onSale;
/**
* 所有图片的路径,以逗号分隔
*/
private String imgPaths;
public ProductAndImgPathsPO(long id, String productName, String productCode, String productMnemonicCode, String unitPrice, int onSale, String imgPaths) {
this.id = id;
this.productName = productName;
this.productCode = productCode;
this.productMnemonicCode = productMnemonicCode;
this.unitPrice = unitPrice;
this.onSale = onSale;
this.imgPaths = imgPaths;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public String getProductMnemonicCode() {
return productMnemonicCode;
}
public void setProductMnemonicCode(String productMnemonicCode) {
this.productMnemonicCode = productMnemonicCode;
}
public String getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(String unitPrice) {
this.unitPrice = unitPrice;
}
public int getOnSale() {
return onSale;
}
public void setOnSale(int onSale) {
this.onSale = onSale;
}
public String getImgPaths() {
return imgPaths;
}
public void setImgPaths(String imgPaths) {
this.imgPaths = imgPaths;
}
}
package com.wmdigit.data.database.mapper; package com.wmdigit.data.database.mapper;
import com.wmdigit.data.database.entity.ExportFeaturesPO; import com.wmdigit.data.database.entity.ExportFeaturesPO;
import com.wmdigit.data.database.entity.FeaturesPO; import com.wmdigit.data.database.entity.FeaturesPO;
...@@ -48,6 +49,5 @@ public interface FeaturesMapper { ...@@ -48,6 +49,5 @@ public interface FeaturesMapper {
*/ */
List<FeaturesPO> toFeaturesPOList(List<ExportFeaturesPO> list); List<FeaturesPO> toFeaturesPOList(List<ExportFeaturesPO> list);
} }
package com.wmdigit.data.database.mapper; package com.wmdigit.data.database.mapper;
import android.text.TextUtils;
import com.wmdigit.common.model.ProductsDTO; import com.wmdigit.common.model.ProductsDTO;
import com.wmdigit.common.model.ProductsVO; import com.wmdigit.common.model.ProductsVO;
import com.wmdigit.data.database.entity.ProductAndImgPathsPO;
import com.wmdigit.data.database.entity.ProductsPO; import com.wmdigit.data.database.entity.ProductsPO;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
...@@ -29,5 +36,29 @@ public interface ProductsMapper { ...@@ -29,5 +36,29 @@ public interface ProductsMapper {
List<ProductsVO> toVoList(List<ProductsPO> poList); List<ProductsVO> toVoList(List<ProductsPO> poList);
/**
* 将包含多个图片路径的字符串转换为图片路径数组
* 此方法用于处理以逗号分隔的图片路径字符串,将其转换为字符串数组,以便于进一步处理或显示图片
*
* @param imgPaths 以逗号分隔的图片路径字符串
* @return 图片路径字符串数组如果输入字符串为空或null,则返回null
*/
@Named("convertImgPathsToImgPathArray")
default List<String> convertImgPathsToImgPathArray(String imgPaths){
// 检查输入的图片路径字符串是否为空或null
if (TextUtils.isEmpty(imgPaths)){
// 如果为空或null,则返回null
return null;
}
// 使用逗号分割输入字符串,返回图片路径字符串数组
String[] array = imgPaths.split(",");
return new ArrayList<>(Arrays.asList(array));
}
@Mapping(source="imgPaths", target = "imgPathList", qualifiedByName = "convertImgPathsToImgPathArray")
ProductsVO toVoWithImgPaths(ProductAndImgPathsPO po);
List<ProductsVO> toVoListWithImgPaths(List<ProductAndImgPathsPO> productAndImgPathsPoList);
} }
...@@ -2,6 +2,8 @@ package com.wmdigit.data.database.repository; ...@@ -2,6 +2,8 @@ package com.wmdigit.data.database.repository;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.elvishew.xlog.XLog; import com.elvishew.xlog.XLog;
import com.wmdigit.data.database.AppDatabase; import com.wmdigit.data.database.AppDatabase;
import com.wmdigit.data.database.dao.FeaturesDao; import com.wmdigit.data.database.dao.FeaturesDao;
...@@ -111,4 +113,29 @@ public class FeaturesRepository { ...@@ -111,4 +113,29 @@ public class FeaturesRepository {
public int getCount(){ public int getCount(){
return getFeaturesDao().getCount(); return getFeaturesDao().getCount();
} }
/**
* 根据地址删除,返回被删除的特征ID
* @param path
* @return
*/
public long deleteByPath(String path){
List<FeaturesPO> list = getFeaturesDao().queryByImgPath(path);
if (list == null || list.size() == 0){
return -1;
}
else{
getFeaturesDao().deleteById(list.get(0).getId());
return list.get(0).getId();
}
}
/**
* 根据商品CODE查询
* @param productCode
* @return
*/
public List<FeaturesPO> queryByProductCode(@NonNull String productCode){
return getFeaturesDao().queryByProductCode(productCode);
}
} }
...@@ -4,6 +4,7 @@ import com.wmdigit.common.model.ProductsDTO; ...@@ -4,6 +4,7 @@ import com.wmdigit.common.model.ProductsDTO;
import com.wmdigit.common.model.ProductsVO; import com.wmdigit.common.model.ProductsVO;
import com.wmdigit.data.database.AppDatabase; import com.wmdigit.data.database.AppDatabase;
import com.wmdigit.data.database.dao.ProductsDao; import com.wmdigit.data.database.dao.ProductsDao;
import com.wmdigit.data.database.entity.ProductAndImgPathsPO;
import com.wmdigit.data.database.entity.ProductsPO; import com.wmdigit.data.database.entity.ProductsPO;
import com.wmdigit.data.database.mapper.ProductsMapper; import com.wmdigit.data.database.mapper.ProductsMapper;
...@@ -92,6 +93,15 @@ public class ProductsRepository { ...@@ -92,6 +93,15 @@ public class ProductsRepository {
return getProductsDao().getByProductCode(productCode); return getProductsDao().getByProductCode(productCode);
} }
public List<ProductsVO> queryProductsAndImgPathsByKeywords(String keywords, int page, int pageSize){
int offset = (page - 1) * pageSize;
List<ProductAndImgPathsPO> list = getProductsDao().getProductAndImgPathsByKeywords("%" + keywords + "%", offset, pageSize);
if (list == null){
list = new ArrayList<>();
}
return ProductsMapper.INSTANCE.toVoListWithImgPaths(list);
}
/** /**
* 删除全部 * 删除全部
*/ */
......
...@@ -24,6 +24,9 @@ v1.0.2.1 2025/04/22 1.特征表增加字段记录图片地址,数据库版本 ...@@ -24,6 +24,9 @@ v1.0.2.1 2025/04/22 1.特征表增加字段记录图片地址,数据库版本
v1.0.2.2 2025/04/25 1.UvcCamera从mjpeg改为yuv v1.0.2.2 2025/04/25 1.UvcCamera从mjpeg改为yuv
2.增加推理时的日志 2.增加推理时的日志
3.修改索引库默认阈值为0.77 3.修改索引库默认阈值为0.77
v1.0.2.3 2025/04/25 1.todo 增加GPU推理
2.增加纠错功能
3.学习页增加重置背景功能
todo 增加学习记录管理模块 todo 增加学习记录管理模块
......
...@@ -9,11 +9,9 @@ import androidx.recyclerview.widget.RecyclerView; ...@@ -9,11 +9,9 @@ import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.facade.annotation.Route;
import com.jiangdg.uvc.USBCameraHelper; import com.jiangdg.uvc.USBCameraHelper;
import com.wmdigit.camera.CameraxController;
import com.wmdigit.common.base.mvvm.BaseMvvmNaviDrawerActivity; import com.wmdigit.common.base.mvvm.BaseMvvmNaviDrawerActivity;
import com.wmdigit.common.constants.RouteConstant; import com.wmdigit.common.constants.RouteConstant;
import com.wmdigit.common.model.DrawerMenuItemVO; import com.wmdigit.common.model.DrawerMenuItemVO;
import com.wmdigit.data.mmkv.repository.CameraLocalRepository;
import com.wmdigit.data.mmkv.repository.CropLocalRepository; import com.wmdigit.data.mmkv.repository.CropLocalRepository;
import com.wmdigit.setting.databinding.ActivitySettingBinding; import com.wmdigit.setting.databinding.ActivitySettingBinding;
import com.wmdigit.setting.viewmodel.SettingViewModel; import com.wmdigit.setting.viewmodel.SettingViewModel;
...@@ -49,6 +47,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel ...@@ -49,6 +47,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel
@Override @Override
protected void initData() { protected void initData() {
mDataBinding.setViewModel(mViewModel);
// 测试数据 // 测试数据
// List<ProductsDTO> list = new ArrayList<>(); // List<ProductsDTO> list = new ArrayList<>();
// list.add(new ProductsDTO("西红柿炒鸡蛋","000323", "XHSCJD", "12.80", 1)); // list.add(new ProductsDTO("西红柿炒鸡蛋","000323", "XHSCJD", "12.80", 1));
...@@ -66,7 +65,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel ...@@ -66,7 +65,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel
public void initObserve() { public void initObserve() {
super.initObserve(); super.initObserve();
// 观察页码位置 // 观察页码位置
mViewModel.pagePosition.observe(this, position -> { mViewModel.getPagePosition().observe(this, position -> {
if (position == 3){ if (position == 3){
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
// 关闭抽屉 // 关闭抽屉
...@@ -148,7 +147,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel ...@@ -148,7 +147,7 @@ public class SettingActivity extends BaseMvvmNaviDrawerActivity<SettingViewModel
@Override @Override
protected void onDrawerMenuItemClickListener(int position) { protected void onDrawerMenuItemClickListener(int position) {
if (position == mViewModel.pagePosition.getValue()) { if (position == mViewModel.getPagePosition().getValue()) {
return; return;
} }
if (FRAGMENTS_NAVI_IDS[position] == R.id.dataLearningFragment){ if (FRAGMENTS_NAVI_IDS[position] == R.id.dataLearningFragment){
......
...@@ -18,19 +18,19 @@ import java.util.List; ...@@ -18,19 +18,19 @@ import java.util.List;
* 数据管理页功能按钮列表 适配器 * 数据管理页功能按钮列表 适配器
* @author dizi * @author dizi
*/ */
public class FuncButtonAdapter extends BaseRecyclerViewAdapter<FuncButtonAdapter.ViewHolder, FuncButton> { public class FuncButtonAdapter extends BaseRecyclerViewAdapter<FuncButton> {
public FuncButtonAdapter(Context mContext, List<FuncButton> mList) { public FuncButtonAdapter(Context mContext, List<FuncButton> mList) {
super(mContext, mList); super(mContext, mList);
} }
@Override @Override
protected int getItemLayoutResId() { protected int getLayoutResIdByViewType(int viewType) {
return R.layout.layout_data_manager_item; return R.layout.layout_data_manager_item;
} }
@Override @Override
protected ViewHolder createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener) { protected ViewHolder createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener, int viewType) {
return new ViewHolder(itemView, onItemClickListener); return new ViewHolder(itemView, onItemClickListener);
} }
......
package com.wmdigit.setting.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.squareup.picasso.Picasso;
import com.wmdigit.common.base.adapter.BaseDiffRecyclerViewAdapter;
import com.wmdigit.common.base.viewholder.BaseViewHolder;
import com.wmdigit.common.view.imageview.RoundedCornersTransformation;
import com.wmdigit.setting.R;
import com.wmdigit.setting.adapter.diff.LocalImageDiffUtil;
import com.wmdigit.setting.model.LocalImage;
import java.io.File;
import java.util.List;
/**
* 本地图片适配器
* @author dizi
*/
public class LocalImageAdapter extends BaseDiffRecyclerViewAdapter<LocalImage, LocalImageDiffUtil> {
public LocalImageAdapter(Context mContext, List<LocalImage> mList) {
super(mContext, mList);
}
@Override
protected LocalImageDiffUtil createDiffUtil(List<LocalImage> oldList, List<LocalImage> newList) {
return new LocalImageDiffUtil(oldList, newList);
}
@Override
protected int getLayoutResIdByViewType(int viewType) {
return R.layout.layout_local_image_item;
}
@Override
protected BaseViewHolder<LocalImage> createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener, int viewType) {
return new LocalImageViewHolder(itemView, onItemClickListener);
}
public static class LocalImageViewHolder extends BaseViewHolder<LocalImage>{
private ImageView img;
public LocalImageViewHolder(@NonNull View itemView, OnItemClickListener itemListener) {
super(itemView, itemListener);
img = itemView.findViewById(R.id.img_local_image);
}
@Override
public void bind(LocalImage item) {
Picasso.get()
.load(new File(item.getPath()))
.transform(new RoundedCornersTransformation(10))
.config(Bitmap.Config.RGB_565)
.into(this.img);
}
}
}
...@@ -3,21 +3,32 @@ package com.wmdigit.setting.adapter; ...@@ -3,21 +3,32 @@ package com.wmdigit.setting.adapter;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.wmdigit.common.base.adapter.BaseDiffRecyclerViewAdapter; import com.wmdigit.common.base.adapter.BaseDiffRecyclerViewAdapter;
import com.wmdigit.common.base.viewholder.BaseViewHolder; import com.wmdigit.common.base.viewholder.BaseViewHolder;
import com.wmdigit.common.model.ProductsVO; import com.wmdigit.common.model.ProductsVO;
import com.wmdigit.setting.R; import com.wmdigit.setting.R;
import com.wmdigit.setting.adapter.diff.ProductsDiffUtil; import com.wmdigit.setting.adapter.diff.ProductsDiffUtil;
import com.wmdigit.setting.model.DelAllFeaturesByProductCodeMessage;
import com.wmdigit.setting.model.LocalImage;
import com.wmdigit.setting.model.DelOneFeatureByPathMessage;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 商品列表适配器 * 商品列表适配器
* @author dizi * @author dizi
*/ */
public class ProductsAdapter extends BaseDiffRecyclerViewAdapter<ProductsAdapter.ViewHolder, ProductsVO, ProductsDiffUtil> { public class ProductsAdapter extends BaseDiffRecyclerViewAdapter<ProductsVO, ProductsDiffUtil> {
public ProductsAdapter(Context mContext, List<ProductsVO> mList) { public ProductsAdapter(Context mContext, List<ProductsVO> mList) {
super(mContext, mList); super(mContext, mList);
...@@ -29,37 +40,143 @@ public class ProductsAdapter extends BaseDiffRecyclerViewAdapter<ProductsAdapter ...@@ -29,37 +40,143 @@ public class ProductsAdapter extends BaseDiffRecyclerViewAdapter<ProductsAdapter
} }
@Override @Override
protected int getItemLayoutResId() { public int getItemViewType(int position) {
return R.layout.layout_product_item; return mList.get(position).getLayoutType();
}
@Override
protected int getLayoutResIdByViewType(int viewType) {
if (viewType == 0){
return R.layout.layout_product_item;
}
else {
return R.layout.layout_learning_correction_item;
}
} }
@Override @Override
protected ViewHolder createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener) { protected BaseViewHolder<ProductsVO> createViewHolder(View itemView, BaseViewHolder.OnItemClickListener onItemClickListener, int viewType) {
return new ViewHolder(itemView, onItemClickListener); if (viewType == 0){
return new ProductsViewHolder(itemView, onItemClickListener);
}
else{
return new LearningRecordsViewHolder(itemView, onItemClickListener);
}
} }
public static class ViewHolder extends BaseViewHolder<ProductsVO>{ /**
* 学习记录视图持有者类,用于显示产品学习记录的详细信息
* 继承自BaseViewHolder,用于管理产品视图的展示
*/
public static class LearningRecordsViewHolder extends BaseViewHolder<ProductsVO>{
// 用于显示产品名称的文本视图
private TextView tvProductName;
// 用于显示产品编码的文本视图
private TextView tvProductCode;
// 删除学习数据的按钮
private Button btnDeleteLearningData;
// 显示本地图片的循环视图
private RecyclerView recyclerView;
/**
* 构造函数,初始化视图组件
*
* @param itemView 视图项
* @param itemListener 项点击监听器
*/
public LearningRecordsViewHolder(@NonNull View itemView, OnItemClickListener itemListener) {
super(itemView, null);
// 初始化视图组件
tvProductName = itemView.findViewById(R.id.tv_product_name2);
tvProductCode = itemView.findViewById(R.id.tv_product_code2);
btnDeleteLearningData = itemView.findViewById(R.id.btn_del_all_learning_data);
recyclerView = itemView.findViewById(R.id.rv_local_images);
// 设置RecyclerView的布局管理器,使用8列的网格布局
recyclerView.setLayoutManager(new GridLayoutManager(itemView.getContext(), 8));
}
/**
* 绑定产品数据到视图
*
* @param item 产品数据对象
*/
@Override
public void bind(ProductsVO item) {
// 设置产品名称和编码到对应的文本视图
tvProductName.setText(item.getProductName());
tvProductCode.setText(item.getProductCode());
// 创建本地图片列表
List<LocalImage> list = new ArrayList<>();
// 遍历产品图片路径列表,创建本地图片对象,并添加到列表中
for (String path: item.getImgPathList()){
list.add(new LocalImage(path));
}
// 创建并设置本地图片适配器到RecyclerView
LocalImageAdapter adapter = new LocalImageAdapter(itemView.getContext(), list);
recyclerView.setAdapter(adapter);
// 图片数量为空时,隐藏删除图标
if (list.size() == 0){
btnDeleteLearningData.setVisibility(View.INVISIBLE);
}
else{
btnDeleteLearningData.setVisibility(View.VISIBLE);
}
// 设置适配器的项点击监听器,用于删除单个特征
adapter.setOnItemClickListener(position -> EventBus.getDefault().post(new DelOneFeatureByPathMessage(list.get(position).getPath())));
// 设置删除学习数据按钮的点击监听器,用于删除所有特征
btnDeleteLearningData.setOnClickListener(v->{
EventBus.getDefault().post(new DelAllFeaturesByProductCodeMessage(item.getProductCode()));
});
}
}
/**
* ProductsViewHolder 类是用于在 RecyclerView 中显示产品信息的视图持有者类
* 它继承自 BaseViewHolder,并专门用于绑定和显示 ProductsVO 类型的数据
*/
public static class ProductsViewHolder extends BaseViewHolder<ProductsVO>{
// 定义用于显示产品名称、代码和价格的 TextView 变量
private TextView tvProductName, tvProductCode, tvProductPrice; private TextView tvProductName, tvProductCode, tvProductPrice;
public ViewHolder(@NonNull View itemView, OnItemClickListener itemListener) { /**
* 构造函数用于初始化 ProductsViewHolder
* 它调用父类的构造函数,并初始化产品信息的 TextView
*
* @param itemView 视图项,用于 findViewById 来查找视图
* @param itemListener 项点击监听器,用于处理点击事件
*/
public ProductsViewHolder(@NonNull View itemView, OnItemClickListener itemListener) {
super(itemView, itemListener); super(itemView, itemListener);
// 初始化显示产品信息的 TextView
tvProductName = itemView.findViewById(R.id.tv_product_name); tvProductName = itemView.findViewById(R.id.tv_product_name);
tvProductCode = itemView.findViewById(R.id.tv_product_code); tvProductCode = itemView.findViewById(R.id.tv_product_code);
tvProductPrice = itemView.findViewById(R.id.tv_product_price); tvProductPrice = itemView.findViewById(R.id.tv_product_price);
} }
/**
* bind 方法用于将产品数据绑定到视图上
* 它将产品名称、代码和价格设置到对应的 TextView 上
*
* @param item ProductsVO 类型的产品数据对象,包含产品信息
*/
@Override @Override
public void bind(ProductsVO item) { public void bind(ProductsVO item) {
// 设置产品名称、代码和价格到对应的 TextView
tvProductName.setText(item.getProductName()); tvProductName.setText(item.getProductName());
tvProductCode.setText(item.getProductCode()); tvProductCode.setText(item.getProductCode());
// 产品价格前添加货币符号
tvProductPrice.setText("¥" + item.getUnitPrice()); tvProductPrice.setText("¥" + item.getUnitPrice());
if (item.isChecked()){
}
else{
}
} }
} }
} }
package com.wmdigit.setting.adapter.diff;
import com.wmdigit.common.base.diffutil.BaseDiffUtil;
import com.wmdigit.setting.model.LocalImage;
import java.util.List;
public class LocalImageDiffUtil extends BaseDiffUtil<LocalImage> {
public LocalImageDiffUtil(List<LocalImage> oldList, List<LocalImage> newList) {
super(oldList, newList);
}
@Override
protected boolean areItemsTheSame(LocalImage oldItem, LocalImage newItem) {
if (!oldItem.getPath().equals(newItem.getPath())){
return false;
}
return true;
}
@Override
protected boolean checkContentsTheSame(LocalImage oldItem, LocalImage newItem) {
if (!oldItem.getPath().equals(newItem.getPath())){
return false;
}
return true;
}
}
...@@ -16,7 +16,27 @@ public class ProductsDiffUtil extends BaseDiffUtil<ProductsVO> { ...@@ -16,7 +16,27 @@ public class ProductsDiffUtil extends BaseDiffUtil<ProductsVO> {
@Override @Override
protected boolean areItemsTheSame(ProductsVO oldItem, ProductsVO newItem) { protected boolean areItemsTheSame(ProductsVO oldItem, ProductsVO newItem) {
return oldItem.getProductCode().equals(newItem.getProductCode()); if (!oldItem.getProductCode().equals(newItem.getProductCode())){
return false;
}
if (oldItem.getLayoutType() != newItem.getLayoutType()){
return false;
}
// 判断图片列表
if (oldItem.getImgPathList() != null && newItem.getImgPathList() !=null){
if (oldItem.getImgPathList().size() != newItem.getImgPathList().size()){
return false;
}
}
else if (oldItem.getImgPathList() == null && newItem.getImgPathList() != null){
return false;
}
else if (oldItem.getImgPathList() != null && newItem.getImgPathList() == null){
return false;
}
else{
}
return true;
} }
@Override @Override
......
package com.wmdigit.setting.fragment; package com.wmdigit.setting.fragment;
import android.annotation.SuppressLint;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.elvishew.xlog.XLog;
import com.jiangdg.uvc.USBCameraHelper; import com.jiangdg.uvc.USBCameraHelper;
import com.wmdigit.camera.CameraxController;
import com.wmdigit.common.base.mvvm.BaseMvvmFragment; import com.wmdigit.common.base.mvvm.BaseMvvmFragment;
import com.wmdigit.common.view.keyboard.EnglishAndNumberKeyboard; import com.wmdigit.common.view.keyboard.EnglishAndNumberKeyboard;
import com.wmdigit.core.videopipe.VideoPipeRepository;
import com.wmdigit.setting.R; import com.wmdigit.setting.R;
import com.wmdigit.setting.adapter.ProductsAdapter; import com.wmdigit.setting.adapter.ProductsAdapter;
import com.wmdigit.setting.databinding.FragmentDataLearningBinding; import com.wmdigit.setting.databinding.FragmentDataLearningBinding;
import com.wmdigit.setting.model.DelAllFeaturesByProductCodeMessage;
import com.wmdigit.setting.model.DelOneFeatureByPathMessage;
import com.wmdigit.setting.viewmodel.DataLearningViewModel; import com.wmdigit.setting.viewmodel.DataLearningViewModel;
import com.wmdigit.setting.viewmodel.SettingViewModel;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/** /**
...@@ -25,8 +29,16 @@ import com.wmdigit.setting.viewmodel.DataLearningViewModel; ...@@ -25,8 +29,16 @@ import com.wmdigit.setting.viewmodel.DataLearningViewModel;
*/ */
public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel, FragmentDataLearningBinding> { public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel, FragmentDataLearningBinding> {
/**
* 商品列表适配器
*/
private ProductsAdapter productsAdapter; private ProductsAdapter productsAdapter;
/**
* activity的ViewModel
*/
private SettingViewModel settingViewModel;
@Override @Override
protected int getLayoutId() { protected int getLayoutId() {
return R.layout.fragment_data_learning; return R.layout.fragment_data_learning;
...@@ -35,6 +47,8 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel ...@@ -35,6 +47,8 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel
@Override @Override
protected void initObserve() { protected void initObserve() {
super.initObserve(); super.initObserve();
// 从Activity中获取共享的SettingViewModel实例
settingViewModel = getSharedViewModelFromActivity(SettingViewModel.class);
// 观察关键字变化 // 观察关键字变化
mViewModel.keywords.observe(this, keyword -> { mViewModel.keywords.observe(this, keyword -> {
mViewModel.onKeywordsChangedEvent(keyword); mViewModel.onKeywordsChangedEvent(keyword);
...@@ -42,31 +56,77 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel ...@@ -42,31 +56,77 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel
}); });
// 观察页码变化 // 观察页码变化
mViewModel.currentPage.observe(this, page -> { mViewModel.currentPage.observe(this, page -> {
mViewModel.onPageChangedEvent(page); if (!mViewModel.currentPage.getValue().equals(page)) {
mViewModel.onPageChangedEvent(page);
productsAdapter.diffAndUpdate();
}
});
// 观察学习模式
settingViewModel.getModeOnLearningPage().observe(this, mode->{
if (mViewModel.getLearningMode() != mode) {
mViewModel.onSwitchLearningMode(mode);
initRecyclerView(mode);
productsAdapter.diffAndUpdate();
}
});
//
mViewModel.getDataSourceChanged().observe(this, b->{
productsAdapter.diffAndUpdate(); productsAdapter.diffAndUpdate();
}); });
} }
@Override @Override
protected void initView() { protected void initView() {
mDataBinding.rvProducts.setLayoutManager(new GridLayoutManager(requireContext(), 4)); initRecyclerView(0);
}
/**
* 初始化列表
* @param mode
*/
private void initRecyclerView(int mode){
if (mode == 0) {
// 学习模式,水平布局列表
mDataBinding.rvProducts.setLayoutManager(new GridLayoutManager(requireContext(), 4));
}
else{
// 纠错模式,垂直列表
mDataBinding.rvProducts.setLayoutManager(new LinearLayoutManager(requireContext()));
}
} }
/**
* 初始化数据方法
* 该方法用于在当前Fragment中初始化所需的数据和视图模型
*/
@Override @Override
protected void initData() { protected void initData() {
// 设置ViewModel,以便数据绑定
mDataBinding.setViewModel(mViewModel); mDataBinding.setViewModel(mViewModel);
productsAdapter = new ProductsAdapter(requireContext(), mViewModel.products);
// 设置Activity级别的ViewModel,用于跨Fragment通信
mDataBinding.setActivityViewModel(settingViewModel);
// 创建并设置产品适配器
productsAdapter = new ProductsAdapter(requireContext(), mViewModel.getProducts());
mDataBinding.rvProducts.setAdapter(productsAdapter); mDataBinding.rvProducts.setAdapter(productsAdapter);
} }
@SuppressLint("ClickableViewAccessibility")
@Override @Override
protected void initListener() { protected void initListener() {
// 注册商品列表Item点击事件 // 注册商品列表Item点击事件
productsAdapter.setOnItemClickListener(position -> { productsAdapter.setOnItemClickListener(position -> {
mViewModel.feedback(mViewModel.products.get(position)); mViewModel.feedback(mViewModel.getProducts().get(position));
}); });
// 注册键盘按钮点击事件 // 注册键盘按钮点击事件
mDataBinding.keyboard.setOnKeyboardClickListener(new EnglishAndNumberKeyboard.OnKeyboardClickListener() { mDataBinding.keyboard.setOnKeyboardClickListener(new EnglishAndNumberKeyboard.OnKeyboardClickListener() {
@Override
public void onClickResetEmptyBackground() {
// 重置背景
showResetEmptyBackgroundDialog();
}
@Override @Override
public void onClickReDetect() { public void onClickReDetect() {
// 重新识别 // 重新识别
...@@ -88,6 +148,17 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel ...@@ -88,6 +148,17 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel
}); });
} }
/**
* 展示重置空盘确认框
*/
private void showResetEmptyBackgroundDialog(){
mSecondConfirmWindow
.setTitle(getString(com.wmdigit.common.R.string.prompt))
.setContent(getString(com.wmdigit.common.R.string.describe_re_take_empty_background_photo))
.setClickListener(()-> mViewModel.setEmptyBackgroundForVideoPipe())
.showWindow();
}
@Override @Override
protected Class<DataLearningViewModel> getViewModel() { protected Class<DataLearningViewModel> getViewModel() {
return DataLearningViewModel.class; return DataLearningViewModel.class;
...@@ -102,20 +173,53 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel ...@@ -102,20 +173,53 @@ public class DataLearningFragment extends BaseMvvmFragment<DataLearningViewModel
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
EventBus.getDefault().register(this);
// 注册相机回调 // 注册相机回调
/*CameraxController.getInstance(requireContext())
.setFrameInterval(2)
.setOnImageAnalyzeListener(mViewModel.getOnImageAnalyzeListener());*/
USBCameraHelper.getInstance(requireContext()) USBCameraHelper.getInstance(requireContext())
.setOnImageAnalyzeListener(mViewModel.getOnImageAnalyzeListener()); .setOnImageAnalyzeListener(mViewModel.getOnImageAnalyzeListener());
mViewModel.openVideoPipe(); mViewModel.openVideoPipe();
} }
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
// CameraxController.getInstance().setOnImageAnalyzeListener(null); EventBus.getDefault().unregister(this);
USBCameraHelper.getInstance(requireContext()).setOnImageAnalyzeListener(null); USBCameraHelper.getInstance(requireContext()).setOnImageAnalyzeListener(null);
mViewModel.closeVideoPipe(); mViewModel.closeVideoPipe();
} }
/**
* 当接收到删除特定路径的特征信息消息时执行的操作
* 此方法在主线程中调用,显示一个确认窗口,以确保用户确实希望删除指定的特征和图片
*
* @param message 包含待删除特征路径信息的消息对象
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(DelOneFeatureByPathMessage message){
// 设置确认窗口的标题、内容和点击事件处理程序,然后显示窗口
mSecondConfirmWindow.setTitle(getString(com.wmdigit.common.R.string.prompt))
.setContent(getString(R.string.confirm_del_photo))
.setClickListener(() -> {
// 用户确认后,调用ViewModel中的方法删除指定特征和图片
mViewModel.deleteFeatureAndImage(message.getPayload());
}).showWindow();
}
/**
* 当接收到删除指定产品代码的所有特征信息消息时执行的操作
* 此方法在主线程中调用,显示一个确认窗口,以确保用户确实希望删除该产品所有的特征和图片
*
* @param message 包含待删除产品代码信息的消息对象
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(DelAllFeaturesByProductCodeMessage message){
// 设置确认窗口的标题、内容和点击事件处理程序,然后显示窗口
mSecondConfirmWindow.setTitle(getString(com.wmdigit.common.R.string.prompt))
.setContent(getString(R.string.confirm_del_all_photos_of_this_product))
.setClickListener(()->{
// 用户确认后,调用ViewModel中的方法删除指定产品所有的特征和图片
mViewModel.deleteAllFeaturesAndImageOfProduct(message.getPayload());
}).showWindow();
}
} }
\ No newline at end of file
package com.wmdigit.setting.model;
import com.wmdigit.common.base.model.BaseMessage;
import com.wmdigit.common.enums.MessageType;
/**
* 继承自 BaseMessage 的 DelAllFeaturesByProductCodeMessage 类
* 用于处理通过产品代码删除所有特性消息的类
* 这个类的存在意义在于封装删除操作的消息结构,使其具有明确的语义和用途
*
* @param <String> 指定消息的有效载荷类型为字符串
*/
public class DelAllFeaturesByProductCodeMessage extends BaseMessage<String> {
/**
* 构造函数,初始化删除所有特性消息的实例
*
* @param payload 消息的有效载荷,即产品代码字符串
* 这个参数是构造函数的核心,因为它承载了执行删除操作所需的信息
*/
public DelAllFeaturesByProductCodeMessage(String payload) {
super(MessageType.DEL_ALL_FEATURES_BY_PRODUCT_CODE, payload);
}
}
package com.wmdigit.setting.model;
import com.wmdigit.common.base.model.BaseMessage;
import com.wmdigit.common.enums.MessageType;
/**
* 根据图片路径删除向量
* @author dizi
*/
public class DelOneFeatureByPathMessage extends BaseMessage<String> {
public DelOneFeatureByPathMessage(String payload) {
super(MessageType.DEL_ONE_FEATURE_BY_PATH, payload);
}
}
package com.wmdigit.setting.model;
import com.wmdigit.common.base.diffutil.BaseDiffUtilModel;
/**
* 本地图
* @author dizi
*/
public class LocalImage extends BaseDiffUtilModel {
private String path;
public LocalImage(String path) {
this.path = path;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
...@@ -3,7 +3,10 @@ package com.wmdigit.setting.viewmodel; ...@@ -3,7 +3,10 @@ package com.wmdigit.setting.viewmodel;
import android.app.Application; import android.app.Application;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import com.elvishew.xlog.XLog;
import com.wmdigit.common.base.mvvm.BaseViewModel; import com.wmdigit.common.base.mvvm.BaseViewModel;
import com.wmdigit.common.base.mvvm.SingleLiveEvent; import com.wmdigit.common.base.mvvm.SingleLiveEvent;
import com.wmdigit.common.model.DrawerMenuItemVO; import com.wmdigit.common.model.DrawerMenuItemVO;
...@@ -26,15 +29,20 @@ public class SettingViewModel extends BaseViewModel { ...@@ -26,15 +29,20 @@ public class SettingViewModel extends BaseViewModel {
* 3-菜品学习页 * 3-菜品学习页
* 4-相机标定页 * 4-相机标定页
*/ */
public SingleLiveEvent<Integer> pagePosition = new SingleLiveEvent<>(); private final MutableLiveData<Integer> pagePosition = new MutableLiveData<>();
/**
* 0-学习模式 1-纠错模式
*/
private final MutableLiveData<Integer> modeOnLearningPage = new MutableLiveData<>();
/** /**
* 抽屉菜单列表 * 抽屉菜单列表
*/ */
private List<DrawerMenuItemVO> list = new ArrayList<>(); private final List<DrawerMenuItemVO> list = new ArrayList<>();
public SettingViewModel(@NonNull Application application) { public SettingViewModel(@NonNull Application application) {
super(application); super(application);
pagePosition.postValue(0); modeOnLearningPage.setValue(0);
pagePosition.setValue(0);
// 初始化抽屉菜单 // 初始化抽屉菜单
list.add(new DrawerMenuItemVO(R.drawable.ic_menu_system, getApplication().getString(R.string.module_setting_system), true)); list.add(new DrawerMenuItemVO(R.drawable.ic_menu_system, getApplication().getString(R.string.module_setting_system), true));
list.add(new DrawerMenuItemVO(R.drawable.ic_menu_register, getApplication().getString(R.string.module_setting_register), false)); list.add(new DrawerMenuItemVO(R.drawable.ic_menu_register, getApplication().getString(R.string.module_setting_register), false));
...@@ -70,4 +78,29 @@ public class SettingViewModel extends BaseViewModel { ...@@ -70,4 +78,29 @@ public class SettingViewModel extends BaseViewModel {
public boolean checkAllowCloseDrawer(){ public boolean checkAllowCloseDrawer(){
return pagePosition.getValue() == 3; return pagePosition.getValue() == 3;
} }
/**
* 切换学习页面的模式
* 此方法用于在学习页面上切换当前的模式当模式值为0时,切换到1,反之亦然
* 这种切换机制对于支持两种不同学习状态的界面非常有用
*/
public void switchModeOnLearningPage(){
// 检查当前模式值是否为0
if (modeOnLearningPage.getValue() == 0){
// 如果当前模式为0,则切换到1
modeOnLearningPage.setValue(1);
}
else{
// 如果当前模式不为0,则切换回0
modeOnLearningPage.setValue(0);
}
}
public MutableLiveData<Integer> getPagePosition() {
return pagePosition;
}
public MutableLiveData<Integer> getModeOnLearningPage() {
return modeOnLearningPage;
}
} }
...@@ -5,7 +5,11 @@ ...@@ -5,7 +5,11 @@
tools:ignore="ResourceName"> tools:ignore="ResourceName">
<data> <data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="com.wmdigit.setting.viewmodel.SettingViewModel"
/>
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
...@@ -24,8 +28,36 @@ ...@@ -24,8 +28,36 @@
style="@style/ToolbarTheme" style="@style/ToolbarTheme"
app:navigationIcon="@drawable/ic_drawer_close" app:navigationIcon="@drawable/ic_drawer_close"
app:title="@string/module_setting_name" app:title="@string/module_setting_name"
app:menu="@menu/toolbar_menu" app:menu="@menu/toolbar_menu">
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="start|center_vertical"
android:paddingStart="@dimen/dp_20">
<!--纠错、学习模式切换按钮-->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_switch_learning_mode"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:text="@{viewModel.modeOnLearningPage == 0 ? @string/switch_to_learning_mode : @string/switch_to_error_correction_mode}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{viewModel.pagePosition != 3 ? View.GONE : View.VISIBLE}"
android:onClick="@{()->viewModel.switchModeOnLearningPage()}"
app:icon="@drawable/ic_switch"
app:iconGravity="textStart"
app:iconPadding="@dimen/dp_4"
app:iconSize="@dimen/dp_30"
android:insetTop="@dimen/dp_0"
android:insetBottom="@dimen/dp_0"
/>
</LinearLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
<variable <variable
name="viewModel" name="viewModel"
type="com.wmdigit.setting.viewmodel.DataLearningViewModel" /> type="com.wmdigit.setting.viewmodel.DataLearningViewModel" />
<variable
name="activityViewModel"
type="com.wmdigit.setting.viewmodel.SettingViewModel" />
</data> </data>
<FrameLayout <FrameLayout
...@@ -27,6 +30,7 @@ ...@@ -27,6 +30,7 @@
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintGuide_percent="0.46"/> app:layout_constraintGuide_percent="0.46"/>
<!--右侧相机预览-->
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="@dimen/dp_0" android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_0" android:layout_height="@dimen/dp_0"
...@@ -44,25 +48,26 @@ ...@@ -44,25 +48,26 @@
app:layout_constraintStart_toStartOf="@+id/img_preview" app:layout_constraintStart_toStartOf="@+id/img_preview"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/img_preview"/> app:layout_constraintBottom_toTopOf="@+id/img_preview"/>
<!--相机预览图-->
<ImageView <ImageView
android:id="@+id/img_preview" android:id="@+id/img_preview"
android:layout_width="@dimen/dp_720" android:layout_width="wrap_content"
android:layout_height="@dimen/dp_540" android:layout_height="@dimen/dp_0"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@color/black" android:background="@color/black"
android:layout_marginTop="@dimen/dp_10" android:layout_marginTop="@dimen/dp_10"
app:image="@{viewModel.frame}" app:image="@{viewModel.frame}"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_image_desc" app:layout_constraintTop_toBottomOf="@+id/tv_image_desc"
app:layout_constraintBottom_toBottomOf="parent"
/> />
<TextView <TextView
android:id="@+id/tv_camera_status" android:id="@+id/tv_camera_status"
style="@style/text_base.content.white" style="@style/text_base.content.white"
android:text="@string/camera_opening" android:text="@string/camera_opening"
android:visibility="@{viewModel.cameraOpened ? View.INVISIBLE : View.VISIBLE}" android:visibility="@{!viewModel.cameraOpened &amp;&amp; viewModel.frame != null ? View.VISIBLE : View.INVISIBLE}"
app:layout_constraintStart_toStartOf="@+id/img_preview" app:layout_constraintStart_toStartOf="@+id/img_preview"
app:layout_constraintTop_toTopOf="@+id/img_preview" app:layout_constraintTop_toTopOf="@+id/img_preview"
app:layout_constraintEnd_toEndOf="@+id/img_preview" app:layout_constraintEnd_toEndOf="@+id/img_preview"
...@@ -76,10 +81,11 @@ ...@@ -76,10 +81,11 @@
android:layout_width="@dimen/dp_0" android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_0" android:layout_height="@dimen/dp_0"
android:layout_margin="@dimen/dp_5" android:layout_margin="@dimen/dp_5"
android:paddingBottom="@dimen/dp_15"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/gl_v_46" app:layout_constraintEnd_toStartOf="@+id/gl_v_46"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toTopOf="@+id/tv_page"/>
<!--空数据的图--> <!--空数据的图-->
<ImageView <ImageView
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/dp_10"
android:paddingVertical="@dimen/dp_10"
android:layout_margin="@dimen/dp_5"
android:background="@drawable/sel_rect_10_white_gray">
<TextView
android:id="@+id/tv_product_name2"
style="@style/text_base.title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="商品名称"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btn_del_all_learning_data"
app:layout_constraintBottom_toBottomOf="@+id/btn_del_all_learning_data"/>
<TextView
android:id="@+id/tv_product_code2"
style="@style/text_base.content"
android:text="00000000"
android:layout_marginStart="@dimen/dp_10"
app:layout_constraintStart_toEndOf="@+id/tv_product_name2"
app:layout_constraintTop_toTopOf="@+id/tv_product_name2"
app:layout_constraintBottom_toBottomOf="@+id/tv_product_name2"
/>
<Button
android:id="@+id/btn_del_all_learning_data"
style="@style/button_base.w130.red"
android:layout_width="wrap_content"
android:paddingHorizontal="@dimen/dp_8"
android:text="@string/delete_all_learning_data"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_local_images"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
app:layout_constraintTop_toBottomOf="@+id/btn_del_all_learning_data" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_3"
android:padding="@dimen/dp_3">
<ImageView
android:id="@+id/img_local_image"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -66,4 +66,7 @@ ...@@ -66,4 +66,7 @@
<string name="import_failed">导入失败</string> <string name="import_failed">导入失败</string>
<string name="learn_local_image_success">学习本地图片成功</string> <string name="learn_local_image_success">学习本地图片成功</string>
<string name="learn_local_image_fail">学习本地图片失败</string> <string name="learn_local_image_fail">学习本地图片失败</string>
<string name="delete_all_learning_data">删除学习记录</string>
<string name="confirm_del_photo">请确认是否删除该照片</string>
<string name="confirm_del_all_photos_of_this_product">请确认是否删除该商品所有照片</string>
</resources> </resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment