Responsive width and height when programatically adding views - android

I'm currently using a Table Layout and programatically adding rows and buttons. However, when I add the views a lot of them go off-screen. Is there a way to programatically set the size to the portion of the screen.
I have decent experience with Android, but new to Kotlin.
Here is where I add the views
private fun setupTable () {
for(i in 0 until this.rowSize) {
val row = TableRow(context)
row.layoutParams
row.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
for(j in 0 until this.columnSize) {
val button = Button(context)
button.apply {
layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT)
text = "R $i C $j"
}
row.addView(button)
}
wordLayout?.addView(row)
}
}
Here is the picture for reference. Here I want a 10x10 table and to fit all the buttons inside the TableLayout.

Try the following code:
package net.androidly.androidlylayouts
class MainActivity : AppCompatActivity() {
val ROWS = 10
val COLUMNS = 5
val tableLayout by lazy { TableLayout(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = "ROWS : $ROWS COLUMNS: $COLUMNS"
val lp = TableLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
tableLayout.apply {
layoutParams = lp
isShrinkAllColumns = true
}
createTable(ROWS, COLUMNS)
}
fun createTable(rows: Int, cols: Int) {
for (i in 0 until rows) {
val row = TableRow(this)
row.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
for (j in 0 until cols) {
val button = Button(this)
button.apply {
layoutParams = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT)
text = "R $i C $j"
}
row.addView(button)
}
tableLayout.addView(row)
}
linearLayout.addView(tableLayout)
}
}
Hope this helps.

Related

ImageView and TextView assigned incorrectly in onBindViewHolder

Expected outcome:
I want to show only ImageView and TextView that corresponds to the current position e.g if I have
val icons = craftingExpandableList[position].requiredList?.icon
val amounts = craftingExpandableList[position].requiredList?.amount
I want to show in RecyclerView only icons and amounts from current position, not all existing icons and amounts.
Current outcome:
I've added 2 items to my list. The problem is with the expandable list. It has values and images from both items that I've added previously. I suppose, that the problem is with for loop or with assigning values to the views.
CraftingExpandableListAdapter:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val iconListSize = craftingExpandableList[position].requiredList?.icon?.size
val amountListSize = craftingExpandableList[position].requiredList?.amount?.size
if (iconListSize!! > 0 || amountListSize!! > 0) {
val icons = craftingExpandableList[position].requiredList?.icon
val amounts = craftingExpandableList[position].requiredList?.amount
// Create a LinearLayout to hold the views
val verticalLinearLayout = LinearLayout(mContext)
verticalLinearLayout.orientation = LinearLayout.VERTICAL
verticalLinearLayout.setBackgroundColor(Color.parseColor("#002019"))
for (i in 0 until iconListSize) {
// Create the ImageView and TextView views
val iconView = ImageView(mContext)
val amountView = TextView(mContext)
// Set the image resource and text for the views
iconView.setImageResource(icons!![i])
amountView.text = amounts!![i].toString()
// Create a horizontal LinearLayout to hold the views
val horizontalLinearLayout = LinearLayout(mContext)
horizontalLinearLayout.orientation = LinearLayout.HORIZONTAL
// Set the size and margins of the ImageView view
val layoutParams = LinearLayout.LayoutParams(dpToPx(50), dpToPx(50))
layoutParams.setMargins(dpToPx(16), dpToPx(16), 0, dpToPx(16))
iconView.layoutParams = layoutParams
// Set the margins of the TextView view
val textLayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
textLayoutParams.gravity = Gravity.CENTER_VERTICAL
textLayoutParams.setMargins(dpToPx(16), 0, 0, 0)
amountView.layoutParams = textLayoutParams
// Set the font of the TextView view
// Set the typeface of the TextView view
amountView.typeface = ResourcesCompat.getFont(mContext!!, R.font.roboto_bold)
amountView.setTextColor(Color.parseColor("#F6E9E0"))
// Add the views to the horizontal LinearLayout
horizontalLinearLayout.addView(iconView)
horizontalLinearLayout.addView(amountView)
// Add the horizontal LinearLayout to the vertical LinearLayout
verticalLinearLayout.addView(horizontalLinearLayout)
}
// Add the vertical LinearLayout to the holder's ViewGroup
holder.viewGroup.addView(verticalLinearLayout)
}
}
Additionally my CraftingListModel:
data class CraftingListModel(
val icon: Int = 0,
val name: String = "",
val type: String = "",
val requiredList: RequiredList? = null
)
fun CraftingListModel.toListModel(): List<CraftingListModel> {
val list = mutableListOf<CraftingListModel>()
val requiredList = mutableListOf<RequiredList>()
requiredList.add(RequiredList(listOf(R.raw.high_quality_metal_icon, R.raw.gears_icon), listOf(20, 5)))
requiredList.add(RequiredList(listOf(R.raw.high_quality_metal_icon, R.raw.gears_icon), listOf(30, 5)))
list.add(CraftingListModel(R.raw.armored_door_icon, "Armored Door", "Construction", requiredList[0]))
list.add(CraftingListModel(R.raw.armored_double_door_icon, "Armored Double Door", "Construction", requiredList[1]))
return list
}
and RequiredList:
data class RequiredList(
val icon: List<Int> = emptyList(),
val amount: List<Int> = emptyList()
)

