Android---Jetpack Compose学习002

news2024/11/15 18:42:04

Compose 布局。Compose 布局的目标:1)实现高性能;2)让开发者能够轻松编写自定义布局;3)在 Compose 中,通过避免多次测量布局子级可实现高性能。如果需要进行多次测量,Compose 具有一个特殊系统,即固有特性测量。

标准布局组件

\bullet 使用 Column 可将多个像垂直地放置在屏幕上;

\bullet 使用 Row 可将多个项水平地放置在屏幕上;

\bullet 使用 Box 可将一个元素放在另一个元素上。类似于 FrameLayout 布局。

修饰符

修饰符的作用类似于xml 布局中的布局参数。借助修饰符,可以修饰或扩充可组合项。我们可以使用修饰符来执行以下操作:

\bullet 更改可组合项的大小、布局、行为和外观;

\bullet 添加信息,如无障碍标签;

\bullet 处理用户输入;

\bullet 添加高级互动,如使元素可点击、可滚动、可拖动或可缩放。

示例:通过标准布局组件修饰符实现如下布局效果

1. 新建一个 PhotoGrapherCard.kt 文件,进行布局

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp

/**
 * @Author HL
 * @Date 2023/12/26 20:21
 * @Version 1.0
 */
@Composable
fun PhotographerCard(modifier: Modifier = Modifier){
    // TODO 水平放置,从左到右
    Row(
        modifier = Modifier
            .clip(RoundedCornerShape(4.dp)) // 设置圆角
            .background(color = MaterialTheme.colors.surface) // surface 是白色
            .clickable(onClick = {})
            .padding(all = 16.dp) // 设置点击时,水波纹效果

    ) {

        Surface (
            modifier = Modifier.size(50.dp),//设置图片大小
            shape = CircleShape, // 设置圆形形状
            // onSurface 是黑色
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f) // 设置颜色。copy()把这个颜色复杂过来,然后设置一个透明度
        ) {
            Image(
                painter = painterResource(id = R.drawable.beauty), // 设置图片加载位置
                contentDescription = null
            )
        }

        // TODO 垂直放置,从上到下
        Column (
            modifier = Modifier
                .padding(start = 8.dp) // 左边设置填充 8dp
                .align(Alignment.CenterVertically) // 让文字垂直居中
        ) {
            Text(
                text = "Alfred Sisley",
                fontWeight = FontWeight.Bold // 设置字体,bold: 粗体
            )
            //LocalContentAlpha provides ContentAlpha.medium == LocalContentAlpha.provides(ContentAlpha.medium)
            // provides 是一个被 infix 修饰的函数。可以省略链式调用的“." 以及一对圆括号"()"
            // 隐式传参
            CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                Text(
                    text = "3 minutes ago",
                    style = MaterialTheme.typography.body2 // 设置字体
                )
            }

        }
    }
}

2. 在 MainActivity.kt 里调用 PhotoGrapherCard

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackComposeLayoutsTheme {
                PhotographerCard()
            }
        }
    }
}

修饰符的顺序

由于每个函数都会对上一个函数返回的 Modifier 进行更改,因此顺序会影响最终结果。

Slots API

Material 组件大量使用槽位 API,这是 Compose 引入的一种模式,它在可组合项之上带来一层自定义设置。这种方法使组件变得更灵活,因为它们接受 可以自行配置的子元素,而不必公开子元素的每个配置参数。槽位会在界面中留出空白区域,让开发者按照自己的意愿来填充。

TopAppBar

scaffold

Scaffold 可以让我们实现具有基本 Material Design 布局结构的界面。Scaffold 可以为最常见的顶级 Material 组件(如 TopAppBar、BottomAppBar、FloatingActionButton 和 Drawer)提供槽位。通过使用 Scaffold,可轻松确保这些组件得到适当放置且正确地协同工作

示例:

创建 LayoutStudy.kt,完成布局

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

/**
 * @Author HL
 * @Date 2023/12/26 21:15
 * @Version 1.0
 */
