I have a json animation that I'm using with LottieAnimation, but my feature needs that I change a text and an image inside the animation in runtime.
The animation is a ranking like a podium. Then animated winner text and image are loaded from the backend. So I need to set these values.
Right now I know that TextDelegate can handle the Text problem, but it only works with View, as the old way to do it. It needs the View in constructor. I don't find a way to do this with Compose. And about the image, I have no glues.
Can someone help me?
Depending on your json file structure, you have two ways of settings an image
Place used images inside src/main/assets/lottieImages. Names should be the same as mentioned in your json file, in my case it's img_0.png.
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(R.raw.we_accept),
imageAssetsFolder = "lottieImages"
)
Set a bitmap to key path, like I'm taking it from the resources, using dynamic properties
val bitmap = remember {
BitmapFactory.decodeResource(context.resources, R.drawable.my_image)
}
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(LottieProperty.IMAGE, bitmap, "weaccept.jpg"),
)
LottieAnimation(
composition,
dynamicProperties = dynamicProperties
)
I'm using this json as a sample animation.
As for the text, Lottie TextDelegate does not seem to be ported to compose, I suggest you create a feature request on github.
Related
Is it possible to do something like
var myThemeVariable = "redTheme" //could be either redTheme, greenTheme, or blueTheme for example
context.setTheme(R.style.myThemeVariable) //sets theme to R.style.redTheme
instead of what i usually do
var myThemeVariable = "redTheme" //could be either redTheme, greenTheme, or blueTheme
when (myThemeVariable)
{
"redTheme" -> context.setTheme(R.style.redTheme)
"greenTheme" -> context.setTheme(R.style.greenTheme)
"blueTheme" -> context.setTheme(R.style.blueTheme)
}
In this example it's not super cluttered, but in my actual code there's a lot more. My current solution is not only hard to understand, add to, or remove from; it's also (I imagine) unnecessarily computationally expensive. Is something akin to the first approach possible? If it's not, does any language have something like it? Thanks!
Thanks y'all, in the end I searched about #blackapps' and #Haseeb Hassan Asif's recommendations and found this post so I ended up doing this :)
val themeResID = context.resources.getIdentifier((sharedPreferences.getString("theme", "default_standard")), "style", context.packageName) //get theme from shared preferences
context.setTheme(themeResID)
getIdentifier uses reflection under the hood, so it has poor performance. If you're using it sparingly, like to set the theme once whenever a button is pressed, that performance difference will not be noticeable. But the code is overly complicated either way.
Just use your IDs directly like this. No Strings or when statements are necessary.
var myThemeVariable = R.style.redTheme // redTheme, greenTheme, or blueTheme
context.setTheme(myThemeVariable)
I use the composable function image. I need to use the specific image depending on the variable it gets.
it looks like the code underneath when hardcoded and this works for the specific image.
Image(
painterResource(id = R.drawable.carrot),
contentDescription = "carrot",
)
The problem is i want it to work like the picture below where recipe.image changes depending on what it gets from the database.
Image(
painterResource(id = recipe.image),
contentDescription = "recipe.name",
)
Somehow it needs the exact path to the drawable that is saved localy.
What i have tried:
I've tried to upload R.drawable.carrot directly to the database as an Integer, it saves it as something like 2400440340, when this integer gets into the painterResource it crashes.
I've tried to save the "R.drawable.carrot" directly as a string into the database but that don't work either because the painter needs an integer.
The only suggestions i can find to this problem on this site is this answer from 11 years ago:
int drawableId = getResources().getIdentifier(drawablename, "drawable", getPackageName());
imageview.setImageResource(drawableId);
I can't seem to find the functions getResources or the getIdentifier.
What i have tried: I've tried to upload R.drawable.carrot directly to the database as an Integer, it saves it as something like 2400440340, when this integer gets into the painterResource it crashes.
It crashes because this Int value in Resources is just a runtime reference, which means if you restart the app, the Resource, for example R.drawable.carrot, gets new referenced.
If you want another image in your painterResource, you have to change the value of recipe.name somewhere or you save your image binaries in the database (which is not very performant, I recommend not to do that).
You can load them over assets:
If you dont't have an assets folder, creat one in this location:
app/src/main/assets/
And then you can access it with a function like this:
fun getImageFromAssets(imageName: String): Bitmap {
val imageStream = assets.open(imageName)
val bitmap = BitmapFactory.decodeStream(imageStream)
imageStream.close()
return bitmap
}
Android Studio will suggest you to "Inline" the variable but don't do that. If you Inline the BitmapFactory at the return point, you close the Stream before you decode it.
You can now simply call the name from the database like:
val image: Bitmap = getImageFromAssets("imageName.jpg")
We can change image by name using string interpolation in Swift. Is there a way to achieve this in Kotlin? Can we change selected image programatically in Kotlin?
let image = UIIMage(named : "\(imageName)_selected")
You can:
val resourceId = resources.getIdentifier("${imagename}_selected", "drawable", packageName)
Personally, I'd go for a solution like this:
val resourceId = when (imageName) {
"image1" -> R.drawable.image1_selected
"image2" -> R.drawable.image2_selected
}
Although this is a little bit longer, it gives compile time warnings and errors, assuring you the drawable is there.
Perhaps the question is: why are you getting the string imageName? Can it be solved better?
I've created an app that has a list of cards within a RecyclerView that each have functionality of their own. I wanted to have each card choose the next color from an array defined in my colors.xml. In order to accomplish this, within my ViewHolder initialization, I set the background color of the card using cardContainer.setBackgroundColor(colors[this.layoutPosition % colors.size]. This would make it so that the colors would be cycled when more cards are created. However, I seem to be encountering the issue where my layout position is negative despite there being a set number of cards (25) created at the beginning.
While trying to search around and find the cause, I read here that if you call notifyDataSetChanged() the adapterPosition will become -1. While I am not using adapterPosition here, I thought that maybe it would be a similar issue, however, I am not adding any additional data at the time of the creation of the list items.
My ViewHolder code can be seen below. This is where the issue arises, but if any additional code is necessary feel free to ask.
class ViewHolder(itemView : View, private val listener: HabitClickListener) : RecyclerView.ViewHolder(itemView) {
val habitTitle: TextView = itemView.habitTitle
val streak: TextView = itemView.dayCounter
val cardContainer: LinearLayout = itemView.cardContainer
private val decreaseCounterButton : Button = itemView.decreaseCounterButton
private val increaseCounterButton : Button = itemView.increaseCounterButton
init {
chooseCardColor() // Choose the color for each card from the available colors
itemView.setOnClickListener {
listener.onCardClick(this.layoutPosition)
}
decreaseCounterButton.setOnClickListener {
listener.onDecrease(this.layoutPosition)
}
increaseCounterButton.setOnClickListener {
listener.onIncrease(this.layoutPosition)
}
}
private fun chooseCardColor() {
val colors = itemView.resources.getIntArray(R.array.cardColors)
cardContainer.setBackgroundColor(colors[this.layoutPosition % colors.size])
}
}
I will try to simplify this further, you should use the getAdapterPosition of ViewHolder
In recyclerview, storing the data and displaying the data are two separate things(Notice how you can use different managers(LinearLayoutManager, GridLayoutManager) to present the data in a different way.When some data changes in recyclerview, it notifies the ui to change what is shown in the screen. Even though it is really small, there is a delay between the change in the content of recyclerview and change in layout, that's why these two behave differently.
My information in this may be outdated but also don't just use the position variable as it can be inconsistent when another element is added/deleted to recyclerview due to how onBindViewHolder()(existing variables position wasn't updated when a new element is added/deleted) behaves. Instead use getAdapterPosition().
Edit: Quick fix if you don't want to deal with viewHolder gimmicks.
Add a new field to your custom object which decides what color it should be. Then make this calculation in your fragment/activity by looking at the index of your object in the list instead of doing the calculation in the viewHolder. Now you can set the color you want inside the viewHolderby looking at your object's new field. Of course you should be careful when adding/deleting a new object when you do this, but same holds true when you do it via viewHolder
I would like to customize ExpandedControllerActivity in CastSDK v3 with our own UI. But right now its not permitted. Is there any way to do that ?
Currently, the customization is limited to the selection of control buttons that are present on that activity. We, however, are actively looking into expanding on that to add more capabilities; stay tuned.
If you want to customize this activity, it's gonna be a lot of work but it is possible. You can create your own layout file with the Views you want and then bind eachView to the informations present in the cast session using UIMediaController.
There is a lot of possible binding on this UIController so I suggest you try some until your find the one interesting you, here are some exemples :
val uiController = UIMediaController(this)
uiController.bindSeekBar(castSeekBar, 1000L) // Binding a CastSeekBar to video progress
uiController.bindTextViewToStreamPosition(castPositionTextView, true) // Binding a TextView to video current position
uiController.bindTextViewToStreamDuration(castDurationTextView) // Binding a TextView to video duration
Some of the informations, especially metada fields can be accesed using keys presents in MediaMetadata like title or subtitle :
uiController.bindTextViewToMetadataOfCurrentItem(castTitleTextView, MediaMetadata.KEY_TITLE)
uiController.bindTextViewToMetadataOfCurrentItem(castSubtitleTextView, MediaMetadata.KEY_SUBTITLE)
In that activity you can also directly access your video that way :
val sessionManager = CastContext.getSharedInstance(this).sessionManager
val remoteMediaClient = sessionManager.currentCastSession?.remoteMediaClient
val mediaInfo = remoteMediaClient?.mediaInfo
And here, mediaInfo will contains a lot of information like duration, metadata, contentId...
I know it's not very precise but I hope that helps :-)