Android:如何实现票据样式三角形布局?是否有更优方案?
Great question! Adding 20+ AppCompatImageViews just to create a row of triangles is far from optimal—it bloats your layout XML, adds unnecessary view hierarchy overhead, and makes future adjustments (like changing triangle count/size) a hassle. Let's break down several cleaner, more performant alternatives:
1. Custom Drawable (Top Recommendation)
Create a reusable Drawable that draws the entire strip of triangles in one pass. This lets you use a single ImageView or even set it directly as a layout background.
Kotlin Implementation:
class TriangleStripDrawable( private val triangleColor: Int, private val triangleSize: Int // Size of each triangle's base/height ) : Drawable() { override fun draw(canvas: Canvas) { val paint = Paint().apply { color = triangleColor style = Paint.Style.FILL isAntiAlias = true } val totalWidth = bounds.width() val stripHeight = bounds.height() val triangleCount = totalWidth / triangleSize // Draw each triangle in the strip for (i in 0 until triangleCount) { val leftOffset = i * triangleSize val path = Path().apply { // Adjust path coordinates to match your desired triangle orientation moveTo(leftOffset.toFloat(), stripHeight.toFloat()) lineTo((leftOffset + triangleSize).toFloat(), stripHeight.toFloat()) lineTo((leftOffset + triangleSize / 2).toFloat(), 0f) close() } canvas.drawPath(path, paint) } } override fun setAlpha(alpha: Int) {} override fun setColorFilter(colorFilter: ColorFilter?) {} override fun getOpacity(): Int = PixelFormat.OPAQUE }
Usage in Code:
// Initialize the drawable with your desired color and size val triangleStrip = TriangleStripDrawable( Color.parseColor("#c6802a"), resources.getDimensionPixelSize(R.dimen.dimen_20dp) ) // Set as background for a LinearLayout or ImageView binding.triangleStripContainer.background = triangleStrip
2. Vector Drawable + Tiled Repeat
Create a single triangle vector asset, then convert it to a tiled drawable that repeats horizontally. This avoids writing custom code if you prefer XML-first approaches.
Step 1: Create vector_triangle.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20"> <!-- Path for upward-pointing triangle (adjust pathData for orientation) --> <path android:fillColor="#c6802a" android:pathData="M0,20 L20,20 L10,0 Z" /> </vector>
Step 2: Apply Tiled Repeat in Code
val triangleVector = ContextCompat.getDrawable(this, R.drawable.vector_triangle) ?: return // Convert vector to bitmap for tiling val bitmap = Bitmap.createBitmap( triangleVector.intrinsicWidth, triangleVector.intrinsicHeight, Bitmap.Config.ARGB_8888 ) val canvas = Canvas(bitmap) triangleVector.setBounds(0, 0, canvas.width, canvas.height) triangleVector.draw(canvas) // Create tiled drawable val tiledDrawable = BitmapDrawable(resources, bitmap).apply { tileModeX = Shader.TileMode.REPEAT // Repeat horizontally } // Set as layout background binding.triangleStripContainer.background = tiledDrawable
3. Custom View
If you want a self-contained UI component, create a custom View that draws the triangle strip directly. This works great if you need to add interactivity later.
Kotlin Implementation:
class TriangleStripView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val triangleColor = Color.parseColor("#c6802a") private val triangleSize = resources.getDimensionPixelSize(R.dimen.dimen_20dp) private val paint = Paint().apply { color = triangleColor style = Paint.Style.FILL isAntiAlias = true } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val totalWidth = measuredWidth val stripHeight = measuredHeight val triangleCount = totalWidth / triangleSize for (i in 0 until triangleCount) { val leftOffset = i * triangleSize val path = Path().apply { moveTo(leftOffset.toFloat(), stripHeight.toFloat()) lineTo((leftOffset + triangleSize).toFloat(), stripHeight.toFloat()) lineTo((leftOffset + triangleSize / 2).toFloat(), 0f) close() } canvas.drawPath(path, paint) } } }
Usage in XML:
<com.yourpackage.TriangleStripView android:layout_width="match_parent" android:layout_height="20dp" />
Why These Are Better
All these approaches cut your view count from 20+ down to 1, which improves layout performance (especially on older devices) and makes your code easier to maintain. You can quickly adjust triangle size, color, or orientation without editing dozens of XML entries.
内容的提问来源于stack exchange,提问作者Ankush Kapoor