How to use SetOnClickListener on a programmatic ScrollView Android Kotlin

I created a scrollView programmaticaly that contains 20 views each with an image and a text.
I have two questions :
1 - is the id assignment correct and is my setOnClickListener correct?
2 - By which method onClick can I know which view of the scrollView the user has clicked?
See my code below
private var integerList: MutableList<Int>? = mutableListOf()
private var cellNo: MutableList<String>? = mutableListOf()
private var sv_mvmtChoosed = ""
private fun showSpinner() {
/* SCROllL VIEW */
var linearLayout: LinearLayout? = null
linearLayout = findViewById(R.id.linear1)
val layoutInflater = LayoutInflater.from(this)
var randIndex = 0
for (posIndex in 0..19) {
val rand = Random()
randIndex = rand.nextInt(20)
while (integerList!!.contains(randIndex)) {
randIndex = rand.nextInt(20)
}
integerList!!.add(randIndex)
// Create the view...
val view: View = layoutInflater.inflate(R.layout.scroll_bckgrnd, linearLayout, false)
// give it an id
view.id = generateViewId()
view.setOnClickListener(this)
cellNo!!.add(view.id.toString())
println(cellNo)
//... then populate it with image and text
val iv = view.findViewById<ImageView>(R.id.iv)
iv.setImageResource(sv_photoImage[randIndex])
val tv = view.findViewById<TextView>(R.id.tv)
tv.text = sv_photoName[randIndex]
linearLayout?.addView(view)
}
// which view the user did select?
fun onClick(view: View?) {
when (view!!.id) {
??? -> doSomething
}
}
}
Any idea to get me back on track will be welcome.
Its probably better to make a new OnClickListener for every view.
view.setOnClickListener(this)
needs to be this
view.setOnClickListener {
// do something
}
or
view.setOnClickListener(createOnClickListner())
fun createOnClickListner():View.OnClickListener{
return object :View.OnClickListener{
override fun onClick(view : View) {
//do something with the view that was clicked
}
}
}
Thanks a lot avalerio.
I finally found a solution as follow :
I replaced :
// give it an id
view.id = generateViewId()
view.setOnClickListener(this)
cellNo!!.add(view.id.toString())
println(cellNo)
with :
// give it an id
view.id = posIndex
view.setOnClickListener(this)
then I did this :
// the onClickListener for my 20 images/text
override fun onClick(view: View?) {
when (view!!.id) {
// Now de position clicked on the ScrollView
in 0..19 -> didHeSucceeded(view!!.id)
}
}
And use the result:
private fun didHeSucceeded(scrllPos: Int) {
// TODO: close de scrollView, how ? :-)
spinnerChoice = nameOfTechScrollVw[scrllPos]
succes = (!allreadyChoosedArray.contains(spinnerChoice)) && (currentArray.contains(spinnerChoice
))
if (succes) {
...
...
}
It works perfectly

Android - LinearView - Dynamic instanciation of views

I am trying to create functions that instanciate some views in a Linear Layout
private lateinit var layout: LinearLayout
var par = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT)
override fun onCreate(savedInstanceState: Bundle?) {
//The init stuff with binding
layout = binding.root.findViewById(R.id.gamelayout)
lpar.setMargins(10,10,10,10)
test()
}
private fun instanciateText(s:String)
{
val tv = TextView(this)
tv.text = s
tv.layoutParams = par
layout.addView(tv)
}
fun instanciateImage()
{
val iv = ImageView(this)
iv.setImageResource(R.drawable.start1)
iv.layoutParams = par
layout.addView(iv)
}
fun test(){
instanciateText("Text 1 ")
instanciateImage()
instanciateText("Text 2 ")
}
It works fine with text, but whith images, the views are so far from each other, and i cannot find why
If a stretchable ImageView is added, it is to be expanded to the maximum extent so the LinearLayout gets 100%-filled with children. It's right behavior. If you'd like to avoid that, specify ImageView.adjustViewBounds = true.
fun instanciateImage()
{
val iv = ImageView(this)
iv.setImageResource(R.drawable.start1)
iv.layoutParams = par
iv.adjustViewBounds = true
layout.addView(iv)
}

Android, set WRAP_CONTENT layout parameter of ImageView by code not working

