how to create dynamic setImageResource - android

How can I replace something like:
when (content[position].ImageSrc) {
1 -> holder.imageView.setImageResource(R.drawable.image_1)
2 -> holder.imageView.setImageResource(R.drawable.image_2)
3 -> holder.imageView.setImageResource(R.drawable.image_3)
else -> {
holder.imageView.setImageResource(R.drawable.image_x)
}
}
more elegant so something like:
var a = "image_"
var b = content[position].ImageSrc
var c = a + b
holder.imageView.setImageResource(R.drawable.c)
The first code is working but way to laborious, the second one isn't.
Thx:)

Make a Repository that can resolve the asset for you...
in your UI you'd do:
holder.imageView.setImageResource(
myResourceRepo.getImageResourceFor(content[position].ImageSrc)
)
Now what you do in there, it really depends, if you really have 100 resources, you have to find a way to MAP them together, either by creating a mapOf(Int, "img source") (whatever that is), where Int is the R.drawable.xxx and img source is whatever is in content[...].imageSrc (a String?)
This is how you map/associate them together.
If you want it to be more dynamic, then store your images in "raw assets" and using the AssetManager, load the image and convert it into a Drawable at runtime. This way you get to "construct" the asset name like you did
var filename = "some/path/" + content[...].imageSrc
val file = assetManager.open(filename)...
val drawable = makeDrawableFrom(file)
return drawable
Your viewHolder will then use it as
val drawable = repo.getDrawableFrom(content[x].imageSrc)
holder.imageView.setImageResource(drawable)
(NOTE: most of these are "pseudo-code", you have to implement most of it, but it's all possible and quite simple).
If you REALLY want to try to load a resource dynamically... it used to be possible with something like
val nameOfResource = "a" + "b" + "c" //construct your "dynamic" name
val drawable = context.resources.getIdentifier(nameOfResource,
"drawable", context.getPackageName())
(not sure if this still works)

I doubt your "second method" exist instead, you can reformat your first one as follows,
holder.imageView.setImageResource(
when (content[position].ImageSrc) {
1 -> R.drawable.image_1
2 -> R.drawable.image_2
3 -> R.drawable.image_3
else ->R.drawable.image_else
}
)

val imageId = resources.getIdentifier("com.packagename.app:drawable/image_${content[position].ImageSrc}", null, null);
holder.imageView.setImageResource(imageId)

Related

Koltin, setting a mediaplayer from a variable

I'm working on an application, where for specific choosen items, specific .mp3 should be assigned. (Like I choose Dachshund, then I dachshund_1.mp3, dachshund_2.mp3 etc. to be played when my functions say so). Is it possible somehow, that I create a variable, that holds the specific name, then I assign it to mediaplayer.create?
What I would like to do would look like that below:
// I have a function here that returns with the specific string
// I have a cnt variable in the code which helps determine which text and sound comes now
fun DogHandler(cnt:Int, receiptName: String) :String
{
return dogName + "_" +cnt.toString()
}
This function is called, and the string it returns should go to the mediaplayer. Cnt is let's say 10
var tmp = DogHandler(dachshund, cnt); // tmp = dachsund_10
mediaPlayer = MediaPlayer.create(context, R.raw.tmp)
mediaPlayer.start()
To my knowledge there is no way to generate resource IDs (like R.raw.something) dynamically. However, you could simply create a list of them and access it by index.
val dachsunds = listOf(
R.raw.dachsund_0,
R.raw.dachsund_1,
R.raw.dachsund_2,
R.raw.dachsund_3,
// ...
R.raw.dachsund_10
)
val dachsund = dachsunds[dachsundIndex]
val mediaPlayer = MediaPlayer.create(context, dachsund).apply {
start()
}

Kotlin - Add more variant - soundPool

please help. I need to add 2 sounds ogg to one action, which will change randomly. How do I do that? I don't know how to do it. Thank you very much.
Thread(Runnable {
val assets = context.resources.assets
sounds[SOUND_DIE] = soundPool.load(assets.openFd("die.ogg"), 1)
sounds[SOUND_HIT] = soundPool.load(assets.openFd("hit.ogg"), 1)
sounds[SOUND_POINT] = soundPool.load(assets.openFd("point.ogg"), 1)
sounds[SOUND_SWOOSHING] = soundPool.load(assets.openFd("swooshing.ogg"), 1)
sounds[SOUND_WING] = soundPool.load(assets.openFd("wing.ogg"), 1)
}).start()
}
You can call .random() on a collection to get a random item. So instead of mapping each sound type (like SOUND_HIT) to one sound, map each one to a list of sounds (which can contain one item if that's all you have). And then for every sound you load, just add it to the appropriate list.
That way when you want to play a sound, you can go play(sounds[SOUND_SWOOSHING].random()) and it will just pick one from that sound type's list.
You can set it up the way you're doing now
sounds = mapOf(
SOUND_DIE to listOf(
soundPool.load(assets.openFd("die.ogg"), 1),
soundPool.load(assets.openFd("yargh.ogg"), 1)
),
SOUND_HIT to ...
)
but I'd recommend adding a function to handle all that loading:
fun loadSound(filename: String) = soundPool.load(assets.openFd(filename), 1)
sounds = mapOf(
SOUND_DIE to listOf(
loadSound("die.ogg"),
loadSound("yargh.ogg")
),
SOUND_HIT to ...
)
or if you want to get fancy...
val filenamesToTypes = mapOf(
"die.ogg" to SOUND_DIE,
"yargh.ogg" to SOUND_DIE,
"point.ogg" to SOUND_POINT,
...
)
// build your sounds collection by grouping all the filenames
// with the same sound type, and transform each filename to a
// loaded sound, so you get a map of SoundType -> List<Sound>
sounds = filenamesToTypes.entries.groupBy(
keySelector = { it.value },
valueTransform = { loadSound(it.key) }
)
Don't worry if that feels too complicated, the first couple of examples are neat enough and hopefully easy to follow! I just like when you can organise stuff all snappy :)

How to detect line breaks in TextView

In my Android App I've created 8 TextViews stacked on top of each other. Now I want to load in some plain text into those TextView-Lines. At the moment my Strings have a ";" as delimiter to indicate a line break, however it would be much more convenient if I would detect a linebreak automatically instead of using the hardcoded semicolon approach.
This is my String at the moment:
myString = "" +
"This seems to be some sort of spaceship,;" +
"the designs on the walls appear to be of;" +
"earth origin. It looks very clean here.;"
And in my other class I load in this string into the 8 TextViews, which I've loaded into an ArrayList, using the ";" as a delimiter.
public fun fillLines(myString: String) {
// How To Make Line Breaks Automatic??
for(i: Int in str until myString.split(";").size) {
if(i > textViewArray.size - 1) {
break
}
textViewArray[i].text = myString.split(";")[i]
textViewArray[i].alpha = 1.0f
}
}
Is there any way I can get the same result as shown above but without hardcoding the delimiter as ";" but instead somehow automatically detect the line break which would occur inside the TextView and then use this as a delimiter to advance through all 8 TextView "Lines".
The reason I need 8 TextViews Stacked On top of each other as individual "text lines" is because of an animation technique I want to use.
Line-breaking gets fairly complicated, so my recommendation would be that you allow a TextView to perform the measuring and layout to determine the line breaks. You could have an invisible TextView with the same style as your other views, and attach it to the layout so that it has the same width as your individual TextView instances. From there, add a layout change listener, and you can then retrieve the individual lines from the TextView Layout:
myTextView.text = // your text string here
myTextView.addOnLayoutChangeListener { view, _, _, _, _, _, _, _, _ ->
(view as? TextView)?.layout?.let { layout ->
// Here you'll have the individual broken lines:
val lines = (0 until layout.lineCount).map {
layout.text.subSequence(layout.getLineStart(it), layout.getLineVisibleEnd(it)
}
}
}
That said, this comes with the caveat that you'll lose out on hyphenation provided by the TextView, so you may wish to disable hyphenation entirely in your case.
You could fill text view with html. Below example.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
tvDocument.setText(Html.fromHtml(bodyData,Html.FROM_HTML_MODE_LEGACY));
} else {
tvDocument.setText(Html.fromHtml(bodyData));
}
If your delimiter ; it is possible call method replaceAll(";", "<br>");
Ok I got it working now:
First you must add these properties for the textviews:
android:singleLine="true"
android:ellipsize="none"
Then you can do this:
public fun fillStorylines() {
val linecap = 46
var finalLine: String
var restChars = ""
val index = 9999
val text1: String = "" +
"This seems to be some sort of spaceship, " +
"the designs on the walls appear to be of " +
"earth origin. It looks very clean here. "
for(j: Int in 0..index) {
try {
finalLine = ""
val lines: List<String> = (restChars + text1.chunked(linecap)[j]).split(" ")
for (i: Int in 0 until lines.size - 1) {
finalLine += lines[i] + " "
}
textViewArray[j].text = finalLine
textViewArray[j].alpha = 1.0f
restChars = lines[lines.size - 1]
} catch (ex: Exception) {
break
}
}
}
If anyone knows a more elegant way to solve this please go ahead, your feedback is appreciated :)

