[关闭]
@cxm-2016 2017-07-07T18:45:54.000000Z 字数 17337 阅读 2473

软件开发说明

0

版本:0.3
最后修改日期:2017/6/30

说明:如对架构有更改应及时更新文档

一、开发样例

全部样例采用MVP设计模式

1.1 获取俱乐部公告列表(列表类型)

需求:根据俱乐部ID请求俱乐部公告列表并显示。列表每一项要能够点击跳转到详情。另外添加一个用来发布的按钮。

1.1.1 创建契约接口

  1. public interface ClubNoticeListContract {
  2. interface Presenter extends BasePresenter<SimpleListUI>, SimpleListPresenter{}
  3. interface Model extends BaseModel {
  4. }
  5. }

1.1.2 完善Model接口

step1:分析接口数据创建模型类NoticeResponse

略(使用GsonFormat)

step2:根据请求方式创建Service接口

  1. interface Service {
  2. @GET("app/new_group/get_group_announcement_list/{params}")
  3. Observable<HttpResult<List<NoticeResponse>>> getNoticeList(@Path("params") String params);
  4. }

step3:在Model接口提供访问方式

  1. void getNoticeList(@NonNull String token,
  2. @NonNull String clubId,
  3. int page,
  4. @NonNull Action1<Throwable> onError,
  5. @NonNull Action1<HttpResult<List<NoticeResponse>>> onNext);

1.1.3 实现Model接口

必须继承自BaseMvpModel,该基类提供了停止请求的方法。另外,Subscription对象必须使用register进行注册,防止内存泄漏。

  1. public class ClubNoticeListModel extends BaseMvpModel implements ClubNoticeListContract.Model {
  2. private Service mService = RetrofitUtil.getInstance().create(Service.class);
  3. @Override
  4. public void getNoticeList(@NonNull String token, @NonNull String clubId, int page, @NonNull Action1<Throwable> onError, @NonNull Action1<HttpResult<List<NoticeResponse>>> onNext) {
  5. HashMap<String, Object> params = new HashMap<>();
  6. params.put("token", token);
  7. params.put("user_group_id", clubId);
  8. params.put("page", page);
  9. Subscription subscribe = mService.getNoticeList(RetrofitUtil.buildParams(params))
  10. .subscribeOn(Schedulers.io())
  11. .observeOn(AndroidSchedulers.mainThread())
  12. .subscribe(onNext, onError);
  13. register(subscribe);
  14. }
  15. }

1.1.4 实现布局

(略)需要在运行时使用的控件ID命名采用与代码属性相同的命名方式,建议以m开头,如果需要说明功能就在中间加上功能名称,然后以控件类型结尾。如:

mContentEdit :内容输入框EditText
mRecyclerView:不需要说明功能的RecyclerView
mHeaderRecyclerView:在头部显示的RecyclerView
mNameText:显示名称的TextView
mAvatarImage:显示头像的ImageView
mDeleteBtn:点击删除功能的任意可点击View

1.1.5 完善Presenter接口

根据需求,只需提供一个用来设置Item点击的方法,与一个加载更多的方法。

  1. void onNoticeClick(@Nullable String id);

1.1.6 创建ViewHolder与适配器

  1. class NoticeHolder extends RecyclerView.ViewHolder {
  2. NoticeHolder(View itemView) {
  3. super(itemView);
  4. ButterKnife.bind(this, itemView);
  5. }
  6. }
  7. class NoticeAdapter extends CommonItemAdapter<Model.NoticeResponse, NoticeHolder> {
  8. private Presenter presenter;
  9. public NoticeAdapter(Presenter presenter) {
  10. this.presenter = presenter;
  11. }
  12. @Override
  13. protected void onBindViewHolder(@NonNull NoticeHolder holder, @Nullable final Model.NoticeResponse item) {
  14. //TODO
  15. }
  16. @Override
  17. public NoticeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  18. //TODO
  19. }
  20. }

1.1.7 创建Activity并实现Presenter接口

  1. public class ClubNoticeListActivity extends BaseMVPActivity implements Presenter