@Composable
fun LayoutStudy(){
    Scaffold (
        // topBar 布局顶部
        topBar = {
            TopAppBar(
                // 设置标题
                title = {
                    Text(text = "LayoutStudy")
                },
                // 设置图标
                actions = {
                    IconButton(onClick = {}) {
                        Icon(imageVector = Icons.Filled.Favorite, contentDescription = null)
                    }
                }
            )
        }
    ) { innerPadding ->
        // 布局 body 部分
        BodyContent(Modifier.padding(innerPadding))
    }
}

@Composable
fun BodyContent(modifier : Modifier = Modifier){
    Column(modifier = Modifier.padding(8.dp)) {
        Text(text = "Hi there!")
        Text(text = "Thanks for going through the LayoutStudy")
    }
}

在 MainActivity.kt 里调用

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackComposeLayoutsTheme {
                //PhotographerCard()
                LayoutStudy()
            }
        }
    }
}

使用列表

如果我们知道用例不需要任何滚动,可以使用简单的 Column 或 Row。如果需要显示大量列表项(或长度未知的列表),可以使用 LazyColumnLazyRow

示例:设置一个可滚动的列表。可以通过 LayColumn 实现上下滚动的列表。

1. Lists.kt

@Composable
fun ScrollingList(){
    val listSize = 100
    val scrollState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()
    // 放一个包含2个按钮的顶部布局,再放一个 List,从上到下
    Column() {
        //TODO 放两个按钮,从左到右
        Row {
            // 第一个按钮
            Button(
                modifier = Modifier.weight(1f), // 设置权重
                onClick = {
                    //Kotlin 携程
                    coroutineScope.launch {
                        // 滚到最顶部
                        scrollState.animateScrollToItem(0)
                    }

                }
            ) {
                Text(text = "Scroll to the top")
            }
            // 第二个按钮
            Button(
                modifier = Modifier.weight(1f),
                onClick = {
                    coroutineScope.launch {
                        // 滚动最底部
                        scrollState.animateScrollToItem(listSize - 1)
                    }
                }
            ) {
                Text(text = "Scroll to the end")
            }
        }

        //TODO 放列表项
        LazyColumn(state = scrollState){
           items(listSize){
               // 列表项
               ImageListItem(index = it)
           }
        }
    }
}

@Composable
fun ImageListItem(index : Int){
    Row(
        verticalAlignment = Alignment.CenterVertically
    ) {
        Image(
            painter = painterResource(id = R.drawable.beauty),
            contentDescription = null,
            modifier = Modifier.size(40.dp)
        )
        Spacer(modifier = Modifier.width(10.dp))
        Text(text = "Item $index", style = MaterialTheme.typography.subtitle1)
    }
}

2. MainActivity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackComposeLayoutsTheme {
                
                ScrollingList()
            }
        }
    }
}

自定义布局

在 Compose 中,界面元素由可组合函数表示,此类函数在被调用后会发出一部分界面,这部分界面随后会被添加到呈现在屏幕上的界面树中。每个界面元素都有一个父元素,还可能有多个子元素。此外,每个元素在其父元素中都有一个位置,指定为(x, y)位置;也都有一个尺寸,指定为 width 和 height。

使用布局修饰符

你可以使用 layout 修饰符修改元素的测量和布局方式。Layout 是一个 lambda;它的参数包括你可以测量的元素(以 measurable 的形式传递)以及该组合项的传入约束条件(以 constraints 的形式传递)。

示例:我们有如下一个需求:有一个文本 “Hi threr!”,它有一个 firstbaseline(图中红框),正常情况下,firstbaseline 是到图中 1 处的距离。现在的需求是 firstbaseline 是和父元素的距离,也就是下图中 2 处的距离。

