如何使用JetPack中的过滤器实现搜索
#kotlin #android #jetpackcompose

各种应用程序中非常常见的功能是允许用户进行搜索。搜索可以从文本,类别或某些信息中完成,以过滤来自应用的数据。

因此,我们改善了用户的经验,这些体验倾向于找到最容易寻求的东西,您同意吗?现在是问题:

“我们如何实现JetPack组成过滤器搜索?”


tl; dr

如果您的目标是在不了解动机的情况下检查最终代码,则可以在下面看到:

App em execução apresentando um campo de texto e uma lista de produtos em coluna. Ao digitar no campo de texto, aparece apenas os produtos que contém o nome ou descrição com o valor do campo de texto

最重要的是保持过滤停机时间的ViewModel,在这种情况下是过滤产品:

class ProductsListViewModel : ViewModel() {

    private val products = MutableStateFlow(emptyList<Product>())
    private val _filteredProducts = MutableStateFlow(emptyList<Product>())
    val filteredProducts = _filteredProducts.asStateFlow()

    fun searchProducts(text: String) {
        _filteredProducts.value = if (text.isEmpty()) {
            products.value
        } else {
            products.value.filter {
                it.name
                    .contains(
                        text,
                        ignoreCase = true
                    ) || it.description
                    .contains(
                        text,
                        ignoreCase = true
                    )
            }
        }

    }

    init {
        products.value = List(10) {
            Product(
                name = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                description = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                price = BigDecimal(Random.nextInt(10, 1000))
            )
        }
        _filteredProducts.value = products.value
    }

}

我们有屏幕筛选以实现文本字段和产品列表:

val viewModel by viewModels<ProductsListViewModel>()
val products by viewModel.filteredProducts.collectAsState(initial = emptyList())
Column {
    var searchText by remember {
        mutableStateOf("")
    }
    OutlinedTextField(
        value = searchText,
        onValueChange = {
            searchText = it
            viewModel.searchProducts(searchText)
        },
        Modifier
            .padding(8.dp)
            .fillMaxWidth(),
        label = {
            Text(text = "Buscar")
        },
        leadingIcon = {
            Icon(Icons.Default.Search, "search icon")
        },
        placeholder = {
            Text(text = "O que você procura?")
        },
        shape = RoundedCornerShape(10.dp)
    )
    ProductsListScreen(
        products = products
    )
}

和代表产品列表的组成代码:

@Composable
fun ProductsListScreen(
    products: List<Product> = emptyList()
) {
    LazyColumn(Modifier.fillMaxSize()) {
        items(products) { p ->
            Column(
                Modifier
                    .clip(RoundedCornerShape(10.dp))
                    .padding(8.dp)
                    .fillMaxWidth()
                    .border(
                        1.dp,
                        Color.Gray.copy(alpha = 0.5f),
                        RoundedCornerShape(10.dp)
                    )
                    .padding(8.dp)
            ) {
                Text(text = p.name, fontWeight = FontWeight.Bold, fontSize = 24.sp)
                Text(text = p.description)
                Text(
                    text = p.price.toBrazilianCurrency(),
                    fontWeight = FontWeight.Bold,
                    style = TextStyle.Default.copy(color = Color(0xFF4CAF50)),
                    fontSize = 18.sp
                )
            }
        }
    }
}

案例您还想查看货币形成器:

private fun BigDecimal.toBrazilianCurrency(): String =
    NumberFormat.getCurrencyInstance(
        Locale("pt", "br")
    ).format(this)

现在,如果您的强度正在理解实现这一目标的步骤,那就是遵循阅读。


示例项目

要举例说明实现,让我们使用一个具有文本字段和产品列表的应用程序:

App em execução apresentando um campo de texto e uma lista de produtos em coluna. Ao digitar no campo de texto, não modifica os items da lista de produtos. A lista de produtos é rolável com até 10 itens

一个简单的应用程序仅专注于过滤产品的功能。

屏幕代码

如果要复制完全相同的结果,则也可以访问屏幕的屏幕:

Column {
    val products by remember {
        mutableStateOf(List(10) {
            Product(
                name = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                description = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                price = BigDecimal(Random.nextInt(10, 1000))
            )
        })
    }
    var searchText by remember {
        mutableStateOf("")
    }
    OutlinedTextField(
        value = searchText,
        onValueChange = {
            searchText = it
        },
        Modifier
            .padding(8.dp)
            .fillMaxWidth(),
        label = {
            Text(text = "Buscar")
        },
        leadingIcon = {
            Icon(Icons.Default.Search, "search icon")
        },
        placeholder = {
            Text(text = "O que você procura?")
        },
        shape = RoundedCornerShape(10.dp)
    )
    ProductsListScreen(
        products = products
    )
}