step1:提供静态的打开方式与Event通知

我们需要通过静态方法将打开自身的方式暴露出去。在外部使用Intent方式并携带参数的方式不不符合编程规范的,会造成代码之间的耦合增加。合格的编程方式是面向扩展开放,面向修改关闭。使用静态方法可以减少由于传参变化造成的大范围修改代码。

  1. final private static String EXTRA_ID = "club_id";
  2. public static void start(@NonNull Activity activity, @NonNull String id) {
  3. activity.startActivity(new Intent(activity, ClubNoticeListActivity.class).putExtra(EXTRA_ID, id));
  4. }
  5. private static class OnRefreshEvent {}
  6. public static void refresh() {
  7. AppUtilKt.postEvent(new OnRefreshEvent());
  8. }

对于Kotlin:

  1. componin object{
  2. public fun start(activity: Activity, id: String) {
  3. activity.startActivity<ClubNoticeListActivity>(EXTRA_ID to id);
  4. }
  5. private class OnRefreshEvent
  6. public fun refresh() = postEvent(OnRefreshEvent());
  7. }

step2:声明需要使用的属性

  1. @Nullable
  2. private String clubId;
  3. @NonNull
  4. private Model mModel = new ClubNoticeListModel();
  5. @NonNull
  6. private NoticeAdapter mAdapter = new NoticeAdapter(this);
  7. private int mPage = 1;
  8. private boolean mCanLoadMore = true;

如果使用的时Java代码编写,必须在应用类型的属性上声明@Nullable或者@NonNull来标记该属性的可空状态。对于标记了@NonNull的属性必须在声明时赋值或者在静态或构造代码块中赋初值,并且在代码的任何位置都不能将其赋值为null。对于标记了@Nullable的属性,不必在声明时赋初值,但是在任何使用使用场景都必须使用if进行判空。

举个例子:

1,不安全的写法:

  1. class Test{
  2. private People mPeople;
  3. public int getAge(){
  4. return mPeople.getAge();
  5. }
  6. }

2,线程不安全的写法

  1. class Test{
  2. @Nullable
  3. private People mPeople;
  4. public int getAge(){
  5. if(mPeople==null)
  6. return 0;
  7. else return mPeople.getAge();
  8. }
  9. }

3,线程安全的写法

  1. class Test{
  2. @Nullable
  3. private People mPeople;
  4. public int getAge(){
  5. People people = mPeople;
  6. return people==null? 0: people.getAge();
  7. }
  8. }

Kotlin的判空是最高级的保证线程安全的机制,所以必须在先创建本地变量再判断,防止其他线程在使用时将其置空。

对于Java,如果可以保证这些属性中不涉及多线程操作,采取第二种方式即可。

