《Jetpack Compose系列学习》-9 Compose的简单控件之Image、ProgressBar

ImageView

我们知道Android View中ImageView用来显示图片,而Compose中我们使用Image展示图片,来看看简单用法:

// Image用法
Image(
    painter = painterResource(id = R.drawable.ic_launcher_background),
    contentDescription = "这是一张图片"
)
复制代码

上面使用了painterResource方法传入了图片Id资源,并添加了文字描述,非常简单,看下效果:

image.png

接下来看看Image的源码,了解它的更多使用方法:

@Composable
fun Image( // 构造方法1
    bitmap: ImageBitmap,
    contentDescription: String?, // 图片描述文本
    modifier: Modifier = Modifier, // 修饰符
    alignment: Alignment = Alignment.Center, // 对齐参数
    contentScale: ContentScale = ContentScale.Fit, // 缩放比例
    alpha: Float = DefaultAlpha, // 透明度
    colorFilter: ColorFilter? = null // 颜色过滤器
) {
    // 省略...
}

@Composable
fun Image( // 构造方法2
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
) {
    // 省略...
}
复制代码

这俩重载方法,除了第一个参数不同外,其他的参数都一样,我们重点看看第一个参数的类型。

ImageBitmap

bitmap参数类型为ImageBitmap,不是Bitmap,是Compose中特有的类,可以通过Bitmap扩展方法asImageBitmap将Bitmap转成Image中需要的Imagebitmap:

val bitmap = BitmapFactory.decodeFile("图片路径")
Image(bitmap = bitmap.asImageBitmap(), contentDescription = "这是一张图片")
复制代码
Painter

painter参数类型是Painter,是一个抽象类,有三个子类,最常用的是BitmapPainter。

image.png

由于直接使用BitmapPainter有些复杂,所以Compose为我们封装好了方法来生成Painter,上面的例子中调用的painterResource就是官方推荐的方法,我们只需要传入资源Id即可。

设置图片样式

Image控件还有好几个参数没有使用,我们来看看各自有什么效果。

  1. alignment

alignment是可选对齐参数,类型为Alignment,用于将Image放置在由宽度和高度自定义的范围内,用法:

Image(
    painter = painterResource(id = R.drawable.ic_launcher_background),
    contentDescription = "这是一张图片",
    alignment = Alignment.Center
)
复制代码

Alignment用场用于定义父布局中子布局的对齐方式,后面我们会一起学习这块。

  1. contentScale

用来设置横竖缩放比例,类型为ContentScale。如果布局边界大小和Image固定大小不同,可以选择scale参数来确定要使用的缩放比例。这里默认缩放方式为ContentScale.Fit,保持原图片的宽高比。当然它还有其它几种类型:

@Stable
interface ContentScale {

    /**
     * 计算比例因子以相互独立地应用于水平轴和垂直轴,以使源内容与给定的目标适配
     */
    fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor

    /**
     * 伴生对象
     */
    companion object {

        /**
         * 保持图片的宽高比,以使图片的宽度和高度都等于或大于目标的相应尺寸。
         */
        @Stable
        val Crop = object : ContentScale {
            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
                computeFillMaxDimension(srcSize, dstSize).let {
                    ScaleFactor(it, it)
                }
        }

        /**
         * 保持图片的宽高比,以使图片的宽度和高度都等于或小于目标的相应尺寸。
         */
        @Stable
        val Fit = object : ContentScale {
            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
                computeFillMinDimension(srcSize, dstSize).let {
                    ScaleFactor(it, it)
                }
        }

        /**
         * 缩放图片,并保持宽高比,以使边界与目标高度匹配,如果高度大于宽度,则
         * 可以覆盖比目标更大的区域
         */
        @Stable
        val FillHeight = object : ContentScale {
            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
                computeFillHeight(srcSize, dstSize).let {
                    ScaleFactor(it, it)
                }
        }

        /**
         * 缩放图片,并保持宽高比,以使边界与目标宽度匹配,如果宽度大于高度,则
         * 可以覆盖比目标更大的区域
         */
        @Stable
        val FillWidth = object : ContentScale {
            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
                computeFillWidth(srcSize, dstSize).let {
                    ScaleFactor(it, it)
                }
        }

        /**
         * 如果图片大于目标,则缩放图片将宽高比保持在目标范围内
         * 如果源内容在宽高维度上都小于或者等于目标,则其行为类似于“无”。
         * 这将始终包含在目标范围内。
         */
        @Stable
        val Inside = object : ContentScale {

            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor {
                return if (srcSize.width <= dstSize.width &&
                    srcSize.height <= dstSize.height
                ) {
                    ScaleFactor(1.0f, 1.0f)
                } else {
                    computeFillMinDimension(srcSize, dstSize).let {
                        ScaleFactor(it, it)
                    }
                }
            }
        }

        /**
         * 不对图片进行任何缩放
         */
        @Stable
        val None = FixedScale(1.0f)

        /**
         * 横向和纵向不均匀缩放以填充目标范围
         */
        @Stable
        val FillBounds = object : ContentScale {
            override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
                ScaleFactor(
                    computeFillWidth(srcSize, dstSize),
                    computeFillHeight(srcSize, dstSize)
                )
        }
    }
}
复制代码

以上几种缩放方式可以根据具体项目需求来使用。

alpha

图片透明度,类型为Float,Image中alpha默认值是DefaultAlpha。

const val DefaultAlpha: Float = 1.0f
复制代码

常量值为1.0f,图片完全不透明。如果设置成完全透明,则将alpha值设置成0f即可:

Box {
    Text(text = "这是一个文字")
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        modifier = Modifier.size(200.dp, 200.dp),
        contentDescription = "这是一张图片",
        alignment = Alignment.Center,
        alpha = 0.1f
    )
}
复制代码

