Android:使用tileMode的可重复背景如何缩放填充视图?
解决Android中TileMode背景自适应视图尺寸并完整填充的问题
这确实是Android BitmapDrawable的一个常见坑——当你开启tileMode后,gravity属性会被直接忽略,因为平铺渲染的逻辑和重力定位的逻辑本质上是冲突的。要实现既能自适应视图尺寸缩放,又能完整填充的可重复背景,我们可以从以下几种方案入手:
方案一:动态计算缩放比例,代码生成平铺Drawable
这种方法最直接,核心思路是先将原始Drawable缩放到与目标视图宽高匹配的尺寸,再开启平铺模式。需要注意的是,要确保视图已经完成测量后再执行计算,避免获取到0尺寸。
代码示例(Kotlin)
yourView.post { // 加载原始平铺素材 val originalTile = BitmapFactory.decodeResource(resources, R.drawable.your_tile_bitmap) val viewWidth = yourView.width val viewHeight = yourView.height // 计算缩放比例,将素材拉伸至视图的宽高(保证完全填充) val scaledTile = Bitmap.createScaledBitmap(originalTile, viewWidth, viewHeight, true) // 创建带平铺模式的Drawable val tileDrawable = BitmapDrawable(resources, scaledTile).apply { tileModeX = Shader.TileMode.REPEAT tileModeY = Shader.TileMode.REPEAT } yourView.background = tileDrawable }
如果需要保持素材的宽高比(避免拉伸变形),可以调整缩放逻辑,取宽高比例中的较大值,裁剪多余部分后再平铺:
val scaleRatio = max(viewWidth.toFloat() / originalTile.width, viewHeight.toFloat() / originalTile.height) val scaledWidth = (originalTile.width * scaleRatio).toInt() val scaledHeight = (originalTile.height * scaleRatio).toInt() val scaledTile = Bitmap.createScaledBitmap(originalTile, scaledWidth, scaledHeight, true)
方案二:自定义Drawable,自动适配视图边界
如果需要在多个视图中复用这个逻辑,自定义Drawable是更优雅的选择。它会在视图边界变化时自动重新计算缩放和平铺参数:
自定义Drawable代码
class ScalingTileDrawable(private val originalBitmap: Bitmap) : Drawable() { private lateinit var tilePaint: Paint override fun onBoundsChange(bounds: Rect) { super.onBoundsChange(bounds) // 每次边界变化时,重新生成适配当前尺寸的平铺Shader val scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, bounds.width(), bounds.height(), true) val tileShader = BitmapShader( scaledBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) tilePaint = Paint().apply { shader = tileShader isAntiAlias = true } } override fun draw(canvas: Canvas) { canvas.drawRect(bounds, tilePaint) } override fun setAlpha(alpha: Int) { tilePaint.alpha = alpha } override fun setColorFilter(colorFilter: ColorFilter?) { tilePaint.colorFilter = colorFilter } override fun getOpacity(): Int = PixelFormat.TRANSLUCENT }
使用方式
val originalTile = BitmapFactory.decodeResource(resources, R.drawable.your_tile_bitmap) yourView.background = ScalingTileDrawable(originalTile)
为什么默认TileMode会忽略Gravity?
简单来说,当你开启tileMode时,BitmapDrawable会切换到Shader平铺渲染模式——它会用素材的Shader重复填充整个视图区域,而gravity是用于将单个Drawable实例定位在视图中的属性,两种渲染逻辑互斥,所以系统会直接忽略gravity参数。
内容的提问来源于stack exchange,提问作者arvere
相关产品推荐
相关产品推荐