step3:实现业务逻辑

  1. @Override
  2. protected void onCreate(@Nullable Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. AppUtilKt.registerEventBus(this);
  5. setTitle("群公告");
  6. clubId = getIntent().getStringExtra(EXTRA_ID);
  7. onRefresh(null);
  8. }
  9. @Override
  10. protected void onRefresh(@Nullable Bundle bundle) {
  11. super.onRefresh(bundle);
  12. mPage = 1;
  13. mCanLoadMore = true;
  14. loadNotice(mPage++);
  15. }
  16. @Override
  17. public void onFabClick() {
  18. }
  19. @Override
  20. public void onPullToRefresh() {
  21. }
  22. @Override
  23. public void onLoadMore() {
  24. if (mCanLoadMore) loadNotice(mPage++);
  25. }
  26. /**
  27. * 加载公告数据
  28. */
  29. private void loadNotice(final int page) {
  30. String token = getMUserInfo().getToken();
  31. if (clubId != null && token != null)
  32. mModel.getNoticeList(token, clubId, page, onError, new Action1<HttpResult<List<Model.NoticeResponse>>>() {
  33. @Override
  34. public void call(HttpResult<List<Model.NoticeResponse>> result) {
  35. if (result.isSuccess()) {
  36. List<Model.NoticeResponse> data = result.getData();
  37. if (data != null && !data.isEmpty()) {
  38. if (page == 1) mAdapter.setItems(data);
  39. else mAdapter.addItems(data);
  40. } else {
  41. if (page == 1) setErrorMessage("这里啥都没有,去别处看看吧");
  42. mCanLoadMore = false;
  43. }
  44. new SimpleListFragment<>().setContentView(ClubNoticeListActivity.this);
  45. } else {
  46. setErrorMessage("网络请求失败,点击刷新");
  47. }
  48. }
  49. });
  50. }
  51. /**
  52. * 公告点击事件
  53. */
  54. @Override
  55. public void onNoticeClick(@Nullable String noticeId) {
  56. if (noticeId != null && clubId != null)
  57. ClubNoticeDetailActivity.start(this, clubId, noticeId);
  58. }
  59. @Override
  60. public void onUIPrepared(@NonNull SimpleListUI ui) {
  61. showTextBtn("发起");
  62. ui.setAdapter(mAdapter);
  63. }
  64. /**
  65. * 发布按钮被点击
  66. */
  67. @Override
  68. public void onBtnClick(View view) {
  69. if (clubId != null)
  70. ClubNoticeEditActivity.start(this, clubId, null, null, null);
  71. }
  72. /**
  73. * 外部通知刷新时回调
  74. */
  75. @Subscribe
  76. public void onRefreshEvent(OnRefreshEvent event) {
  77. onRefresh(null);
  78. }
  79. @Override
  80. protected void onDestroy() {
  81. super.onDestroy();
  82. AppUtilKt.unRegisterEventBus(this);
  83. mAdapter.clear();
  84. mModel.onDestroy();
  85. }

函数说明:

setTitle("群公告") :设置标题栏标题内容
onEmptyError属性:不做任何操作的错误处理对象,另外还有一个onError属性,onError接收到错误后会将已经显示的Fragment隐藏并显示出错误信息。
protected void onRefresh(@Nullable Bundle bundle)方法用来跟错误页面进行沟通,错误页面会调用此函数通知唤醒Fragment,bundle中可以携带错误页面传来的信息,比如错误原因等等,根据信息,我们可以决定以什么样的方式重新加载这个页面。

1.2 店铺详情(收藏、分享、定位与第三方导航)

1.2.1 在UI加载完成后显示收藏与分享按钮

  1. @Override
  2. public void onUIPrepared(@NonNull RepairSiteDetailContract.View ui) {
  3. if (mResponse != null) {
  4. showCollectionBtn();
  5. showShareBtn();
  6. //something
  7. boolean isCollection = "1".equals(mResponse.getIs_collection());
  8. setCollection(isCollection);
  9. //something
  10. }
  11. }

showCollectionBtnshowShareBtn分别用来显示标题栏上的收藏与分享按钮。

1.2.2 收藏的点击事件处理

重写BaseMvpActivity的public void onCollectionClick(View view)方法。在其中实现点击收藏的事件。

  1. @Override
  2. public void onCollectionClick(View view) {
  3. //do something
  4. }

1.2.3 分享的点击事件处理

step1:重写分享回调方法,在其中显示分享页面

  1. @Override
  2. public void onShareClick(View view) {
  3. ShareUI mShareUI = new ShareUI(this);
  4. mShareUI.setOnSocialShareListener(new Function1<ShareType, Unit>() {
  5. @Override
  6. public Unit invoke(ShareType shareType) {
  7. ToastsKt.toast(getApplication(), "缺少参数");
  8. // mShareServiceBuilder.setShareType(shareType).setUrl("1").setContent("2").setTitle("3").build().post(); // TODO: 2017/5/24
  9. return null;
  10. }
  11. });
  12. mShareUI.setOnFriendsShareListener(new Function0<Unit>() {
  13. @Override
  14. public Unit invoke() {
  15. if (AppUtils.autoLogin(RepairSiteDetailActivity.this))
  16. FriendsListActivity.start(RepairSiteDetailActivity.this);
  17. return null;
  18. }
  19. });
  20. mShareUI.show(view);
  21. }

