当前位置:首页 » 《资源分享》 » 正文

使用Kotlin+JetPack 从零开发自己的日记App_Jinxed.的博客

11 人参与  2021年10月01日 12:23  分类 : 《资源分享》  评论

点击全文阅读


前言

本人Android小菜鸡一枚,开发该app的主要目的是为了巩固kotlin语法,学习使用JetPack进行一个完整App的开发,不得不说,Kotlin+JetPack开发起来真是无敌丝滑(末尾附上项目地址)

介绍

😄时刻是一个使用纯kotlin和Jetpack实现的具有简单增删改查功能的日记App

效果图

开始页面请添加图片描述请添加图片描述请添加图片描述请添加图片描述请添加图片描述

使用的相关技术

  • DataBinding :省去了繁琐的findViewById工作,而且通过和ViewMode配合,可以直接把ViewModel的数据渲染到界面上
  • Paging:分页加载库
    郭神的博客
  • Room:封装了sqlite,将表映射成Java或Kotlin对象,我觉得相比于之前使用的greendao,room最大的优势是支持了kotlin的flow,通过和paging配合,实现自动的刷新数据链接
  • dataStore:用来代替SP的数据持久化方案,最大的优势是支持异步获取保存数据,通过一个PreferencesKey保存对应类型泛型的方法保证了类型安全
  • ViewModel:用于保存actvity的数据中转
  • Glide:图片加载,这个太牛皮了就不介绍了
  • liveData:代替EventBus用于进行事件分发

部分代码展示

  • 日记的主题颜色更改
/**
     * 设置日记编辑的背景主题
     * 判断颜色是否是亮色,设置对应主题字体
     *
     * todo 自定义背景功能
     */
    private fun setThemeBackGround(@ColorRes colorRes: Int) {
        val color: Int = ContextCompat.getColor(this, colorRes)
        mBinding.clEditDairy.setBackgroundResource(colorRes)
        setStatusBarColor(color)

        if (ColorUtils.calculateLuminance(color) > 0.6) {
            // 亮色
            setAndroidNativeLightStatusBar(this, true)
        } else {
            // 暗色
            setAndroidNativeLightStatusBar(this, false)
        }

        val shape = GradientDrawable()
        shape.color = ColorStateList.valueOf(addColorDepth(color))
        shape.cornerRadius = dp2px(baseContext, 5f)

        ripperDrawable =
            RippleDrawable(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.DairyEditHintText)), shape, null)
    }

 /**
     * 将原有颜色的R,G,B值依次减去20得到一个更深的颜色
     */
    private fun addColorDepth(color: Int): Int {
        var red = color and 0xff0000 shr 16
        var green = color and 0x00ff00 shr 8
        var blue = color and 0x0000ff

        red = if (red - 25 > 0) red - 25 else 0
        green = if (green - 25 > 0) green - 25 else 0
        blue = if (blue - 25 > 0) blue - 25 else 0
        return Color.rgb(red, green, blue)
    }
  • 日记意外退出恢复功能
/**
	这里把保存操作写在onPause里,因为不管是手动退出app还是意外终止app都一定会走到该方法,然后增加一个标记表示是意外中止
**/
override fun onPause() {
        super.onPause()
        if (!TextUtils.isEmpty(viewModel.dairyContent.value) && isNeedToSaved) {
            DataStoreUtils.saveSyncStringData(RECOVER_CONTENT, viewModel.dairyContent.value!!)
            DataStoreUtils.saveSyncStringData(RECOVER_TITLE, mBinding.appBar.getTitle())
        }
    }

/**
	恢复操作,开始一个协程,异步获取
**/
 private fun tryToRecoverDairy() {
        lifecycleScope.launch(Dispatchers.Main) {
            DataStoreUtils.readStringFlow(RECOVER_CONTENT).first {
                if (it.isNotEmpty()) {
                    recoveredContent = it
                }
                true
            }
            DataStoreUtils.readStringFlow(RECOVER_TITLE).first {
                if (it.isNotEmpty()) {
                    recoveredTitle = it
                }
                true
            }
 }
  • 主界面获取日记列表的操作
/**
	Activity层
	Activity里调用到viewModel
**/
private fun getDairyData() {
        lifecycleScope.launch(Dispatchers.Main) {
            viewModel.getAllDairy().collect {
                dairyAdapter.submitData(it)
            }
        }
    }

/**
	ViewModel层
	ViewModel当作中转站,通过持有的仓库对象repository访问到数据库
**/
fun getAllDairy(): Flow<PagingData<DairyItem>> {
        return repository.getAllDairyData().cachedIn(viewModelScope)
    }

/**
     repository层
     查找表获得所有日记
 **/
    fun getAllDairyData(): Flow<PagingData<DairyItem>> {
        return Pager(
            config = PagingConfig(PAGE_SIZE, maxSize = 150),
            pagingSourceFactory = dairyDao.getAllDairy().asPagingSourceFactory()
        ).flow
    }

/**
	 * Dao层  这里通过和Paging的factory绑定,数据库变动后会自动的刷新到paging中
     * 用DataSource把数据绑定到Paging  响应式
     * 根据时间降序查找
     */
    @Query("SELECT * FROM DairyEntity order by createTime desc")
    fun getAllDairy(): DataSource.Factory<Int, DairyItem>
  • 调起手机的相册和相机
 /**
 		这里我使用了新版的启动器方式来调起,只需要写一个启动器和一个启动协议
     * 初始化相册启动器
     */
    private val toAlbumLauncher =
        registerForActivityResult(ToSystemAlbumResultContract()) {
            if (it != null) {
                viewModel.pictureList.add(it)
                viewModel.isChanged.value = true
                // 只需要刷新新增的一个和尾部,也是就itemCount为2
                pictureSelectAdapter.notifyItemRangeChanged(viewModel.pictureList.size, 2)
            }
        }

    /**
     * 初始化相机启动器
     */
    private val toCameraLauncher =
        registerForActivityResult(ActivityResultContracts.TakePicture()) {
            if (it) {
                viewModel.pictureList.add(currentUri)
                viewModel.isChanged.value = true
                pictureSelectAdapter.notifyItemRangeChanged(viewModel.pictureList.size, 2)
                galleryAddPic()
            }
        }

    /**
     * 跳转到系统相册
     * 传入参数  ArrayList<Bitmap>  图片列表
     * 返回参数  Int                标识哪一张图片被删除
     */
    inner class ToSystemAlbumResultContract : ActivityResultContract<Unit, Uri?>() {
        override fun createIntent(context: Context, input: Unit?): Intent {
            val intent = Intent(
                Intent.ACTION_PICK,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            )
            intent.type = "image/*"
            return intent
        }

        override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
            return intent?.data
        }
    }

最后

源码中代码的注释非常详细,有兴趣的大佬可以进去看看呐
Kotlin的扩展函数用起来贼爽

github地址:LongerRelationShip

有兴趣的大佬可以下载提提建议
时刻下载地址
请添加图片描述
后期开发功能

  • 记账功能
  • 录音笔记功能
  • App的图库功能
  • 日记的收藏和设置私密
感谢
  • 界面设计很大部分参考了该设计
    美贴 记事原型
  • 代码的学习参考
    Eyepetizer
    官方的代码示例
    Sunflower

点击全文阅读


本文链接:http://zhangshiyu.com/post/29108.html

启动器  功能  日记  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1