FirstBaselineToTop.kt
// firstBaselineToTop 为 Modifier 的扩展函数
fun Modifier.firstBaselineToTop(firstBaselineToTop : Dp) = this.then(// this.then() 的 放回值就是一个 Modifier

    // TODO 用 layout 修饰符来修改元素的测量和布局位置
    // measurable 测量元素;constraints 约束条件
    layout { measurable, constraints ->
        // 测量元素
        val placeable = measurable.measure(constraints)
        // 测量之后,获取元素的基线值,即 firsrBaseline 到元素顶部的值
        val firstBaseline = placeable[FirstBaseline]
        // 元素左上角新的 Y 坐标值 = 新基线值 - 旧基线值;firstBaselineToTop 方法传进来的值
        // roundToPx() 转为像素
        val Y = firstBaselineToTop.roundToPx() - firstBaseline

        // 设置元素的宽和高。元素的高是指,元素底部到父容器顶部的位置
        val height = placeable.height + Y
        layout(placeable.width, height){
            //设置元素的新位置
            placeable.placeRelative(0, Y)
        }
    }
)
@Composable
fun TextWithPaddingToBaseline(){
    JetpackComposeLayoutsTheme {

        // 这里我们要 firstBaselineToTop 设置值为 baseline 到 父容器顶部为 24dp,
        // TODO 这个值由我们自己设定
        Text(text = "Hi There!",
            Modifier
                    // 指定 firstBaselineToTop 的值
                .firstBaselineToTop(24.dp) // 这里需要返回一个 Modifier 才能继续下面的链式调用
                .background(Color.Red))
    }
}

firstBaselineToTop

右边的布局中, firstBaselineToTop 值是到父容器顶部的距离,是我们上面代码中设定的 24dp, 然后 measure 可以帮我们测出 Text 的基线高度(firstBaseline),那么 Text 左上角黑点的位置坐标为(0, firstBaselineToTop - firstBaseline)

MyOwnColumn

完成如下布局,模仿 Column 布局,给了4个文本,一列4行的方式排列。 

示例代码:

@Composable
fun MyOwnColumn(
    modifier: Modifier = Modifier,
    content : @Composable ()-> Unit){
    // 有多个元素,所以用 Layout()
    Layout(
        modifier = modifier,
        content = content){ measurables, constraints ->
        // 测量多个元素
        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }
        var positionY = 0
        // 元素布局的大小,设置为父容器的最大高度和宽度
        layout(constraints.maxWidth, constraints.maxHeight){
            placeables.forEach { placeable ->
                //placeable 就是我们测量的每一个子容器
                placeable.placeRelative(x = 0, y = positionY)
                // positionY 更新高度,第一个元素的顶部到父容器顶部的距离为0;
                // 第二个元素顶部到父容器顶部的距离为 第一个元素的高度
                // 第三个元素顶部到父容器顶部的距离为 第一个元素的高度 + 第二个元的的高度
                positionY += placeable.height
            }
        }
    }
}

@Composable
fun MyOwnColumnSample(){
    JetpackComposeLayoutsTheme() {
        MyOwnColumn {
            Text(text = "MyOwnColumn")
            Text(text = "places items")
            Text(text = "vertically.")
            Text(text = "We've done it by hand!")
        }
    }
}

StaggeredGrid

通过 StaggeredGrid 自定义如下布局

StaggeredGrid.kt

// 话题列表
val topics = listOf(
    "Arts & Crafts",
    "Beauty",
    "Books",
    "Business",
    "Comics",
    "Culinary",
    "Design",
    "Fashion",
    "Film",
    "History",
    "maths"
)