How to refer to outer this on forEach in Kotlin

I have the following case
someThing.forEach{
someWidget.setOnClickListener{
//it is an View
//I need foreach it of someObject
}
}
I read this answer but it does not work
kotlin how to refer outer-scope this in multi-layer apply functions
The problem is that you are not dealing with this here.
forEach has a parameter and for simplicity you can leave it away and just use it instead. Not using it is the same as using _ -> instead... you just discard it.
So your example written with named lambda parameters instead:
someThing.forEach{ some -> // 'it' was available here too, but will not be accessible from within the next setOnClickListener...
someWidget.setOnClickListener{
// some contains one of the someThings now and 'it' is still your View
}
}
You can name the variable in the forEach.
things.forEach { thing ->
someWidget.setOnClickListener {
thing.doSomething()
}
}
I think you mean something like this:
someThing.forEach{ x->
someWidget.setOnClickListener{
//use x
//I need foreach it of someObject
}
}
just use another name like x, you don't have to use it.
Here is an example:
val a = mutableListOf<Int>(1, 3)
val b = mutableListOf<Int>(2, 4)
a.forEach { x ->
b.forEach {
println("" + x + " " + it)
}
}
here x is each item from list a
and it is each item from list b

How to append 2 strings in Kotlin?

I am trying to concatenate 2 String but not sure how to go about it.
this is my code:
val word = R.string.word
and i'm trying to append it with "$currentPage/5" inside the setText("$currentPage/5")
i tried to make it in this way setText("$word $currentPage/5")
and this way setText("${R.string.value} $currentPage/5")
and it did not work , it only shows me numbers not the text
try to use this:
val word = getString(R.string.word)
text_view.text = "$word $currentPage/5"
If you want to edit your value (e.g. current page) wrap it with {}
E.g.
val word = getString(R.string.word)
text_view.text = "$word ${currentPage/5}"
Remember to use proper kotlin syntax
In Kotlin, the concatenation of string can be done by **interpolation/templates**.
val a = "Its"
val b = "Kotlin!"
val c = "$a $b"
The output will be Its Kotlin!
Or we can alson do concatenate using the **+ / plus() operator**:
val a = "String"
val b = "Concatenate"
val c = a + b
val d =a.plus(b)
print(c)
The output will be: StringConcatenate
print(d)
The output will be: StringConcatenate
Or you can concatenate using the StringBuilder which is a normal way to do that.
To concatenate two string, we could do
val concatenatedWord = "${resources.getString(R.string.value)}:
${number/3}."
If R.string.value was "The result" and number was 15, value of concatenatedWord will be "The result: 5."
Or we could also concatenate using the + operator or using StringBuilder.
But if you do
textView.text = "${resources.getString(R.string.value)}: ${number/3}."
AS will warn "Do not concatenate text displayed with setText." so, in the case of setting concatenated text in textview, consider using
String.format("%s: %d.", resources.getString(R.string.value):
number/3)
As a future resource and answer why the accepted answer works:-
String Templates:-
Strings may contain template expressions, i.e. pieces of code that are evaluated and whose results are concatenated into the string.
How to implement these?
A template expression should start with a dollar sign ($) and consists of either a simple name:
when the expression is a simple variable.
val i = 10
println("i = $i") // prints "i = 10"
or else arbitrary expression in curly braces:
val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"
Note :- Templates are supported both inside raw strings and inside escaped strings.
val nameOfAnimal = "fish"
val speciesClass = "is an Aquatic Vertebrate"
println(nameOfAnimal.plus(speciesClass))
println(nameOfAnimal+speciesClass)
println("$nameOfAnimal $speciesClass")
Results:
fishis an Aquatic Vertebrate
fishis an Aquatic Vertebrate
fish is an Aquatic Vertebrate

Categories

Resources