这是代表产品清单和货币组合人的组合:

@Composable
fun ProductsListScreen(
    products: List<Product> = emptyList()
) {
    LazyColumn(Modifier.fillMaxSize()) {
        items(products) { p ->
            Column(
                Modifier
                    .clip(RoundedCornerShape(10.dp))
                    .padding(8.dp)
                    .fillMaxWidth()
                    .border(
                        1.dp,
                        Color.Gray.copy(alpha = 0.5f),
                        RoundedCornerShape(10.dp)
                    )
                    .padding(8.dp)
            ) {
                Text(text = p.name, fontWeight = FontWeight.Bold, fontSize = 24.sp)
                Text(text = p.description)
                Text(
                    text = p.price.toBrazilianCurrency(),
                    fontWeight = FontWeight.Bold,
                    style = TextStyle.Default.copy(color = Color(0xFF4CAF50)),
                    fontSize = 18.sp
                )
            }
        }
    }
}

private fun BigDecimal.toBrazilianCurrency(): String =
    NumberFormat.getCurrencyInstance(
        Locale("pt", "br")
    ).format(this)

准备好了!这足以启动过滤器的实现。

查找屏幕信息的ViewModel

我们需要认为的第一件事是,过滤器是数据操纵,也就是说,理想是这种羊毛是屏幕的其他地方。

因此,我们需要创建一个ViewModel来为我们维护这种羊毛,并且可以开始包含代表数据源的产品列表,即屏幕上的所有产品:

class ProductsListViewModel : ViewModel() {

    private val products = MutableStateFlow(emptyList<Product>())

    init {
        products.value = List(10) {
            Product(
                name = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                description = LoremIpsum(Random.nextInt(1, 10)).values.first(),
                price = BigDecimal(Random.nextInt(10, 1000))
            )
        }
    }

}

通常,数据源由数据库或通过REST API表示。

从那时起,我们就有开始数据处理所需的一切。

添加数据以表示过滤器

在过滤器的情况下,我们需要另一个列表来表示过滤的产品,毕竟,产品列表代表数据源,不应修改:

class ProductsListViewModel : ViewModel() {

    private val products = MutableStateFlow(emptyList<Product>())
    private val _filteredProducts = MutableStateFlow(emptyList<Product>())
    val filteredProducts = _filteredProducts.asStateFlow()

    fun searchProducts(text: String) {
        _filteredProducts.value = if (text.isEmpty()) {
            products.value
        } else {
            products.value.filter {
                it.name
                    .contains(
                        text,
                        ignoreCase = true
                    ) || it.description
                    .contains(
                        text,
                        ignoreCase = true
                    )
            }
        }

    }

    init {
        // ...
        _filteredProducts.value = products.value
    }

}

如果这种方式看起来很复杂,让我们了解他的所作所为:

  • init:它初始化了必要的属性:
    • 将代表无法修改的数据源的产品列表
    • 用与源相同的值过滤的产品列表,因为在初始状态(无需搜索文本),它们都会呈现所有产品。
  • searchProducts():母亲从文本中进行搜索:
    • 首先,我们验证文本的值是否为空,如果是空的,则需要指出过滤产品的值与数据源具有相同的值,如果相反,我们应用了过滤器羊毛。li> li>
    • 该过滤器是使用Collection filter()制成的,该收集允许您添加条件,在这种情况下,仅返回包含通过Parano收到的文本的名称或描述的产品。
    • 搜索的来源将始终是数据的来源,因为除了拥有所有产品外,从未更改。
  • 属性filteredProducts是必须在屏幕上阅读的废话的等级。

现在我们有了ViewModel的视图,它只是连接到屏幕。

从文本更改事件中持有过滤器

在屏幕的屏幕上,我们只需要创建ViewModel,阅读过滤产品并致电产品搜索产品更改事件:

val viewModel by viewModels<ProductsListViewModel>()
val products by viewModel.filteredProducts.collectAsState(initial = emptyList())
Column {
    var searchText by remember {
        mutableStateOf("")
    }
    OutlinedTextField(
        value = searchText,
        onValueChange = {
            searchText = it
            viewModel.searchProducts(it)
        },
        // ...
    )
    ProductsListScreen(
        products = products
    )
}

App em execução apresentando um campo de texto e uma lista de produtos em coluna. Ao digitar no campo de texto, aparece apenas os produtos que contém o nome ou descrição com o valor do campo de texto

准备好了!我们实施了一个代码以在具有JetPack Compiss的应用程序上执行过滤器。值得注意的是,这是一个简单的实现,但根据范围的不同,可能会有更多步骤,因为搜索各种来源,治疗等。

您如何看待此实施?你以不同的方式这样做吗?享受并发表评论ð