This layout is made by me, the layout you are looking is a SVG image so I have just made the image to fill max size and added the above text and camera capture button below. But now I want to remove the image background and want to make the same layout programmatically.
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.fillMaxSize()) {
AndroidView({ previewView }, modifier = Modifier.fillMaxSize())
Column(modifier = Modifier.fillMaxSize()) {
Icon(
painter = painterResource(id = R.drawable.ic_card_overlay),
contentDescription = null
)
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.ic_black_transparent),
contentDescription = null,
contentScale = ContentScale.FillWidth
)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(26.dp)
) {
Row(
modifier = Modifier
.padding(bottom = 20.dp), verticalAlignment = Alignment.CenterVertically
) {
Icon(
modifier = Modifier.clickable {
onCloseCameraClick()
},
painter = painterResource(id = R.drawable.ic_baseline_arrow_back_ios_24),
contentDescription = null,
tint = Color.White
)
Text(
text = "Passport",
color = Color.White,
fontSize = 20.sp
)
}
Text(
text = "Place your passport inside the frame and take a\npicture.\nMake sure it is not cut or has any glare.",
color = Color.White,
fontSize = 12.sp
)
}
IconButton(
modifier = Modifier.padding(bottom = 20.dp),
onClick = {
Log.d("takePhoto", "ON CLICK")
takePhoto(
imageCapture = imageCapture,
outputDirectory = outputDirectory,
executor = executor,
onImageCaptured = onImageCaptured,
one rror = one rror
)
},
content = {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_camera_24),
contentDescription = stringResource(R.string.take_picture),
tint = Color.White,
modifier = Modifier
.fillMaxSize(0.2f)
)
}
)
}
You can see I have used ic_card_overlay image which act like a background. I want to achieve the same black transparent background with the box in the middle which will not include the black transparent color. Thank you.
CodePudding user response:
You can achieve this with using BlendMode.Clear
@Composable
fun TransparentClipLayout(
modifier: Modifier,
width: Dp,
height: Dp,
offsetY: Dp
) {
val offsetInPx: Float
val widthInPx: Float
val heightInPx: Float
with(LocalDensity.current) {
offsetInPx = offsetY.toPx()
widthInPx = width.toPx()
heightInPx = height.toPx()
}
Canvas(modifier = modifier) {
val canvasWidth = size.width
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawRect(Color(0x77000000))
// Source
drawRoundRect(
topLeft = Offset(
x = (canvasWidth - widthInPx) / 2,
y = offsetInPx
),
size = Size(widthInPx, heightInPx),
cornerRadius = CornerRadius(30f,30f),
color = Color.Transparent,
blendMode = BlendMode.Clear
)
restoreToCount(checkPoint)
}
}
}
You can customize corner radius size too. This is only for demonstration
Usage
Column {
Box(modifier = Modifier.fillMaxSize()) {
Image(
modifier =Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.landscape1),
contentDescription = null,
contentScale = ContentScale.Crop
)
TransparentClipLayout(
modifier = Modifier.fillMaxSize(),
width = 300.dp,
height = 200.dp,
offsetY = 150.dp
)
}
}
Result
CodePudding user response:
You can archieve this background layout using a custom Shape
in combination with a Surface
. With a custom implementation you can define what parts of the Surface
are displayed and which parts are "cut out".
The cutoutPath
defines the part which are highlighted. Here it is defined as a RoundRect
with a dynamically calculated position and size. Adjust the topLeft
and ``formulas as you need.
Using Path.combine(...)
the outlinePath
is combined with the cutoutPath
. This is where the magic happens.
/**
* This is a shape with cuts out a rectangle in the center
*/
class CutOutShape : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val outlinePath = Path()
outlinePath.addRect(Rect(Offset(0f, 0f), size))
val cutoutHeight = size.height * 0.3f
val cutoutWidth = size.width * 0.75f
val center = Offset(size.width / 2f, size.height / 2f)
val cutoutPath = Path()
cutoutPath.addRoundRect(
RoundRect(
Rect(
topLeft = center - Offset(
cutoutWidth / 2f,
cutoutHeight / 2f
),
bottomRight = center Offset(
cutoutWidth / 2f,
cutoutHeight / 2f
)
),
cornerRadius = CornerRadius(16f, 16f)
)
)
val finalPath = Path.combine(
PathOperation.Difference,
outlinePath,
cutoutPath
)
return Outline.Generic(finalPath)
}
}
The shape can be used like this:
Surface(
shape = CutOutShape(),
color = Color.Black.copy(alpha = 0.45f)
) { }
This results in the following screen:
Box {
AndroidView({ previewView }, modifier = Modifier.fillMaxSize())
Surface(
shape = CutOutShape(),
color = Color.Black.copy(alpha = 0.45f),
modifier = Modifier.fillMaxSize()
) { }
Column(
modifier = Modifier
.padding(top = 54.dp, start = 32.dp, end = 32.dp, bottom = 54.dp)
) {
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.TwoTone.ArrowBack,
contentDescription = null,
tint = Color.White
)
}
Text(
"Passport",
color = Color.White,
fontSize = 20.sp
)
}
Text(
"Place your passport inside the frame and take a picture.\nMake sure it is not cut or has any glare.",
color = Color.White,
fontSize = 12.sp
)
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = Icons.TwoTone.Camera,
contentDescription = null,
tint = Color.White,
modifier = Modifier
.size(48.dp)
.align(Alignment.CenterHorizontally)
)
}
}