烟草仓储管理系统开发笔记(五)

本文最后更新于:2024年3月18日 晚上

悲报

前几天写完的采购订单界面,花了很长的时间去实现树形列表,构思数据的呈现形式,算是写了挺多的内容了,前天晚上明明都暂告一段落了,却忘记了push到GitHub,结果昨天打开电脑,发现存放毕设的硬盘损坏了,系统直接识别不到硬盘了。。。也就是前几天写的采购订单页面全部作废。GitHub上的最新的commit也是两天前的,直接导致昨天心态炸裂,摆了一天。期望着今天硬盘能够奇迹般恢复(毕竟之前有过一次),看来当时就不该心存侥幸,留下这块硬盘,明明都买了新硬盘,但因为硬盘恢复了便也没更换。不过更重要的还是一定要注意备份,要是那天晚上将写完的内容push到GitHub上,那么硬盘损坏倒也无所谓了。

今天首先着手把产品信息页面调整了一下,并且把库存管理界面给写了,两个订单管理界面也留个几天,万一硬盘又恢复了呢🤡不过真恢复不了也罢,正好之前的方案也存在一些无法解决的问题,这次换一个实现方式,换一个数据的呈现方式。

产品信息界面

之前的产品信息界面的Item设计始终觉得不美观,辨识度也不太够,因此今天把产品信息界面进行了重新的设计,考虑再三还是为产品加上了图片字段,更加直接地显示产品信息,也方便库存管理中Item的设计,此外,原本觉得页面没有其他颜色,过于单调,加上图片也方便布局和查看,而原料信息则没有进行更新,一是原料图片也没有什么辨识度,二是原料只是为产品服务而已(”使用原料“)

重新设计后的Item中去除了名称和型号的标识label,直接用加粗的大号字体显示,使层次更加清晰。

库存管理界面

由于库存信息中有两大块,一个是产品,一个是原料,两者都存储在Inventory数据表中,通过一个字段Type来分辨存放的是原料还是产品,原本设想过在一个页面中放置两个RecycleView,分别显示产品和原料信息,但总觉得不太妥当,最后还是选择了底部导航栏的方式,再用一个Navigation,在库存的Fragment中再嵌套两个Fragment。为了尽可能多的显示产品和原料的库存条目,将原本的头部布局进行了压缩,将菜单按钮和标题等挪到了同一高度。具体界面如下。

原料界面也是如此,将标题更换一下而已。

Fragment嵌套导航实现

因为此处的库存界面本身是一个Fragment,在其中再套NavHostFragment时出现了一点问题,首先是创建Fragment,创建menu的xml文件,创建navigation的xml文件,这些都没有问题,但是NavControll的获取出现了问题。在Activity中通过如下代码实现导航。

1
2
3
4
5
NavigationView navigationView = findViewById(R.id.navigationView);  
NavHostFragment navHostFragment = (NavHostFragment)
getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView);
NavController controller = navHostFragment.getNavController();
NavigationUI.setupWithNavController(navigationView,controller);

首先获取到navigationView,通过getSupportFragmentManager()方法获取FragmentContainerView,并通过类型强转为NavHostFragment,最后通过NavHostFragment的getNavControll()方法获取到NavControll,而在Fragment内部的NavHostFragment中,首先就没有getSupportFragmentManager()方法,这是Activity的方法。那么Fragment中用什么呢,在Fragment中有一个方法getChildFragmentManager(),就是用来获取嵌套中的Fragment的FragmentContainer。于是我便参照Activity,在Fragment的onCreate()方法中加入了修改后的代码,结果还是报错,程序找不到NavHostFragment,即findFragmentById方法找到的Fragment是个null,于是我又尝试了其他的方法,结果都没有成功,最后意识到一点,我要查找的Fragment是页面中的组件,而在Fragment的onCreate方法中,页面都还没有创建,自然也就无法找到页面中的组件了,于是把代码挪到了onCreateView中,结果成功实现了想要的效果。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Override  
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_inventory, container, false);
BottomNavigationView bottomNavigationView =view.findViewById(R.id.inventoryBottomNavigationView);
NavHostFragment navHostFragment = (NavHostFragment)
getChildFragmentManager().findFragmentById(R.id.inventoryNavHostFragment);
NavController controller = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView,controller);
return view;
}

产品信息Item设计与实现

库存管理中的产品信息需要的体现的内容有产品的名称和型号,产品的库存情况,由于产品只需要销售,而不需要购入,因此设计了两个数据,一个是库存数量,一个是运输中数量(售出数量),因为是出库,所以颜色设定为红色。而库存数量则设定为蓝色,同时为两个数据加上对应的Iconfont图标。具体实现结果如下:

由于库存信息中只需要查看,而不需要进行删除操作,因此这里的RecycleView移除了滑动删除功能。这里的item右侧添加了一个向右图标,标志可以点击打开进一步的详细页面,暂时还没写😂,优先级可能没那么高,尽量实现。

原料信息Item设计与实现

原料信息Item和产品信息Item相比,少了图片,此外因为原料是入库,因此将入库的字体颜色设定为绿色。具体实现效果如下:

公用Adapter

因为原料信息和产品信息的内容几乎相同,唯一的区别只是产品信息有图片,因此两个RecycleView公用一个Adapter,在自定义的ViewHolder中定义所有的组件,包括ImageView,不过因为在原料信息中没有ImageView,所以此时在ViewHolder中findViewById方法找到的是个null,所以在赋值时需要进行判断,根据数据表中设计的Type字段来分辨是产品信息还是原料信息,只有在当前是产品信息时才对ImageView设定图片路径,如果是原料信息则不对ImageView进行操作。