@Composable
fun StaggeredGrid(
    modifier: Modifier = Modifier,
    rows : Int = 3, //指定元素最终传进来,显示多少行。默认3行
    content : @Composable () -> Unit
){

    Layout(
        modifier = modifier,
        content = content
    ){ measurables, constraints ->
        // rowWidths 用于保存每行的宽度值
        val rowWidths = IntArray(rows){ 0 }
        // rowHeights 用于保存每行的高度值
        val rowHeights = IntArray(rows){ 0 }

        // 测量元素,带索引
        val placeables = measurables.mapIndexed{index, measurable ->
            // 得到每一个元素
            val placeable = measurable.measure(constraints)
            // 计算当前元素该去到数组的那个位置,row = 0, 1, 2
            var row = index % rows
            // 每一行的宽度由每一个元素的宽度累加
            rowWidths[row] += placeable.width
            // 每一行的高度由每一行中最高的元素代表
            rowHeights[row] = max(rowHeights[row], placeable.height)
            placeable
        }
        // 计算表格的宽度,即为 rowWidth 中最宽的那一行
        // 计算表格的高度,即为 rowHeight 中元素之和。也就是rows列之和
        val gridWidth = rowWidths.maxOrNull() ?: constraints.minWidth
        val gridHeight = rowHeights.sumOf { it } // rowHeights 里所有元素相加

        // 设置每一行的 Y 坐标,每一行中的每一个元素的 Y 坐标是一样的
        val rowY = IntArray(rows){ 0 }
        // 第一列元素的 Y 坐标都0,第i行(i > 0)元素的 Y 坐标为前面 i - 1 行的高度之和
        for(i in 1 until rows){
            rowY[i] = rowY[i - 1] + rowHeights[i - 1]
        }
        // TODO 设置表格的宽高
        layout(gridWidth, gridHeight){
            //TODO 布局每一个元素,那么就要计算每一个元素的(x, y)坐标
            // 上面我们已经计算了每个元素的 Y坐标,TODO 这里还需计算每个元素的 X 坐标
            val rowX = IntArray(rows){ 0 }
            // 遍历每一个元素
            placeables.forEachIndexed{ index, placeable ->
                val row = index % rows
                // 每一列的第一个元素的 X 值都是0,所以直接先放置,然后再修改 rowX[row]
                placeable.placeRelative(
                    x = rowX[row],
                    y = rowY[row]
                )

                // 修改 rowX[row]位置的值,为同一列中下一个元素计算 X 值,即前面元素的 宽度之和
                rowX[row] += placeable.width
            }
        }
    }

}

// TODO 每一个条目
@Composable
fun Chip(
    modifier: Modifier = Modifier,
    text : String
){
    // 一个卡片,圆角。里面包含一个 Row,第一列是 Box,第二列是 Text
    Card (
        modifier = modifier,
        border = BorderStroke(color = Color.Black, width = Dp.Hairline),// 设置边框线,Hairline默认1dp
        shape = RoundedCornerShape(8.dp) // 设置4个圆角
    ) {
        Row(
            modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 4.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Box(
                modifier = Modifier
                    .size(16.dp, 16.dp)
                    .background(color = MaterialTheme.colors.secondary)
            )
            Spacer(modifier = Modifier.width(8.dp))
            Text(text = text)
        }
    }
}

@Composable
fun StaggeredGridBodyContent(){
    Row(
        modifier = Modifier
            .background(color = Color.LightGray)
            .padding(16.dp)
            .horizontalScroll(rememberScrollState()), // 可以水平滚动
        content = {
            StaggeredGrid(modifier = Modifier) {
                // 放元素
                for(topic in topics){
                   Chip(modifier = Modifier.padding(8.dp), text = topic)
                }
            }
        }
    )
}

Activity.kt

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackComposeLayoutsTheme {
                //PhotographerCard()
                //LayoutStudy()
                //SampleList()
                //LazyList()
                //ScrollingList()
                //TextWithPaddingToBaseline()
                //MyOwnColumnSample()
                //StaggeredGrid()
                StaggeredGridBodyContent()
            }
        }
    }
}

约束布局

在实现对齐要求比较复杂的较大布局时,ConstraintLayout 很有用。 

引用

引用是使用 createRefs() 或 createRefFor() 创建的,ConstraintLayout 中的每个可组合项都需要有与之关联的引用。

约束条件

 约束条件是使用 constrainAs() 修饰符提供的,该修饰符将引用作为参数,可让你在主体 lambda 中指定其约束条件。

约束条件是使用 linkTo() 或其它有用的方法指定的。parent 是一个现有的引用,可用于指定对 ConstraintLayout 可组合项本身的约束条件。

案例1:用 ConstraintLayout 布局出如下效果。

1. 导入 ConstraintLayout 的依赖

implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha08'

2. ConstraintLayout.kt