ShareUI是显示选择分享渠道的页面,其中的点击事件有两个回调。分别是

  1. /**
  2. * 设置社会化分享的回调接口
  3. */
  4. var onSocialShareListener: ((ShareType) -> Unit)? = null

和应用内好友分享

  1. /**
  2. * 好友分享接口
  3. */
  4. var onFriendsShareListener: (() -> Unit)? = null

stop2:在社会化分享的回调中使用ShareService对象进行发送消息。

  1. ShareService.with(Activity)
  2. .setShareType(ShareType)
  3. .setUrl(String)
  4. .setContent(String)
  5. .setTitle(String)
  6. .build()
  7. .post();

step3:在应用内分享时打开好友列表,并且在onActivityResult中接收选择对象

  1. FriendsListActivity.start(Activity);

选择结果

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. FriendsListActivity.onResult(requestCode, resultCode, data, new Function1<String, Unit>() {
  5. @Override
  6. public Unit invoke(String imId) {
  7. return null;
  8. }
  9. });
  10. }

1.2.4 获得当前位置

  1. LocationUtilKt.getLocation(this, new Function1<Location, Unit>() {
  2. @Override
  3. public Unit invoke(Location location) {
  4. // todo
  5. return null;
  6. }
  7. });

1.2.5 显示IOS风格对话框

step1:创建一个String资源数据

  1. private static final int[] sheetItems = {R.string.baidu_map, R.string.gaode_map, R.string.google_map};

step2:创建并显示对话框

  1. AppUtils.showActionSheet(Activity, sheetItems, ActionSheet.ActionSheetListener)

1.2.6 打开第三方导航

Java:

  1. //打开百度地图
  2. AppUtilKt.startBaiBuMap(Activity, mLocation.getLatitude(), mLocation.getLongitude(), mTargetLatitude, mTargetLongitude);
  3. //打开高德地图
  4. AppUtilKt.startGaoDeMap(Activity, mTargetLatitude, mTargetLongitude);
  5. //打开谷歌地图
  6. AppUtilKt.startGoogleMap(Activity, mTargetLatitude, mTargetLongitude);

Kotlin:在Activity中

  1. startBaiBuMap(mLatitude, mLongitude, mTargetLatitude, mTargetLongitude);
  2. startGaoDeMap(mTargetLatitude, mTargetLongitude);
  3. startGoogleMap(mTargetLatitude, mTargetLongitude);

二、开发说明

工程说明

业务逻辑:MVP
异步任务流:Rxjava
网络请求框架:Retrofit2 + OkHttp3
图片处理框架:Glide
数据库框架:GreenDao
通信框架:EventBus
编程语言:Java + Kotlin,业务逻辑中的Presenter层慎用Kotlin

包名规范

业务逻辑部分包名规范如下:

com.muqi.qmotor.[模块名]
在其下再划分以下包:

adapter:用于存放适配器
contract:MVP模式的契约类
model:用于整个模块的Model类
presenter:MVP模式的Presenter类(Activity)
ui:MVP模式的View类(Fragment)

关于Activity

该工程项目中的Activity(无论规模大小)都必须使用MVP设计模式。

工程的Activity需要继承自BaseMVPActivity并实现一个Presenter接口。

1,启动页面
启动一个Activity的方法需要该Activity提供一个静态方法,并封装需要的参数。
示例:

  1. private static final String EXTRA_CLUB_ID = "club_id";
  2. public static void start(@NonNull Activity activity, @NonNull String clubId) {
  3. activity.startActivity(new Intent(activity, ClubMemSortActivity.class)
  4. .putExtra(EXTRA_CLUB_ID, clubId));
  5. }

2,页面通信
不同页面之间的通信使用EventBus,同第一条,接受消息的页面需要提供一个静态方法,并由接受消息的类自行处理数据并通过EventBus发送。
示例:

  1. private static class RefreshDeleteEvent {
  2. @NonNull
  3. private String id;
  4. RefreshDeleteEvent(@NonNull String id) {
  5. this.id = id;
  6. }
  7. }
  8. public static void refreshDelete(@NonNull String id) {
  9. AppUtilKt.postEvent(new RefreshDeleteEvent(id));
  10. }