image.png

这里容器我们用了Box,相当于Android View中的帧布局Framelayout,一个文本Text,上面放置一张Image,将图片的透明度设置成0.1f后,图片下面的文字就能清晰的看到了。

colorFilter

colorFilter可以对Image进行着色,也可以为Image设置颜色矩阵,还可以为Image创建简单的照明效果,来看看源码:

@Immutable
class ColorFilter internal constructor(internal val nativeColorFilter: NativeColorFilter) {
    companion object {
        /**
         * 创建一个颜色滤镜,该滤镜作为第二个参数给出的混合模式
         * 源颜色是第一个参数指定的颜色,目标颜色是来自要合成的图层颜色
         */
        @Stable
        fun tint(color: Color, blendMode: BlendMode = BlendMode.SrcIn): ColorFilter =
            actualTintColorFilter(color, blendMode)

        /**
         * 创建一个ColorFilter以通过4*5颜色矩阵转换颜色
         * 该滤镜可用于更改像素的饱和度,从YUV转换为RGB等
         */
        @Stable
        fun colorMatrix(colorMatrix: ColorMatrix): ColorFilter =
            actualColorMatrixColorFilter(colorMatrix)

        /**
         * 创建可用于模拟简单照明效果的ColorFilter
         * ColorFilter由两个参数定义,一个参数用于乘以源颜色,另一个参数用于添加至源颜色
         */
        @Stable
        fun lighting(multiply: Color, add: Color): ColorFilter =
            actualLightingColorFilter(multiply, add)
    }
}
复制代码

我们来简单设置下ColorFilter:

Box {
    Text(text = "这是一个文字")
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        modifier = Modifier.size(200.dp, 200.dp),
        contentDescription = "这是一张图片",
        alignment = Alignment.Center,
        alpha = 1.0f,
        colorFilter = ColorFilter.tint(Color.Red)
    )
}
复制代码

image.png

显示网络图

Android中显示一张网络图片,最开始都是自己写网路请求,请求下载后的Bitmap再通过ImageView去显示,当然现在有现成的图片库Glide、Fresco等;在Compose中显示网络图也不难,只需要添加一个依赖库:

implementation 'com.google.accompanist:accompanist-coil:0.10.0'
复制代码

coil是一个很新的图片加载库,用kotlin开发的,使用了kotlin的协程,网络请求默认为Okhttp,特点:足够快,图片存储、内存、下载等都做了大幅度优化,支持Compose,里面实现了网络图片的可组合项。具体使用如下:

Box {
    Image(
        painter = rememberCoilPainter(request = "https://picsum.photos/300/300"),
        contentDescription = null
    )
}
复制代码

image.png

我们看看rememberCoilPainter的构造参数:

@Composable
fun rememberCoilPainter(
    request: Any?, // 图片地址
    imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(), // 请求图片时使用的ImageLoader,默认为CoilPainterDefaults.defaultImageLoader()
    shouldRefetchOnSizeChange: ShouldRefetchOnSizeChange = ShouldRefetchOnSizeChange { _, _ -> false }, // 大小更改时回调,允许可选地重新获取图片,返回true以重新获取图片
    requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null, // ImageRequest的可选生成器
    fadeIn: Boolean = false, // 成功加载图片后是否进行淡入动画
    fadeInDurationMs: Int = LoadPainterDefaults.FadeInTransitionDuration, // 淡入动画时长(单位毫秒)
    @DrawableRes previewPlaceholder: Int = 0, // 占位图
)
复制代码

这个图片加载库的其他用法可以参考其官网:google.github.io/accompanist…

progressBar

进度条应用场景也很多,比如页面加载时的loading,下载文件时的进度、播放视频时的进度条等。

圆形进度条

先看看Compose中怎样使用圆形进度条:

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    CircularProgressIndicator() // 圆形进度条
}
复制代码

image.png

Row是横向布局,CircularProgressIndicator就是圆形进度条,一行代码就搞定。这种实时刷新的控件,通过Preview方式进行预览的话,会很卡,所以建议运行在真机或模拟器查看。来看看CircularProgressIndicator源码:

@Composable
fun CircularProgressIndicator(
    modifier: Modifier = Modifier, // 修饰符
    color: Color = MaterialTheme.colors.primary, // 进度条颜色
    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth // 进度条宽度
) {
    // 省略...
}
复制代码

我们来看看属性的用法:

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    CircularProgressIndicator( // 圆形进度条
        modifier = Modifier.size(80.dp),
        color = Color.Red,
        strokeWidth = 10.dp
    )
}
复制代码

image.png

还有另外一个可以设置进度值的构造方法:

@Composable
fun CircularProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
)
复制代码

progress参数设置0.0没有进度,1.0表示已完成进度。这里需要注意,设置进度值的进度条是没有转圈动画效果的。

条形进度条

条形进度条和圆形进度条的使用上基本一致,来看看:

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    LinearProgressIndicator() // 条形进度条
}
复制代码

image.png

来看看它的源码:

@Composable
fun LinearProgressIndicator(
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    backgroundColor: Color = color.copy(alpha = IndicatorBackgroundOpacity)
)
复制代码

除了修饰符外,还可以设置背景色:

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    LinearProgressIndicator(
        color = Color.Red, // 进度条颜色
        backgroundColor = Color.Yellow // 背景色
    )
}
复制代码

image.png

同样,也可以设置进度progress

@Composable
fun LinearProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    backgroundColor: Color = color.copy(alpha = IndicatorBackgroundOpacity)
) 
复制代码

设置进度后,进度条也变成静态的、没有动画效果。后面我们会学习Compose里的布局组件。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享