@Composable
fun ConstraintLayoutContent(){
    // TODO 需要在 build.gradle 中引入依赖
    ConstraintLayout {
        //TODO 1. 通过 createRefs 创建引用,ConstraintLayout 中的每个元素都需要关联一个引用
        val (button, text) = createRefs()

        Button(
            onClick = { /*TODO*/ },
            //TODO 2.使用 Modifier.constrainAs 来提供约束,引用作为它的第一个参数
            // 在 lambda 表达式中指定约束规则
            modifier = Modifier.constrainAs(button){
                // TODO 3.约束条件是使用 linkTo(), parent.top表示在父容器顶部,有一个外边距 margin
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text(text = "Button")
        }
        
        Text(text = "Text", modifier = Modifier.constrainAs(text) {
            // 约束条件是使用 linkTo(), button.bottom表示位于button 的底部,有一个外边距 margin
            top.linkTo(button.bottom, margin = 16.dp)
            // 在 ConstraintLayout 中水平居中
            centerHorizontallyTo(parent)
        })
    }
}

 案例2:

@Composable
fun ConstraintLayoutContent2(){
    ConstraintLayout {
        val (button1, button2, text) = createRefs()

        Button(
            onClick = {},
            modifier = Modifier.constrainAs(button1){
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text(text = "Button1")
        }
        Text(text = "Text", modifier = Modifier.constrainAs(text) {
            // 约束条件是使用 linkTo(), button.bottom表示位于button 的底部,有一个外边距 margin
            top.linkTo(button1.bottom, margin = 16.dp)
            //TODO 在 text 的中间位置位于 boton1 的 end 位置
            centerAround(button1.end)
        })

        // 将 button1 和 text 组合起来,建立一个屏障
        val barrier = createEndBarrier(button1, text)
        Button(
            onClick = {  },
            modifier = Modifier.constrainAs(button2){
                //TODO top: 给顶部设置约束,start: 给左边设置约束
                top.linkTo(parent.top, margin = 16.dp)
                start.linkTo(barrier)
            }
        ) {
            Text(text = "Button1")
        }
    }
}

解耦 API

在某些情况下,最好将约束条件与应用它们的布局分离开来。例如,我们可能会希望根据屏幕配置来更改约束条件,或在两个约束条件集之间添加动画效果。

\bullet 将 ConstraintSet 作为参数传递给 ConstraintLayout。

\bullet 使用 layoutId 修饰符将在 ConstraintSet 中创建的引用分配给可组合项。

如下代码所示:我们系统代码中 margin 是根据屏幕旋转时,动态设置的值,这种就不好弄了。

我们可以通过解耦的方式,将 Button 和 Text 组件之间的约束关系在外部定义,然后传入到 ConstraintLayout 中。而 margin 作为一个其它约束,就作为参数传递。如下代码所示

@Composable
fun DecoupledConstraintLayout2(){
    // 打包
    BoxWithConstraints {
        // maxWidth 和 maxHeight 是 BoxWithConstraints 中的属性
        val constrains = if(maxWidth < maxHeight){
            //TODO 返回的是一个 ConstraintSet
            DecoupledConstraints(16.dp) // 竖屏情况下
        }else{
            DecoupledConstraints(64.dp)// 横屏情况下
        }
        ConstraintLayout (constrains) {
            Button(
                onClick = { },
                modifier = Modifier.layoutId("button")
            ) {
                Text(text = "Button")
            }

            Text(text = "Text", modifier = Modifier.layoutId("text"))
        }
    }
}

// TODO 将Button 和 Text 的约束抽离到外部,
private fun DecoupledConstraints(margin : Dp) : ConstraintSet{
    return ConstraintSet{
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button){
            top.linkTo(parent.top, margin)
        }
        constrain(text){
            top.linkTo(button.bottom, margin)
        }
    }
}

Intrinsics

\bullet Compose 只测量子元素一次,测量两次会引发运行时异常。但是,有时在测量子元素之前,我们需要一些有关子元素的信息。

\bullet Intrinsics 允许你在实际测量之前查询子项。1)(min|max)intrinsicWidth:鉴于此高度,你可以正确绘制内容的最小/最大宽度是多少。2)(min|max)intrinsicHeight:鉴于此宽度,你可以正确绘制内容的最小/最大高度是多少。

示例:如下布局。要在中间做一个分割线,它的高度与左右文本的高度一致