注意RefreshDeleteEvent被设置为私有。

3,保存数据
在异常状态下(如屏幕旋转、内存不足等),我们需要在onSaveInstanceState方法中保存状态,并在onCreate方法中取出。这里提供一个注解:
@SaveInstanceState,仅需要将该注解声明在需要被保存的变量上,比如:

  1. @Nullable
  2. @SaveInstanceState
  3. private String hxId;
  4. @Nullable
  5. @SaveInstanceState
  6. private String clubId;

需要注意的是,我们使用了该注解后,在初始化时就需要注意不要重复取值,比如需要通过getIntent获取值的时候,可以这样写:

  1. Bundle extras = getIntent().getExtras();
  2. hxId = extras.getString(EXTRA_HX_ID, hxId);
  3. clubId = extras.getString(EXTRA_CLUB_ID, clubId);

4,onRefresh函数
在可能出现异常的页面,应当重写onRefresh函数,该函数是产生错误后点击刷新的回调函数,在这里我们可以进行重新初始化布局等操作。其参数可以带有错误信息,通过判断其中的信息,可以决定如何从错误中恢复。

关于空指针

Java
必须在任何可以使用@Nullable@NonNull的地方使用它。并且不能忽略任何可能为空的情况。目前使用上述注解的常见位置有:函数参数、全局变量、函数返回值。
Kotlin
禁止使用!!操作符,所有可空类型必须判断。

关于Sharepreferences文件

创建一个Kotlin文件。以存储用户信息为例:

  1. class SpUserInfo {
  2. var token by SpString
  3. var userId by SpString
  4. var nickname by SpString
  5. var ImUserId by SpString
  6. var username by SpString
  7. }

SpString表示该属性与Sp文件中的String类型相关联,提供默认值null

委托类型 影射类型 Nullable 默认值
SpInt int false 0
SpDefaultInt(default) int false default
SpBoolean boolean false false
SpDefaultBoolean(default) boolean false default
SpFloat float false 0f
SpDefaultFloat(default) float false default
SpLong long false 0l
SpDefaultLong(default) long false default
SpString String true null
SpDefaultString(default) String false default
SpStringSet Set<String> true null
SpDefaultStringSet(default) Set<String> false default
SpSerializable Serializable true null
SpDefaultSerializable(default) Serializable false default
SpAsyncSerializable* Serializable true null

说明SpAsyncSerializable 异步类型的映射委托,其类型必须为可空,建议对于非基本数据类型或StringSet以外的对象使用此委托,能够提高系统运行效率。

使用方法:
Java:

  1. SpSettings spSettings = SpUtil.INSTANCE.register(new SpSettings());

在OnDestroy方法中销毁

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. SpUtil.INSTANCE.unRegister(spSettings);
  5. }

Kotlin:

  1. val mUserInfo = SpUtil.register(this, SpUserInfo())

在OnDestroy方法中销毁

  1. override fun onDestroy() {
  2. super.onDestroy()
  3. if (mLoadingView != null) mLoadingView = null
  4. SpUtil.unRegister(mUserInfo)
  5. }

三、工具类说明

3.1 基类说明

BaseFragmentActivity

1,数据临时保存功能(限Kotlin)

  1. private var id by saveString { intent.getStringExtra(EXTRA_BOARD_ID) }

通过上述写法,id这个值将在第一次被使用时调用函数{ intent.getStringExtra(EXTRA_BOARD_ID) }获取值,如果该Activity被意外销毁,此值将会被保存在onSaveInstanceState方法的Bundle对象,并在Activity恢复时取出。

2,显示正在加载效果

  1. showLoading(); //显示
  2. hideLoading(); //隐藏

3,获取用户信息

对于Kotlin

  1. mUserInfo.token //获取Token

对于Java

  1. getMUserInfo().getToken(); //获取Token

