Android 上的浮动窗口:Jetpack Compose & Room

62 篇文章 1 订阅
10 篇文章 0 订阅

你有没有想过如何制作 Facebook Heads 和其他应用程序使用的那些浮动窗口?您是否曾经想在您的应用程序中使用相同的技术?这很容易,我将指导您完成整个过程。

在本文中,我将教您如何使用 Jetpack Compose 和 Room 构建简单的主应用程序。

理念

我们需要一个想法来应用浮动技术。让我们构建一个允许快速记笔记的应用程序。这是一个完美的案例。为了写一个快速的笔记,你通常不想离开你当前的任务。

让我们将其实现为具有永久通知的长期运行服务 - 随时准备好在需要时提供服务。用户点击我们的通知并开始添加注释。

对于您的应用,您可以更改此行为。不需要长期运行的服务。

主应用

好吧,我们需要从乞讨开始。让我们先构建主应用程序。它使我们能够在现实生活场景中展示集成过程。

我决定使用Kotlin、Jetpack Compose和Room,并构建一个非常简单的笔记应用程序。

顺便说一句,您需要将 Android Studio Canary 安装为 Jetpack Compose,目前在稳定版本中不可用。

Room

Room持久性库在SQLite 之上提供了一个抽象层,以允许更强大的数据库访问,同时利用 SQLite 的全部功能。

让我们从一个简单的实体开始我们的笔记:

@Entity  
data class Note(  
  @PrimaryKey val id: Int,  
  @ColumnInfo(name = "content") val content: String  
)

以及对应的DAO:

@Dao  
interface NotesDao {  

  @Query("SELECT * FROM note")  
  fun getAll(): List<Note>  

  @Insert  
  fun insert(note: Note)  

  @Delete  
  fun delete(note: Note)  

}

访问我们数据的最后一个缺失部分是AppDatabase类。同样,它非常简单:

@Database(entities = arrayOf(Note::class), version = 1)  
abstract class AppDatabase : RoomDatabase() {  

  abstract fun notes(): NotesDao  

}

视图模型

现在,通过Jetpack Compose 来ViewModel访问我们的数据。MutableState更改变量会自动运行使用它notes的 's 的重组。@Composable

Room 是使用Kotlin 协程访问的,因为我们无法在主线程上调用 SQLite。但是,仅使用基本方法,因为更复杂的场景超出了本文的范围。

class NotesViewModel(application: Application) : AndroidViewModel(application) {  

  private val db = Room.databaseBuilder(  
    application.applicationContext,  
    AppDatabase::class.java,  
    "db-notes"  
  ).build()  

  var notes by mutableStateOf(listOf<Note>())  
    private set  

  // Load initial data from Room asynchronously.  
  init {  
    GlobalScope.launch {  
      val items = db.notes().getAll()  
      viewModelScope.launch { notes = items }  
    }  
  }  

  fun addNote(note: String) {  
    // Generate ID in a simple way - from timestamp.  
    val noteObj = Note(
      (System.currentTimeMillis() % Int.MAX_VALUE).toInt(), 
      note
    )  
    notes = notes + listOf(noteObj)  
    GlobalScope.launch { db.notes().insert(noteObj) }  
  }  

  fun removeNote(note: Note) {  
    notes = notes - listOf(note)  
    GlobalScope.launch { db.notes().delete(note) }  
  }  

}

Composable

同样,保持简单并只创建两个复杂@Composable的一个用于添加注释,第二个用于列出和删除它们。

AddNote Composable

只是一个带有加号按钮的文本字段。而已。

@Composable
fun AddNote(title: String, onNoteAdded: (String) -> Unit) {
  Row {
    val text = remember { mutableStateOf(TextFieldValue("")) }
    TextField(
        value = text.value,
        onValueChange = { text.value = it },
        label = { Text(title) },
        modifier = Modifier
            .weight(1f, true)
            .padding(16.dp, 16.dp, 8.dp, 16.dp)
    )
    Button(
        onClick = {
            val newNote = text.value.text
            if (newNote.isNotBlank()) {
                onNoteAdded(newNote)
                text.value = TextFieldValue("")
            }
        },
        modifier = Modifier
            .padding(8.dp, 16.dp, 16.dp, 16.dp)
            .gravity(Alignment.CenterVertically)
    ) {
      Icon(
          asset = Icons.Filled.Add,
          modifier = Modifier.size(24.dp)
      )
    }
  }
}

ShowNotes Composable

Composable会LazyColumnFor自动列出我们所有的注释,当items来自视图模型时,会在更改时更新它。

@Composable
fun ShowNotes(items: List<Note>, onNodeRemoved: (Note) -> Unit) {
  LazyColumnFor(items = items) {
    Row {
      Text(
        text = it.content,
        modifier = Modifier
          .padding(16.dp, 4.dp, 4.dp, 4.dp)
          .weight(1f, true)
          .gravity(Alignment.CenterVertically)
      )
      TextButton(
        onClick = {
          onNodeRemoved(it)
        },
        contentPadding = InnerPadding(0.dp),
        modifier = Modifier
          .padding(4.dp, 4.dp, 16.dp, 4.dp)
          .gravity(Alignment.CenterVertically)
      ) {
        Icon(
          asset = Icons.Filled.Delete,
          modifier = Modifier.size(24.dp)
        )
      }
    }
  }
}

Activity - 粘贴

我们MainActivity只是将上面的所有代码粘合在一起并显示我们的两个可组合项。

class MainActivity : AppCompatActivity() {

  private val notesViewModel by viewModels<NotesViewModel>()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      Column {

        AddNote(getString(R.string.add_note)) {
          notesViewModel.addNote(it)
        }

        ShowNotes(notesViewModel.notes) {
          notesViewModel.removeNote(it)
        }

      }
    }
  }

}

在我们的示例应用程序中,您可以在源代码中注意到它,我通过在我的根 build.gradle 中包含以下行来使用 Localazy :

repositories {  
  maven { url "https://maven.localazy.com/repository/release/" }  
}  

dependencies {  
  classpath "com.localazy:gradle:1.5.2"  
}

在我的应用程序的 build.gradle 中:

apply plugin: 'com.localazy.gradle'

localazy {  
  readKey "a8922414862262844150-..."  
  writeKey "a8922414862262844150-..."  
}

这已经足够了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值