name: ylgs-android-conventions description: >- Implements and refactors Java code in the Ylgs Android app (ylgs) following project conventions: LatteActivity/ViewBinding, latte-core BaseActivity lifecycle, feature packages (ylpc, tyuan, route, ylgs), Fastjson, GreenDao, tab/fragment patterns, and copy-paste code templates. Use when editing this repository, adding Activities or Fragments, scaffolding new screens, or when
在 ylgs 仓库中写或改 Java/Android 代码时,以 app 模块里与 水表普查 / ylpc 相关的实现为风格基准;PcTaskDetailActivity 是轻量、典型的 LatteActivity 页面示例(无 Presenter、ViewBinding、Tab+Fragment)。
:app(主应用)、:latte-core(LatteActivity、BaseActivity、基础 UI/工具)、:graffiti 等。com.tofly.ylpc — 普查等业务com.tofly.tyuan — 台帐/表务等com.tofly.route — 路线/任务等com.tofly.ylgs — 壳、登录、部分通用 ActivityR、*Binding 类在 com.tofly.ylgs(databinding 生成于 app 模块),业务类在 ylpc 等包中 import 自 ylgs 是正常做法。LatteActivity(com.tofly.latte_core.base),其继承 BaseActivity<LatteContract.LattePresenter> 并实现 LatteContract.LatteView。Object setLayout():
*Binding.inflate(getLayoutInflater()); return binding.getRoot();Integer(旧式 XML-only)。createPresenter()。无网络/MVP 需求时 return null(与 PcTaskDetailActivity 一致);非 null 时需在 onDestroy 前由基类处理 attach/detach。initView()(基类在 onCreate 中 setContentView → ButterKnife → createPresenter → initView() → initNet())。不要绕过该顺序在子类重复 setContentView(除非像 PhotoActivity 等有特殊 onCreate 链的特殊基类)。@author(项目内常见 76486 / ychk),保持与邻文件一致。initBar(Toolbar toolbar, String title);嵌套 toolbar 时常用 binding.*Toolbar*.mainToolbar 形式(随 layout 命名)。onError(String)(BaseView → ToastUtils),与 PcTaskDetailActivity 扫码失败分支一致。getIntent().getLongExtra / getIntExtra / getBooleanExtra / getStringExtra,key 使用 字符串字面量(项目现有风格;新代码若引入常量类需与模块内既有做法一致)。com.alibaba.fastjson.JSON:
JSON.toJSONString(entity)JSON.parseObject(json, XxxEntity.class)pcId 等,若带 data JSON 则可在读库后再覆盖(见 PcTaskDetailActivity)。ListPageAdapter(FragmentPagerAdapter):FragmentManager + 标题列表 + 可选 tab 图标 List<Integer>(R.mipmap.*) + List<Fragment>。setupWithViewPager 后,对 tab 设置 setCustomView(listPageAdapter.getTabView(i, this))。setOffscreenPageLimit 按页数设置,避免相邻页被销毁导致状态丢失(普查示例为 2)。type == 2)调用 setCurrentItem。Bundle 置于 setArguments(bundle),不要在构造里塞大块状态。PhotoFragment(拍照/地图等)← LatteDelegate;复杂表单 Fragment 使用对应 FragmentXxxBinding。setQc),与 PcTaskDetailActivity.onActivityResult 模式一致。onActivityResult 中 requestCode 49374 与 IntentIntegrator.parseActivityResult 配合使用(与 Google ZXing 集成一致);成功则把内容下发到当前业务 Fragment,失败 onError("扫码失败")。PcDaoUtilsStore.getInstance() 等 GreenDao 封装访问(如 queryById);路径与实体定义随 greenDao 包与 com.tofly.ylpc.entity 保持一致。JSON、JSONObject)。RxAppCompatActivity(BaseActivity 父类);订阅需遵守项目既有 RxLifecycle 用法。PhotoActivity / PhotoFragment、TakePhoto、高德等——新页面若需同类能力应 继承对应基类,而不是在 LatteActivity 里堆叠重复逻辑。以下模板为 com.tofly.ylpc(或对应业务包)+ com.tofly.ylgs.R / databinding 写法;将占位符替换为实际类名、布局名、实体类与 presenter 实现。布局文件需先在 app 工程中创建(如 activity_xxx.xml),ViewBinding 类名遵循 Android 命名规则。
package com.tofly.ylpc.ui.activity;
import com.tofly.latte_core.base.LatteActivity;
import com.tofly.latte_core.base.LatteContract;
import com.tofly.ylgs.databinding.ActivityXxxPlaceholderBinding;
/**
* @author 76486
*/
public class XxxPlaceholderActivity extends LatteActivity {
private ActivityXxxPlaceholderBinding binding;
@Override
public Object setLayout() {
binding = ActivityXxxPlaceholderBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected LatteContract.LattePresenter createPresenter() {
return null;
}
@Override
public void initView() {
initBar(binding.llToolbar.mainToolbar, "标题");
// getIntent()、控件事件、RecyclerView 等
}
}
说明:initBar 第一参随 layout 中 Toolbar 实际 id 调整(无嵌套则可能为 binding.mainToolbar 等)。
Intent intent = new Intent(fromActivity, TargetActivity.class);
intent.putExtra("id", entity.getId());
intent.putExtra("data", JSON.toJSONString(entity));
fromActivity.startActivity(intent);
接收端:
String data = getIntent().getStringExtra("data");
if (!TextUtils.isEmpty(data)) {
WaterMeterEntity entity = JSON.parseObject(data, WaterMeterEntity.class);
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import androidx.fragment.app.Fragment;
import com.tofly.ylpc.ui.adapter.ListPageAdapter;
// 在 initView() 或独立方法中:
List<String> titles = Arrays.asList("标签一", "标签二");
List<Integer> tabIcons = Arrays.asList(R.mipmap.icon_jc, R.mipmap.icon_wf); // 可改为单页时不传图标逻辑
List<Fragment> fragments = new ArrayList<>();
ExampleFragmentTab0 f0 = new ExampleFragmentTab0();
ExampleFragmentTab1 f1 = new ExampleFragmentTab1();
Bundle bundle = new Bundle();
bundle.putString("data", JSON.toJSONString(modelEntity));
f0.setArguments(bundle);
f1.setArguments(bundle);
fragments.add(f0);
fragments.add(f1);
ListPageAdapter adapter = new ListPageAdapter(getSupportFragmentManager(), titles, tabIcons, fragments);
binding.viewPager.setAdapter(adapter);
binding.viewPager.setOffscreenPageLimit(fragments.size());
binding.tabLayout.setupWithViewPager(binding.viewPager);
for (int i = 0; i < binding.tabLayout.getTabCount(); i++) {
binding.tabLayout.getTabAt(i).setCustomView(adapter.getTabView(i, this));
}
binding.viewPager.setCurrentItem(0);
继承 LatteDelegate(或项目中的 PhotoFragment 等你需要的中间基类):
package com.tofly.ylpc.ui.fragment;
import com.tofly.latte_core.base.LatteContract;
import com.tofly.ylgs.databinding.FragmentXxxPlaceholderBinding;
/**
* @author 76486
*/
public class XxxPlaceholderFragment extends LatteDelegate {
private FragmentXxxPlaceholderBinding binding;
@Override
public Object setLayout() {
binding = FragmentXxxPlaceholderBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
protected LatteContract.LattePresenter createPresenter() {
return null; // 若有上传/接口实现则 return new YourImpl();
}
@Override
public void initView() {
Bundle args = getArguments();
if (args != null) {
String json = args.getString("data");
// JSON.parseObject(json, ...)
}
}
}
需要拍照/地图等能力时,将 extends LatteDelegate 改为 extends PhotoFragment,并补齐父类要求的构造或生命周期(与同目录已有 Fragment 一致)。
import android.content.Intent;
import androidx.annotation.Nullable;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
private static final int ZXING_SCAN_REQUEST = 49374;
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ZXING_SCAN_REQUEST && resultCode == RESULT_OK) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null && !android.text.TextUtils.isEmpty(result.getContents())) {
String text = result.getContents();
if (yourFragment != null) {
yourFragment.setQc(text); // 方法名以目标 Fragment 为准,如同模块的 setQc
}
} else {
onError("扫码失败");
}
}
}
启动扫码:new IntentIntegrator(this).initiateScan();(与项目现有写法保持一致)。
若无独立 Presenter,始终 createPresenter() → null。若新建 LatteContract.LattePresenter 的实现类(如普查中的 UpLoadImpl),再在对应 Activity/Delegate 里 return new XxxImpl(),并实现契约中的网络方法;不要在模板里虚构方法体,应从同模块 Impl / Activity 复制订阅与 getResult* 回调模式。
setLayout 返回 Binding root 或 layout idcreatePresenter 明确 null 或真实 PresenterinitView 内解析 Intent、初始化 toolbarR / Binding 来自 com.tofly.ylgsonError,加载走 showLoading/hideLoading(若用网络层)