BaseMvpModel

注册网络请求
注册Subscription对象以便统一关闭请求,以免造成内存泄漏
Java:

  1. Subscription subscribe = mService.getDynamic(RetrofitUtil.buildParams(params))
  2. .subscribeOn(Schedulers.io())
  3. .observeOn(AndroidSchedulers.mainThread())
  4. .subscribe(onNext, onError);
  5. register(subscribe);

Kotlin:

  1. mService.getList(RetrofitUtil.buildParams(params))
  2. .subscribeOn(Schedulers.io())
  3. .observeOn(AndroidSchedulers.mainThread())
  4. .subscribe(onNext, onError)
  5. .register()

关闭全部请求

可以在Activity的onDestroy中关闭全部请求

  1. public void onDestroy(){
  2. super.onDestroy();
  3. mModel.onDestroy();
  4. }

生成Multipart请求体
Java:

  1. RequestBody requestToken = createRequestBody(token);
  2. MultipartBody.Part requestImgPart = createRequestBody(imageFile, "uploadfile");

Kotlin:

  1. val requestToken = token.createRequestBody();
  2. val requestImgPart = imageFile.createRequestBody("uploadfile");

3.2 util类

网络相关

解析生成Service类

java:

  1. private Service mService = RetrofitUtil.getInstance().create(Service.class);

kotlin:

  1. private val mService by ServiceCreate(Service::class.java)

图片相关

图片加载框架

本应用使用Glide作为图片加载框架,图片加载类为ImageModel,该控件具有设置圆形圆角矩形高斯模糊等效果的功能。
使用方式如下:

1,普通图片加载方式
Java:

  1. ImageModel.inflate(context, pic)
  2. .defaultDrawable(R.drawable.default_cover)
  3. .into(imageView);

2,设置圆形图片
Java:

  1. ImageModel.inflate(context, pic)
  2. .asCircleBitmap()
  3. .defaultDrawable(R.drawable.default_cover)
  4. .into(imageView);

3,混合设置图片效果

  1. ImageModel.inflate(context, pic)
  2. .asBlurBitmap() //设置高斯模糊
  3. .asRoundCornersBitmap(10) //设置圆角矩形
  4. .defaultDrawable(R.drawable.default_cover)
  5. .into(imageView);

注意:给图片设置多种效果时,效果的绘制顺序与调用顺序相同,在上面的例子中,图片被设置完高斯模糊的效果之后才会被裁剪为圆角矩形。

从相册选择图片

在Activity任意位置调用如下代码打开相册,该方法打开仅能选择一张图片:

  1. ImageGridActivity.start(this)

通常需要与GrideView配合选择多图,可以调用如下方法:

  1. ImageGridActivity.start(this9)

获取选择结果的方式是在Activity的onActivityResult中添加如下代码:
Java:

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. ImageGridActivity.onResult(requestCode, resultCode, data, new Function1<List<ImageItem>, Unit>() {
  5. @Override
  6. public Unit invoke(List<ImageItem> imageItems) {
  7. return null;
  8. }
  9. });
  10. }
  11. }

Kotlin:

  1. override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  2. super.onActivityResult(requestCode, resultCode, data)
  3. ImageGridActivity.onResult(requestCode, resultCode, data, this::onImageResult)
  4. }
  5. private fun onImageResult(imageItems: List<ImageItem> ){
  6. }

点击查看大图

查看单张图片

  1. ScanPictureActivity.start(activity, url);

多图查看

  1. public void onImageClick(ArrayList<String> picList, int index) {
  2. ScanPictureActivity.start(this,picList,index);
  3. }

通用

  1. private fun onImageClick(image: MultipartImageView.Image) {
  2. if (image is SubjectDetailContract.Model.DetailResponse.PicsBean) {
  3. ScanPictureActivity.start(this, mMultiImages, image) { (it as SubjectDetailContract.Model.DetailResponse.PicsBean).pic_original }
  4. }
  5. }

