我有一个 LazyColumn 连接到 Room 数据库查询。我正在扩展“看得见风景的房间”示例(https://developer.android.com/codelabs/android-room-with-a-view-kotlin#0),但我使用的是 LazyColumn RecyclerView 的用于显示表条目列表。
插入条目(Word)后,我希望 LazyColumn 自动滚动到包含新插入条目的行(或至少使其可见)。请注意,列表查询是按字母顺序排序的,新行不会出现在列表末尾。
// table:
@Entity(tableName = "word_table")
class Word(
@PrimaryKey @ColumnInfo(name = "word") val text: String
)
// DAO:
@Query("SELECT * FROM word_table ORDER BY word COLLATE NOCASE ASC")
fun getAlphabetizedWords(): Flow<List<Word>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(word: Word)
// repository:
val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()
suspend fun insert(word: Word) {
wordDao.insert(word)
}
// viewModel:
val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()
fun insert(word: Word) = viewModelScope.launch {
repository.insert(word)
}
// simplified composable:
@Composable
fun WordList() {
val list by mWordViewModel.allWords.observeAsState(listOf())
LazyColumn() {
items(list) { word ->
Row() {
Text(word.text)
}
}
}
}
否则一切工作正常,存储库和视图模型是按照一般准则实现的。有什么想法吗?
val listState = rememberLazyListState(initialFirstVisibleItemIndex = (list.size - 1))
LazyColumn(state = listState){
items(list){
...
}
}
您可以使用 ListState 来操纵滚动位置,有点像您使用 LayoutManager 那样。
我将使用的工作流程:
您可以将旧列表与新列表进行比较,如下所示:
firstList.filter { it.name !in secondList.map { item -> item.name } }
然后使用for循环进行计数,直到在列表中找到单词
根据位置根据编号滚动到位置
这是我自己的看法。插入时,行 ID 存储在变量中。当布局重组时,通过列表搜索找到最后插入行的索引,并将 LazyColumn 滚动到该索引。
编辑:我修改了答案中的代码,使其更加通用并符合 Kotlin 习惯用法。
// table:
@Entity(tableName = "word_table")
class Word(
val text: String,
// primary key is autogenerated, also variable
// because it has to be modified:
@PrimaryKey(autoGenerate = true) var id: Long = 0L
)
// DAO:
@Query("SELECT * FROM word_table ORDER BY text COLLATE LOCALIZED ASC")
fun getAlphabetizedWords(): Flow<List<Word>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertBase(word: Word) : Long
suspend fun insert(word: Word)
{
// explicitly store inserted row id in entity object,
// it will not happen automatically:
word.id = insertBase(word)
}
// repository:
val mAllWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()
suspend fun insert(word: Word)
{
wordDao.insert(word)
}
// viewModel:
val mAllWords: LiveData<List<Word>> = repository.mAllWords.asLiveData()
var mInsertedId: Long = -1L; // the id of the last inserted row
fun insert(word: Word) = viewModelScope.launch
{
repository.insert(word)
mInsertedId = word.id // store id
}
fun getInsertedId() : Long
{
val id = mInsertedId
mInsertedId = -1L // clear stored value
return id
}
// helper used by the "Add" button composable (not shown here):
fun addWordToDb(text: String)
{
if(!text.isBlank())
mWordViewModel.insert(Word(text))
}
// simplified composable:
@Composable
fun WordList(viewModel: WordViewModel)
{
val list by viewModel.allWords.observeAsState(listOf())
val listState = rememberLazyListState()
// scroll to inserted item:
LaunchedEffect(list)
{
val insertedId = viewModel.getInsertedId()
if(insertedId != -1L){
// find index of last inserted item in list:
val index = list.indexOfFirst{ it.id == insertedId }
if(index != -1)
listState.animateScrollToItem(index)
}
}
// display list:
LazyColumn(state = listState)
{
items(list) { word ->
Row() {
Text(word.text)
}
}
}
}
感谢 F.Mysir 和 eimmer 的投入。顺便说一句,我尝试从 SQLite/Room 获取索引,如下所示:
@Query("SELECT COUNT(*) FROM word_table WHERE LOWER(text) < LOWER(:searchString)")
suspend fun getWordIndex(searchString: String): Int
...并且它(几乎)适用于 ASCII,但对于包含非 ASCII 字符的字符串则失败,即使数据库区域设置设置为具有不同字母表的语言也是如此。