ViewHolder

1
2
3
4
5
6
7
8
9
10
11
12
class MyInventoryViewHolder extends BaseViewHolder {  
TextView name,model,hostCount,deliveryCount;
ImageView image;
public MyInventoryViewHolder(@NonNull View view) {
super(view);
name = view.findViewById(R.id.textViewName);
model = view.findViewById(R.id.textViewModel);
hostCount = view.findViewById(R.id.textViewHostCount);
deliveryCount = view.findViewById(R.id.textViewDeliveryCount);
image = view.findViewById(R.id.productImageView);
}
}

赋值方法

1
2
3
4
5
6
7
8
9
10
@Override  
protected void convert(@NonNull MyInventoryViewHolder holder, InventoryTest inventory) {
holder.name.setText(inventory.getName());
holder.model.setText(inventory.getModel());
holder.hostCount.setText(String.valueOf(inventory.getHostCount()));
holder.deliveryCount.setText(String.valueOf(inventory.getDeliveryCount()));
if (inventory.getType() == InventoryTest.TYPE_PRODUCT)
//设置图片路径
//holder.image.setImageURI();
}

产品订单送达确认

在产品信息界面中,我仍然放置了一个添加按钮,不过这个按钮不是用来添加产品库存信息的,而是进行订单的确认,因为订单存在”运输中“这个状态,而此时订单中的售出数量包含在deliveryCount中,且订单一直为运输中状态,那么订单怎么完成呢,就是在库存界面确认订单送达,则订单状态改为完成,并减去对应的运输中数量。点击按钮后弹出一个底部对话框,对话框中的数据都由TextView呈现,无法修改,用户可做的就是选择要操作的哪个订单(Spinner),以及设定订单完成的时间。具体实现界面如下:

其中送达日期由两部分组成,第一部分是一个Date类型的输入框,用户可以手动输入,第二部分是一个IconFontTextView,用于显示图标并响应点击事件,原本尝试了EditText的drawableRight属性,但显示效果差强人意,更重要的是,不知道怎么样(能不能)单独实现这个图标的点击事件,所以干脆用IconFontTextView替代。
点击日历按钮后跳出自带的日期选择器,使用的是DatePickerDialog,在此处也遇到了两个小问题。

初始日期的设定

第一个问题是初始日期的设定,DatePickerDialog默认是1970年那个时间,那显然不方便使用,因此设定了三个变量分别表示年月日,并通过Calendar类获取了当前日期作为初始值。DatePickerDialog的构造函数中需要有一个OnDateSetListener,那就创建一个,当日期选择完成时,将选择的日期分别赋值给三个本地变量,并同步设置送达日期EditText的数值,而此时就出现了问题,因为年月日的三个变量是外部变量,在定义OnDateSetListener时是定义的内部类,而在内部类中要使用外部变量就需要将外部变量定义为final,而定义为final又无法赋值,最后只能用IDE建议的修改方式,将年月日变量修改为final的单容量数组。具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
addButton.setOnClickListener(v->{  
BottomDialog.show("确认送达",new OnBindView<BottomDialog>(R.layout.dialog_inventory_check_product) {
@Override
public void onBind(BottomDialog dialog, View v) {
//TODO: 添加“确认”事件
Spinner spinner;
TextView name,model,customer,count,price,saleDate,calendar;
EditText deliveryDate;
final int[] mYear = new int[1];
final int[] mMonth = new int[1];
final int[] mDay = new int[1];
mYear[0] = Calendar.getInstance(Locale.CHINA).get(Calendar.YEAR);
mMonth[0] = Calendar.getInstance(Locale.CHINA).get(Calendar.MONTH);
mDay[0] = Calendar.getInstance(Locale.CHINA).get(Calendar.DAY_OF_MONTH);

spinner = v.findViewById(R.id.orderSpinner);
name = v.findViewById(R.id.textViewName);
model = v.findViewById(R.id.textViewModel);
customer = v.findViewById(R.id.textViewCustomer);
count = v.findViewById(R.id.textViewCount);
price = v.findViewById(R.id.textViewPrice);
saleDate = v.findViewById(R.id.textViewSaleDate);
calendar = v.findViewById(R.id.textViewCalendar);
deliveryDate = v.findViewById(R.id.editTextDeliveryDate);

calendar.setOnClickListener(view1->{
DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
mYear[0] = year;
mMonth[0] = monthOfYear;
mDay[0] = dayOfMonth;
deliveryDate.setText(new StringBuilder().append(mYear[0]).append("-")
.append(mMonth[0] + 1).append("-").append(mDay[0]));
}
};
new DatePickerDialog(getActivity(), mDateSetListener, mYear[0], mMonth[0], mDay[0]).show();
});
}
}).setOkButton("确定").setCancelButton("取消");
});

DatePickerDialog显示英文

但是很奇怪,DatePickerDialog默认显示的是英文,没能找到办法修改为中文,在网上查阅了许多也没发现有说这个问题的什么内容,不过有些类似问题倒是提到了”修改程序的默认地址“,也就是说,因为程序的默认地址不是中文区,所以显示不是中文,所以通过设置Local的默认值来修改地址

1
2
//将系统环境默认设置为中文  
Locale.setDefault(Locale.SIMPLIFIED_CHINESE);

这样就可以将DatePickerDialog显示为简体中文,但最后发现其实问题是我的安卓模拟器设置的语言是英文😂,所以DatePickerDialog也就使用的英文,将系统语言改成中文就好了。


烟草仓储管理系统开发笔记(五)
http://starnight.top/2022/04/21/烟草仓储管理系统开发笔记(五)/
作者
Cardy Xie
发布于
2022年4月21日
许可协议