如果Pic继承自PicBean

  1. public void onPicClick(@NonNull MultipartImageView.Image image) {
  2. if (mDynamicResponse != null)
  3. ScanPictureActivity.start(this, mDynamicResponse.getPics(), image);
  4. }

对话框相关

Alert对话框

Java:

  1. UIUtils.showDialog(Activity, "确定删除吗?", new IDialogClickListenr() {
  2. @Override
  3. public void onConfirm() {
  4. // 确定事件
  5. }
  6. @Override
  7. public void onCancel() {
  8. // 取消事件
  9. }
  10. });

kotlin:

  1. dialog("确定要删除吗?") {
  2. buttonPositive("确定") {
  3. //todo
  4. }
  5. buttonNegative("取消") {
  6. //todo
  7. }
  8. }

或者

  1. dialog("确定要删除吗?") {
  2. buttonPositive{
  3. //todo
  4. }
  5. }

buttonPositive必须有事件

日期选择对话框

  1. DatePickerDialog.start(this, new DatePickerDialog.OnDateSetListener() {
  2. @Override
  3. public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) {
  4. }
  5. });

如果需要设置初始值,可以传入yyyy-MM-dd格式的字符串

  1. DatePickerDialog.start(this, "2016-11-25",new DatePickerDialog.OnDateSetListener() {
  2. @Override
  3. public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) {
  4. }
  5. });

仿iOS的ActionSheet

Java

  1. AppUtils.showActionSheet(this, mSheetItems, new ActionSheet.ActionSheetListener() {
  2. @Override
  3. public void onSheetClick(ActionSheet actionSheet, int stringId) {
  4. switch (stringId) {
  5. case R.string.share: //分享
  6. break;
  7. case R.string.edit_activity: //编辑活动
  8. break;
  9. case R.string.cancel_activity: // 取消活动
  10. break;
  11. }
  12. }
  13. });

Kotlin:

  1. showActionSheet(intArrayOf(R.string.share,R.string.edit_activity,R.string.cancel_activity)){ id->
  2. when(id){
  3. R.string.share->{} //分享
  4. R.string.edit_activity->{} //编辑活动
  5. R.string.cancel_activity->{} //取消活动
  6. }
  7. }

定位相关

观察位置信息

通过LocationObservable.from(Context)可以创建一个定位观察对象。

  1. LocationObservable.from(this)
  2. .subscribe { location ->
  3. // 处理回调
  4. }

通过以上方式可以立即获得一次定位回调。

如果我们需要连续的获取位置,可以采用如下方式:

  1. val subscribe = LocationObservable.from(this)
  2. .interval(5) // 这里设置每5秒获得一次回调信息
  3. .subscribe { location ->
  4. // 处理回调
  5. }
  6. // 需要在合适的位置取消订阅
  7. subscribe.unSubscribe()

如果我们需要固定次数的定位回调,可以采用如下方式:

  1. LocationObservable.from(this)
  2. .interval(5) // 这里设置每5秒获得一次回调信息
  3. .take(3) // 获取3次位置信息,结束之后会自动取消订阅
  4. .subscribe { location ->
  5. // 处理回调
  6. }

在新页面选择位置并回调

在Activity的任意位置调用

  1. BMapActivity.start(this);

然后在onActivityResult中使用如下代码接收回调

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. BMapActivity.onResult(requestCode, resultCode, data, new Function3<String, Double, Double, Unit>() {
  5. @Override
  6. public Unit invoke(String address, Double latitude, Double longitude) {
  7. return null;
  8. }
  9. });
  10. }

3.3 应用内util

骑行记录

单选骑行记录

在Activity中使用如下代码打开选择页面

  1. SelectRideActivity.startCrop(this);

然后在onActivityResult中处理回调:

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. SelectRideActivity.onResult(requestCode, resultCode, data, new Function2<String, RideViewContract.Model.TripDetailResponse, Unit>() {
  5. @Override
  6. public Unit invoke(String crop, RideViewContract.Model.TripDetailResponse tripDetailResponse) {
  7. return null;
  8. }
  9. });
  10. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注