This doesn't works as expected:
imageView = ImageView(context)
imageView.adjustViewBounds = true
imageView.scaleType = ImageView.ScaleType.FIT_CENTER
imageView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
imageView.setImageBitmap(myBitmap)
The imageView is added to a TableRow layout (which is added to a Table).
Full function:
fun loadTable(data: List<ListItem>) = lifecycleScope.launch {
val rowParams = TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)
val table = binding.table
val context = table.context
for (i in data.indices) {
val rowIndex = i / TABLE_COLUMNS
val row: TableRow = if (table.childCount <= rowIndex) {
val r = TableRow(context)
r.layoutParams = rowParams
binding.table.addView(r)
table.layoutParams.width = myBitmap.width * TABLE_COLUMNS
r
} else {
table[rowIndex] as TableRow
}
imageView = ImageView(context)
imageView.adjustViewBounds = true
imageView.scaleType = ImageView.ScaleType.FIT_CENTER
imageView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
imageView.setImageBitmap(myBitmap)
row.addView(imageView)
}
}
Expected result: the imageView display the image and has the same size.
Actual result: the imageView has a zero-size and image is not displayed.
With the layout inspector I can see that the attributes are correct, but the size of the imageView remains 0.0.
If I set the same attribute at design time, the imageView is displayed correctly.

Placing dynamically added buttons below each other

I'm building a calculator app and in it there's a ScrollView, used to show and switch the buttons for operators and units whenever the user switches between modes.
The problem is that I didn't want to create a XML layout for each mode, so I thought of adding those buttons programmatically (which now, for me, seems pretty hard to accomplish). Here's the code that's supposed to add them:
// For each text in texts (which represents the modes), a Button is created and, if its id (represented by the index of its respective text in the list) is greater than 0, it is positioned below the previous one.
fun ScrollView.add(context: Context, input: EditText, texts: List<String>) {
removeAllViews()
val container = ConstraintLayout(context).apply {
layoutParams = ConstraintLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
topToTop = this#add.top
startToStart = this#add.left
endToEnd = this#add.right
}
}
val buttons: MutableList<Button> = mutableListOf()
texts.forEach { text ->
val button = Button(context)
val originalWidth = 60
val width = originalWidth.toDimension(context, COMPLEX_UNIT_DIP)
val originalHeight = 60
val height = originalHeight.toDimension(context, COMPLEX_UNIT_DIP)
with(button) {
layoutParams = ConstraintLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
id = texts.indexOf(text)
val previous = try { buttons[id - 1] } catch (exception: Exception) { this }
setWidth(width)
setHeight(height)
with(layoutParams as ConstraintLayout.LayoutParams) {
if (id == 0)
topToTop = this#add.top
else if (id > 0) {
layoutParams = RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
(layoutParams as RelativeLayout.LayoutParams).addRule(BELOW, previous.id)
}
}
left = this#add.left
right = this#add.right
button.text = text
isAllCaps = false
textSize = 25f
while (text().contains(System.getProperty("line.separator").toString())) textSize -= 5
setOnClickListener { input.input((it as Button).text()) }
setBackgroundResource(0)
container.addView(this)
buttons.add(this)
val buttonAdded = "[Dynamic List] Button $id added as \"$text\""
println(if (id == 0) "$buttonAdded." else "$buttonAdded. The button was positioned below \"${previous.text}\" (${previous.id}).")
}
}
addView(container)
}
And here's the code I implemented using the method above:
// Each calculator mode is represented by an Int within the list.
val modes = listOf("calculator" to 1, "length" to 2, "temperature" to 3, "time" to 4)
fun mode(context: Context, button: Button, input: EditText, view: ScrollView) {
var counter = 0
val operators = listOf("+", "-", times.toString(), division.toString())
val length = with(context) { listOf(getString(R.string.light_year), context.getString(R.string.kilometer), context.getString(R.string.hectometer), context.getString(R.string.decameter), context.getString(R.string.mile), context.getString(R.string.meter), context.getString(R.string.centimeter), context.getString(R.string.millimeter), context.getString(R.string.micrometer)) }
val temperature = with(context) { listOf(getString(R.string.celsius), context.getString(R.string.fahrenheit), context.getString(R.string.kevin), context.getString(R.string.rankine), context.getString(R.string.reaumur)) }
val time = with(context) { listOf(getString(R.string.year), context.getString(R.string.month), context.getString(R.string.day), context.getString(R.string.hour), context.getString(R.string.minute), context.getString(R.string.second), context.getString(R.string.millisecond)) }
with(button) {
setOnClickListener {
if (counter < modes.size - 1) counter++ else counter = 0
with(view) {
val mode: Pair<String, Int>? = modes[counter]
when (mode?.first) {
"calculator" -> add(context, input, operators)
"length" -> add(context, input, length)
"temperature" -> add(context, input, temperature)
"time" -> add(context, input, time)
}
text = with(context) {
with(resources) { getString(identify(context, mode?.first, "string")) }
}
}
}
}
}
Well, the problem is when I run it, the UI ends up looking like this, with all the buttons positioned at the same place:

Categories

Resources