I'm new to Jetpack Compose and trying to experiment with theming.
I have a theme.kt file that looks like this:
#Composable
fun AppThemeProvider(
content: #Composable () -> Unit
) = CompositionLocalProvider(
LocalColors provides Colors(),
LocalShapes provides Shapes(),
LocalTypography provides Typography(),
content = content
)
object AppTheme {
val colors: Colors #Composable get() = LocalColors.current
val shapes: Shapes #Composable get() = LocalShapes.current
val typography: Typography #Composable get() = LocalTypography.current
}
And 3 classes for each provided values: Colors.kt, Shapes.kt and Typography.kt
This is my Colors class (The other 2 are basically the same):
data class Colors(
val primary: Color = Color(0xFF00663f),
val primaryVariant: Color = Color(0xFF007265),
val onPrimary: Color = Color(0xFFFFFFFF),
val secondary: Color = Color(0xFFFFFFFF),
val secondaryVariant: Color = Color(0xFFFFFFFF),
val onSecondary: Color = Color(0xFFFFFFFF),
val background: Color = Color(0xFFFFFFFF),
val surface: Color = Color(0xFFFFFFFF),
val error: Color = Color(0xFFBF0013),
val onBackground: Color = Color(0xFF3E3E3E),
val onError: Color = Color(0xFFFFFFFF),
)
val LocalColors = staticCompositionLocalOf { Colors() }
val colors: Colors
#Composable
#ReadOnlyComposable
get() = LocalColors.current
Now, I have an API that I call during the splash screen that returns my theme as a JSON structure.
I'd like to use such response to build my 3 values classes to have a 'built-at-runtime' theme.
Is it possible to achieve this behaviour?
Yes it is possible. You can pass the Colors as a parameter to your AppThemeProvider composable and when the colors changes then the theme of your up will be updated.
Simple example:
var colors by remember { mutableStateOf(Colors()) }
LaunchedEffect(Unit) {
colors = downloadColorsFromAPI()
}
AppThemeProvider(colors = colors) {
App()
}
and your AppThemeProvider becomes:
#Composable
fun AppThemeProvider(
colors: Colors = Colors(),
content: #Composable () -> Unit
) = CompositionLocalProvider(
LocalColors provides colors,
LocalShapes provides Shapes(),
LocalTypography provides Typography(),
content = content
)
Related
I am currently working on an Android app and I am trying to create an infinite animated progress bar using Jetpack Compose. I have been looking through the documentation but I am having trouble finding a straightforward example of how to create this. Can anyone provide a simple example or point me in the right direction to get started?"
#Composable
fun LoadingProgressBar(isLoading: Boolean) {
val animation = remember {
repeatable(periodMillis = 1000) {
animation(0f, 1f) { progress ->
progress
}
}
}
if (isLoading) animation.start() else animation.stop()
Box(modifier = Modifier.preferredSize(100.dp, 5.dp).background(color = Color.Gray)) {
Box(modifier = Modifier.preferredWidth(animation[animation] * 100.dp)
.preferredHeight(5.dp)
.background(color = Color.Blue))
}
}
CircularProgressIndicator and LinearProgressIndicator are the progressbars that can be determinate or indeterminate based on which overload you pick.
#Composable
fun CircularProgressIndicator(
/*#FloatRange(from = 0.0, to = 1.0)*/
progress: Float,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.primary,
strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
)
or
#Composable
fun CircularProgressIndicator(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.primary,
strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
)
same goes for linear one either
Is there a way to convert jetpack Compose's androidx.compose.ui.graphics.Color to android.graphics.drawable.ColorDrawable ?
I tried ColorDrawable(Color.Red.toArgb()) but its not working!
You tried it in the correct way.
Here is a sample that does work
#Composable
fun MyComposable() {
val color = androidx.compose.ui.graphics.Color.Red
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(20.dp),
factory = { context ->
View(context).apply {
background = ColorDrawable(color.toArgb())
}
}
)
}
I'm using Glide in an Android, Kotlin, Jetpack project. When the photos are loading, the DEFAULT_MOVIE_IMAGE appears. However if one of the movies that is returned has a null value for an image URL, no image appears and the card shrinks down to the size of only the title. I'm trying to set it so the DEFAULT_MOVIE_IMAGE will appear if there is a null value for the movie poster url.
Setting a placeholder or an error image does not appear to be working.
Image part of the MovieCard composable
movie.posterPath?.let { url ->
val image = loadPicture(url = url, defaultImage = DEFAULT_MOVIE_IMAGE).value
image?.let { img ->
Image(
bitmap = img.asImageBitmap(),
contentDescription = "Movie Projector",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}
ImageUtils.kt
const val DEFAULT_MOVIE_IMAGE = R.drawable.movie_placeholder
const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w500"
#Composable
fun loadPicture(
url: String,
#DrawableRes defaultImage: Int
): MutableState<Bitmap?> {
val bitmapState: MutableState<Bitmap?> = remember { mutableStateOf(null) }
Glide.with(LocalContext.current)
.asBitmap()
.load(defaultImage)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
bitmapState.value = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
Glide.with(LocalContext.current)
.asBitmap()
.load("https://image.tmdb.org/t/p/w500$url")
.error(R.drawable.space_dog_laika1)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
bitmapState.value = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
return bitmapState
}
I ended up wrapping the image piece of the movie card in an if/else statement. I'm not sure if this is best practice or the most terse solution, but it worked.
if (movie.posterPath != null) {
movie.posterPath?.let { url ->
val image = loadPicture(url = url, defaultImage = DEFAULT_MOVIE_IMAGE).value
image?.let { img ->
Image(
bitmap = img.asImageBitmap(),
contentDescription = "Movie Poster",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}
}
} else {
val image: Painter = painterResource(id = DEFAULT_MOVIE_IMAGE)
Image(
painter = image,
contentDescription = "Film Projector",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}
I am a beginner developer. I'm sorry for my english.
I am trying to make a barcode reader application. I use MLKit and CameraX.
I want to analyze only the part of the preview that is in the rectangle. Now the preview is being analyzed in full. I only want to analize what is in the rectangle. I tried to use the ViewPort, but it seems I didn't quite understand what it was for, because it could not solve the problem. I looked for solutions on the Internet, but my problem remained relevant. I think that before analysis it is necessary to crop the image and only then analyze it, but is this true?
My layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:background="#android:color/white"
tools:context=".ui.BarcodeScanningFragment">
<androidx.camera.view.PreviewView
android:id="#+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.t_ovchinnikova.android.scandroid_2.ui.ViewFinderOverlay
android:id="#+id/overlay"
android:layerType="software"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Camera class:
class CameraSource (private val overlay: ViewFinderOverlay) {
private lateinit var cameraExecutor: ExecutorService
val preview : Preview = Preview.Builder()
.build()
fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(overlay.context)
cameraProviderFuture.addListener(Runnable {
//Используется для привязки жизненного цикла камер к владельцу жизненного цикла
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
bindCameraUseCases(cameraProvider)
}, ContextCompat.getMainExecutor(overlay.context))
}
#SuppressLint("UnsafeOptInUsageError")
private fun bindCameraUseCases(cameraProvider: ProcessCameraProvider) {
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(overlay.width, overlay.height))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
val orientationEventListener = object : OrientationEventListener(overlay.context) {
override fun onOrientationChanged(orientation : Int) {
// Monitors orientation values to determine the target rotation value
val rotation : Int = when (orientation) {
in 45..134 -> Surface.ROTATION_270
in 135..224 -> Surface.ROTATION_180
in 225..314 -> Surface.ROTATION_90
else -> Surface.ROTATION_0
}
imageAnalysis.targetRotation = rotation
}
}
orientationEventListener.enable()
cameraExecutor = Executors.newSingleThreadExecutor()
val analyzer = BarcodeAnalyzer()
imageAnalysis.setAnalyzer(cameraExecutor, analyzer)
var cameraSelector : CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val useCaseGroup = UseCaseGroup.Builder()
.addUseCase(preview)
.addUseCase(imageAnalysis)
.build()
cameraProvider.bindToLifecycle(overlay.context as LifecycleOwner, cameraSelector, useCaseGroup)
}
}
class ViewFinderOverlay:
class ViewFinderOverlay(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val boxPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_stroke)
style = Paint.Style.STROKE
strokeWidth = context.resources.getDimensionPixelOffset(R.dimen.barcode_stroke_width).toFloat()
}
private val scrimPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_background)
}
private val eraserPaint: Paint = Paint().apply {
strokeWidth = boxPaint.strokeWidth
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private val boxCornerRadius: Float =
context.resources.getDimensionPixelOffset(R.dimen.barcode_reticle_corner_radius).toFloat()
var boxRect: RectF? = null
fun setViewFinder() {
val overlayWidth = width.toFloat()
val overlayHeight = height.toFloat()
val boxWidth = overlayWidth * 80 /100
val boxHeight = overlayHeight * 36 / 100
val cx = overlayWidth / 2
val cy = overlayHeight / 2
boxRect = RectF(cx - boxWidth / 2, cy - boxHeight / 2, cx + boxWidth / 2, cy + boxHeight / 2)
invalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
boxRect?.let {
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), scrimPaint)
eraserPaint.style = Paint.Style.FILL
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
eraserPaint.style = Paint.Style.STROKE
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
// Draws the box.
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, boxPaint)
}
}
}
ViewFinderOverlay - a view that is superimposed on the camera preview.
Screen:
[1]: https://i.stack.imgur.com/cYvFP.jpg
Image analyzer:
#SuppressLint("UnsafeOptInUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val scanner = BarcodeScanning.getClient()
scanner.process(image)
.addOnSuccessListener { barcodes ->
barcodes?.firstOrNull().let { barcode ->
val rawValue = barcode?.rawValue
rawValue?.let {
}
}
}
imageProxy.close()
}
}
Help me please. I would be glad to any advice.
You can take a look at LifecycleCameraController. It takes care of creating use cases as well as configuring ViewPort.
However AFAIK, MLKit doesn't respect the crop rect. What you can do is checking the MLKit result, and discard any barcode whose coordinates are outside of the crop rect.
I'm trying to create a dropdown box/combo box for that contains multiple colors such as Color.RED, Color.BLUE, etc.. But when I run the program, it prints the colors in their hex form: 0xff0000. Actually, it prints them in some hex-like form that contains 8 numbers instead of 6: 0xff0000ff. I know it has something to do with the ObervableList. That's where I add the colors, and that's what the combo box gets the colors from. But how do I get it to just print "Red" or "Blue"?
Text diffuseColorText = new Text("Diffuse Color");
diffuseColorText.setFont(Font.font("Arial", FontWeight.BOLD, 10));
ObservableList<Color> colorList = FXCollections.observableArrayList(Color.RED, Color.BLUE, Color.YELLOW,
Color.GREEN, Color.PURPLE, Color.ORANGE);
ComboBox<Color> dColors = new ComboBox<>(colorList);
dColors.setPromptText("Color of Box");
dColors.setPrefSize(150, 30);
dColors.setOnAction(event -> {
diffuseColor = ((ComboBox<Color>) event.getSource()).getValue();
mymaterial.setDiffuseColor(diffuseColor);
});
Color.toString prints the channels (including alpha) and there's nothing you can do about it.
You could use a Map<String, Color> though to map from strings to color:
Map<String, Color> colors = new LinkedHashMap<>();
colors.put("RED", Color.RED);
...
colors.put("ORANGE", Color.ORANGE);
ObservableList<String> colorList = FXCollections.observableArrayList(colors.keySet());
ComboBox<String> dColors = new ComboBox<>(colorList);
dColors.setPromptText("Color of Box");
dColors.setPrefSize(150, 30);
dColors.setOnAction(event -> {
diffuseColor = colors.get(((ComboBox<String>) event.getSource()).getValue());
mymaterial.setDiffuseColor(diffuseColor);
});
If reflection is available and you want to list all colors available via fields in the Color class, you could also use it to the Colors:
private static final int PUBLIC_STATIC = Modifier.PUBLIC | Modifier.STATIC;
...
Map<String, Color> colors = new LinkedHashMap<>();
for (Field field : Color.class.getDeclaredFields()) {
if (field.getType() == Color.class && (field.getModifiers() & PUBLIC_STATIC) == PUBLIC_STATIC ) {
colors.put(field.getName(), (Color) field.get(null));
}
}