@Composable
fun TwoTexts(modifier: Modifier = Modifier){
    // 将 Row 的高度限制为文本的高度,通过 Intrinsic 在测量之前来获取文本的高度信息
    Row (modifier = Modifier.height(IntrinsicSize.Min)) {
        Text(
            text = "Hi",
            modifier = Modifier
                .padding(start = 4.dp)
                .weight(1f)
                .wrapContentWidth(Alignment.Start)
        )

        // 两个文本中间的线
        Divider(
            color = Color.Black,
            modifier = Modifier
                .fillMaxHeight() //fillMaxHeight()获取父容器最大高度
                .width(1.dp))

        Text(
            text = "There",
            modifier = Modifier
                .padding(start = 4.dp)
                .weight(1f)
                .wrapContentWidth(Alignment.End)
        )
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1444271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】数据库的基础——数据库的介绍、MySQL的介绍和架构、SQL分类、MySQL的基本使用、MySQL的存储引擎

文章目录 MySQL1. 数据库的介绍1.2 主流数据库 2. MySQL的介绍2.1 MySQL架构2.2 SQL分类2.3 MySQL的基本使用2.4 MySQL存储引擎 MySQL 1. 数据库的介绍 数据库&#xff08;Database&#xff0c;简称DB&#xff09;是按照数据结构来组织、存储和管理数据的仓库。它是长期存储在计…

安装了多个Java版本如何指定特定版本

Java版本问题的实战场景 机器安装了一个JDK 8的安装版本&#xff0c;默认的安装路径是 C:\Program Files\Java&#xff0c;JDK的安装版本同时安装了JDK 和JRE, 安装的路径分别是&#xff1a; JDK 路径&#xff1a; C:\Program Files\Java\jdk1.8.0_361JRE 路径&#xff1a; C…

Java图形化界面编程——菜单组件 笔记

2.7 菜单组件 ​ 前面讲解了如果构建GUI界面&#xff0c;其实就是把一些GUI的组件&#xff0c;按照一定的布局放入到容器中展示就可以了。在实际开发中&#xff0c;除了主界面&#xff0c;还有一类比较重要的内容就是菜单相关组件&#xff0c;可以通过菜单相关组件很方便的使用…

在 Windows上恢复删除照片的 4 种有效方法

您是否曾在 Windows 7/8/10/11 中不小心删除过照片&#xff1f;如何轻松快速地恢复已删除的照片&#xff1f;在这里这篇文章列出了几种在Windows 11/10/8/7中恢复已删除照片的可行方法&#xff0c;而MiniTool数据恢复软件 是丢失照片恢复的最佳选择。 意外删除的照片 根据一项…

【深度学习每日小知识】卷积神经网络(CNN)

在深度学习领域&#xff0c;卷积神经网络&#xff08;CNN&#xff09;彻底改变了视觉分析领域。凭借从图像中提取复杂模式和特征的能力&#xff0c;CNN 已成为图像分类、目标检测和面部识别等任务不可或缺的一部分。本文全面概述了 CNN&#xff0c;探讨了其架构、训练过程、应用…

《CSS 简易速速上手小册》第5章:CSS 动画与过渡(2024 最新版)

文章目录 5.1 CSS 过渡基础&#xff1a;网页的微妙舞步5.1.1 基础知识5.1.2 重点案例&#xff1a;按钮悬停效果5.1.3 拓展案例 1&#xff1a;渐变显示导航菜单5.1.4 拓展案例 2&#xff1a;动态调整元素大小 5.2 关键帧动画&#xff1a;编排你的网页芭蕾5.2.1 基础知识5.2.2 重…

宠物空气净化器哪个牌子好?养猫家庭如何挑选宠物空气净化器?

养猫的朋友都知道&#xff0c;猫咪掉毛是一个令人头痛的问题。猫毛和皮屑会漂浮在空气中&#xff0c;不仅遍布全屋的各个角落&#xff0c;而且清理起来也非常麻烦&#xff0c;特别是那些难以清除的猫毛。更糟糕的是&#xff0c;这些猫毛还可能引发人们的过敏反应&#xff0c;如…

Netty源码系列 之 HashedWheelTimer源码

Netty优化方案 之前总结NioEventLoop以及其他内容时&#xff0c;已经总结了Netty许多优化的设计方案。 1.Selector的优化 (1) 为epoll空转问题提供了解决思路&#xff0c;虽然并没有从根本上解决epoll空转问题&#xff0c;但是使用一个计数器的方式可以减少空转所带来的性能…

[word] word如何打印背景和图片? #微信#其他#经验分享

word如何打印背景和图片&#xff1f; 日常办公中会经常要打印文件的&#xff0c;其实在文档的打印中也是有很多技巧的&#xff0c;可以按照自己的需求设定&#xff0c;下面给大家分享word如何打印背景和图片&#xff0c;一起来看看吧&#xff01; 1、打印背景和图片 在默认的…

【数据结构与算法】【小白也能学的数据结构与算法】迭代算法专题

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

面向数据报编程-UDP协议

目录 前言&#xff1a; 1.UDP协议API 1.1UDP编程原理 1.2DatagramSocket类 &#xff08;1&#xff09;DatagramSocket构造方法 &#xff08;2&#xff09;DatagramSocket普通方法 1.3DatagramPacket类 &#xff08;1&#xff09;DatagramPacket构造方法 &#xff08;2…

电商小程序03登录页面开发

目录 1 创建应用2 创建页面3 首页功能搭建4 登录页搭建5 设置叠加效果总结 小程序开发在经过需求分析和数据源设计之后&#xff0c;就可以进入到页面开发的阶段了。首先我们需要开发登录的功能。 登录功能要求用户输入用户名和密码&#xff0c;勾选同意用户协议和隐私协议&…

SAP-PS-02-004利润中心移除分配公司代码

问题描述 在用KE51创建利润中心时&#xff0c;如果不采用“参考创建”的方式&#xff0c;SAP默认将所有公司代码分配给此利润中心。 上图中&#xff0c;L100003利润中心分配了1000、1001、1002公司&#xff0c;但是实际需求是&#xff1a;L100003只需要分配1000公司。 解决方法…

elasticsearch增删改查

一、数据类型 1、字符串类型 &#xff08;1&#xff09;text &#xff08;2&#xff09;keyword 2、数值类型 &#xff08;1&#xff09;long、integer、short、byte、float、double 3、日期类型 &#xff08;1&#xff09;date 4、布尔类型 &#xff08;1&#xff0…

ChatGPT高效提问—prompt常见用法(续篇九)

ChatGPT高效提问—prompt常见用法(续篇九) ​ 如何准确地向大型语言模型提出问题,使其更好地理解我们的意图,从而得到期望的答案呢?编写有效的prompt的技巧,精心设计的prompt,获得期望的的答案。 1.1 增加条件 ​ 在各种prompt技巧中,增加条件是最常用的。在prompt中…

基于JavaWeb的网上订餐项目

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88825723?spm1001.2014.3001.5503 Java项目-16 浏览商品&#xff0c;会员登录&#xff0c;添加购物车&#xff0c;进行配送等功能 文件代码功能介绍 1.Src下的java文件存放的我们后端的…

基于PHP网上图书销售商城系统qo85w

软件体系结构方案&#xff1a;由于本系统需要在不同设备上都能运行&#xff0c;而且电脑配置要求也要越低越好&#xff0c;为了实现这一要求&#xff0c;经过考虑B/S结构成为最佳之选。使用B/S结构的系统可以几乎在任何电脑上运行&#xff0c;只要浏览器可以正常工作就可以正常…

C++ //练习 5.12 修改统计元音字母的程序,使其也能统计空格、制表符和换行符的数量。

C Primer&#xff08;第5版&#xff09; 练习 5.12 练习 5.12 修改统计元音字母的程序&#xff0c;使其也能统计空格、制表符和换行符的数量。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /******************************…

【JavaScript 】finally() 方法和Filter() 方法

JavaScript 中的finally() 方法 finally是 JavaScript 构造中使用的方法try-catch。try它在and阻塞之后执行catch&#xff0c;无论 Promise 是已履行还是已拒绝。该函数的主要作用是执行必要的清理任务并向用户传达消息。一个常见的用例可能是通知用户“您的请求已被处理”&am…

使用client-only 解决组件不兼容SSR问题

目录 前言 一、解决方案 1.基于Nuxt 框架的SSR应用 2.基于vue2框架的应用 3.基于vue3框架的应用 二、总结 往期回顾 前言 最近在我的单页面SSR应用上开发JSON编辑器功能&#xff0c;在引入组件后直接客户端跳转OK&#xff0c;但是在直接加载服务端渲染的时候一直报这…