使用了Litepal作为数据库ORM框架,还有BRAVH来简化RecyclerView的适配器的写法。
这里记录下开发途中遇到的坑。
创建自己的Application类 因为LitePal和MobSDK都需要对Application进行修改,所以最好实现自己的Application:
1 2 3 4 5 6 7 8 9 10 11 import android.app.Applicationimport com.mob.MobSDKimport org.litepal.LitePal class LifeUpApplication : Application () { override fun onCreate () { super .onCreate() MobSDK.init (this ) LitePal.initialize(this ); } }
LitePal最新版的一些用法 实体类继承LitePalSupport 而不是原先的DataSupport。
另外Kotlin的var会自动实现getter和setter:
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 import org.litepal.crud.LitePalSupportimport java.util.*data class TaskModel ( var content: String, var remark: String, var taskExpireTime: Date?, var taskRemindTime: Date?, var relatedAttribute1: String?, var relatedAttribute2: String?, var relatedAttribute3: String?, var taskUrgencyDegree: Int , var taskDifficultyDegree: Int , var taskFrequency: Int , var userId: Int ?, var isShared: Boolean , var taskType: Int ? ) : LitePalSupport() { var id: Long ? = null var taskId: Long ? = null var createdTime: Long = 0 var updatedTime: Long = 0 var expReward: Int = 0 var endDate: Date? = null var taskStatus: Int = 0 }
而对数据库的操作方法也从调用DataSupport类的方法变成了调用LitePal类 的方法。
如:
1 2 3 fun getFirstAttribute () : AttributeModel? { return LitePal.findFirst(AttributeModel::class .java) }
其他的话,没什么不一样。
刷新RecyclerView数据 需要注意的一个坑是,RecyclerView的Adapter用的List不能重新指向一个新的引用。
必须用回原本的引用更改数据,然后再调用notifyDataSetChanged()
就能刷新数据了。
1 2 3 4 5 6 private fun refreshDataSet () { mList.clear() mList.addAll(todoService.getUncompletedTodoList()) mAdapter.notifyDataSetChanged() }
当返回或是切换Fragment刷新数据 1 2 3 4 5 6 7 8 9 10 11 override fun onResume () { super .onResume() refreshDataSet() } override fun onHiddenChanged (hidden: Boolean ) { super .onHiddenChanged(hidden) if (!hidden) { refreshDataSet() } }
Lottie的动画回调调用多次的问题 原本在Lottie的结束和取消回调里调用一个对话框的显示的方法,实际使用发现这个回调会被调用数次?(有时候1次,有时候2次,3次。很是诡异。)
解决方法自然是加个boolean值标识:
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 var isEverShowDialog = false mView.addAnimatorListener(object : Animator.AnimatorListener { override fun onAnimationRepeat (p0: Animator ?) { } override fun onAnimationEnd (p0: Animator ?) { if (!isEverShowDialog) { showDialogAbbr(item) isEverShowDialog = true } } override fun onAnimationCancel (p0: Animator ?) { if (!isEverShowDialog) { showDialogAbbr(item) isEverShowDialog = true } } override fun onAnimationStart (p0: Animator ?) { } })
获得经验值的进度条动画的实现 讲真,这个小小的动画实现可能是我花费时间最长的一个小模块了。
首先要解决无论是3个属性都选择了,还是只选择了1个,2个都能正确显示 的问题。
其次,需要计算获得经验值前的经验值与等级的占比,来正确显示ProgressBar(进度条)的进度。
还需要判断会不会升级,如果升级要将进度条置为0再进行下一步操作。
还要保证进度增长的时间是大概一致的。
还需要用到多线程来实现进度的缓慢增长。
等等……
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 private fun doProgressOrigin (dialogView: View , item: TaskModel , index: Int ) { val relatedAttribute = when (index) { 1 -> item.relatedAttribute1 2 -> item.relatedAttribute2 3 -> item.relatedAttribute3 else -> return } if (relatedAttribute.isNullOrBlank()) return val nowExpTotal = when (index) { 1 -> attributeService.getAttributeExpByString(relatedAttribute ?: "" ) - item.expReward 2 -> attributeService.getAttributeExpByString(item.relatedAttribute2 ?: "" ) - item.expReward 3 -> attributeService.getAttributeExpByString(item.relatedAttribute3 ?: "" ) - item.expReward else -> return } var nowExp = nowExpTotal - attributeLevelService.getAttributeLevelByExp(nowExpTotal).startExpValue val levelMaxExp = attributeLevelService.getAttributeLevelByExp(nowExpTotal).endExpValue - attributeLevelService.getAttributeLevelByExp(nowExpTotal).startExpValue val progressBar = when (index) { 1 -> dialogView.npb_first 2 -> dialogView.npb_Sec 3 -> dialogView.npb_thr else -> return } progressBar.progress = nowExp * 100 / levelMaxExp var finalProgress = (nowExp + item.expReward) * 100 / levelMaxExp if (finalProgress >= 100 ) { thread = Thread { try { val toMax = progressBar.max - progressBar.progress while (threadRunning == true && progressBar.progress != progressBar.max) { activity?.runOnUiThread { progressBar.incrementProgressBy(if (toMax / 20 > 0 ) toMax / 20 else 1 ) } Thread.sleep(40 ) } val newLevelModel = attributeLevelService.getAttributeLevelByExp(nowExpTotal + item.expReward) val textViewLevel = when (index) { 1 -> dialogView.tv_levelFirst 2 -> dialogView.tv_levelSec 3 -> dialogView.tv_expThr else -> return @Thread } activity?.runOnUiThread { progressBar.progress = 0 textViewLevel.text = "LV${newLevelModel.levelNum} " } nowExp = nowExpTotal + item.expReward - newLevelModel.startExpValue val nextMaxExpTotal = newLevelModel.endExpValue val nextMaxExp = newLevelModel.endExpValue - newLevelModel.startExpValue finalProgress = nowExp * 100 / nextMaxExp Thread.sleep(40 ) while (threadRunning == true && progressBar.progress != finalProgress) { activity?.runOnUiThread { progressBar.incrementProgressBy(if (finalProgress / 30 > 0 ) finalProgress / 30 else 1 ) } Thread.sleep(40 ) } } catch (e: InterruptedException) { e.printStackTrace() } } thread?.start() } else { thread = Thread { try { var progressToGo = finalProgress - progressBar.progress while (threadRunning == true && progressBar.progress != finalProgress) { activity?.runOnUiThread { progressBar.incrementProgressBy(if (progressToGo / 30 > 0 ) progressToGo / 30 else 1 ) } Thread.sleep(40 ) } } catch (e: InterruptedException) { e.printStackTrace() } } thread?.start() }
自定义View对话框的宽和高 用了网上各种改跟View,getWindow然后setLayout的方法通通行不通。
最后发现在自定义View的根View里加上android:minHeight
和android:minWidth
两个属性就好了。
其实那个按钮是OptionMenu里面的Item。
设置Menu的时候将app:showAsAction
设为always
就可以强制显示图标了。
Menu布局文件:
1 2 3 4 5 6 7 8 9 10 11 12 <menu xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:app ="http://schemas.android.com/apk/res-auto" xmlns:tools ="http://schemas.android.com/tools" tools:context ="net.sarasarasa.lifeup.activities.AddToDoItemActivity" > <item android:id="@+id/action_finish" android:icon="@drawable/ic_done_white_24dp" android:orderInCategory="100" android:title="完成" app:showAsAction="always" /> </menu >
Activity代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 override fun onCreateOptionsMenu (menu: Menu ) : Boolean { menuInflater.inflate(R.menu.menu_add_to_do_item, menu) return true } override fun onOptionsItemSelected (item: MenuItem ) : Boolean { when (item.itemId) { R.id.action_finish -> { if (check()) { addItem(getItem()) } return true } else -> return super .onOptionsItemSelected(item) } }