Add relative location RelativeLayout.BELOW - android

I've the below code, in which I construct a "card" and adding to it both Switch and button, I need the button to be exactly under the switch, how to make it!
val swithy = Switch(this).apply {
text = "active"
isChecked = true
id = View.generateViewId()
}
val mcard = CardView(this).apply {
background = getDrawable(R.drawable.card_background)
radius = 12F
setContentPadding(25, 25, 25, 25)
setCardBackgroundColor(Color.LTGRAY)
cardElevation = 8F
maxCardElevation = 12F
addView(swithy, RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT).apply {
RelativeLayout.ALIGN_PARENT_BOTTOM
RelativeLayout.ALIGN_PARENT_LEFT
})
addView(Button(this.context).apply {
text = "click me"
}, LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT).apply {
/* addRule(RelativeLayout.BELOW, swithy.id); */
// How to make this?! it works in Java, how to make it in Kotlin
})
}
card_background.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:name="custom_view"
android:id="#+id/custom_view">
<stroke
android:width="1dp"
android:color="#c2f2f2f2" />
<solid
android:color="#FFFFFFFF"
/>
<corners
android:bottomRightRadius="2dp"
android:bottomLeftRadius="2dp"
android:topLeftRadius="2dp"
android:topRightRadius="2dp" />
</shape>

I was able to manage my requirements using ConstraintLayout, I'm posting my answer here, may some people find it helpful:
In MainActivity.kt I'm calling my custom element as:
main_layout.addView(Cardy(this))
My custom element Cardy.kt is:
import android.content.Context
import android.support.constraint.ConstraintLayout
import android.support.v4.content.ContextCompat
import android.widget.Button
import oryx.kortex.locateme.R
import android.graphics.Color
import android.support.constraint.ConstraintSet
import android.widget.EditText
import android.util.TypedValue
class Cardy : ConstraintLayout {
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
val r = resources
val px = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 200f,
r.displayMetrics).toInt()
val myEditText = EditText(context).apply {
id = ConstraintLayout.generateViewId()
width = px
}
val myButton = Button(context).apply {
text = "Press Me"
setBackgroundColor(Color.BLUE)
id = ConstraintLayout.generateViewId()
}
this.apply {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
background = ContextCompat.getDrawable(context, R.drawable.card)
addView(myButton)
addView(myEditText)
}
val set = ConstraintSet().apply {
constrainHeight(myButton.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myButton.id, ConstraintSet.WRAP_CONTENT)
connect(myButton.id, ConstraintSet.LEFT,
ConstraintSet.PARENT_ID, ConstraintSet.LEFT, 0)
connect(myButton.id, ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, 0)
connect(myButton.id, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(myButton.id, ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0)
constrainHeight(myEditText.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myEditText.id, ConstraintSet.WRAP_CONTENT)
connect(myEditText.id, ConstraintSet.LEFT,
ConstraintSet.PARENT_ID, ConstraintSet.LEFT, 0)
connect(myEditText.id, ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, 0)
connect(myEditText.id, ConstraintSet.BOTTOM,
myButton.id, ConstraintSet.TOP, 70)
}
set.applyTo(this)
}
}
And the output is:
NOTES:
This part is to center the element horizontal:
connect(myButton.id, ConstraintSet.LEFT,
ConstraintSet.PARENT_ID, ConstraintSet.LEFT, 0)
connect(myButton.id, ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, 0)
This part is to center the element vertically:
connect(myButton.id, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0)
connect(myButton.id, ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, 0)
To have both elements in the same line, below code do the job:
val set = ConstraintSet().apply {
constrainHeight(myEditText.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myEditText.id, ConstraintSet.WRAP_CONTENT)
constrainHeight(myButton.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myButton.id, ConstraintSet.WRAP_CONTENT)
connect(myButton.id, ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT,0)
}

It can be done using the below:
RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
addRule(RelativeLayout.BELOW, ELEMENT.id)
}
Below is a full running code with me:
val card1 = Switchy(this).apply {
id = ConstraintLayout.generateViewId()
}
val card2 = Cardy(this).apply {
id = ConstraintLayout.generateViewId()
}
main_layout.addView(card1)
main_layout.addView(card2,
RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
addRule(RelativeLayout.BELOW, card1.id)
})
main_layout.addView(Settings(this),
RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
addRule(RelativeLayout.BELOW, card2.id)
}
)
Where Switchy is:
class Switchy : RelativeLayout {
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
val colorOn = -0xcdc1ba
val trackStates = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf()),
intArrayOf(Color.LTGRAY, colorOn)
)
this.apply {
layoutParams = android.widget.RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
addView(Switch(context).apply {
background = ContextCompat.getDrawable(context, R.drawable.card)
text = "active or not is it ?"
isChecked = true
showText = true
textOn = "Active"
textOff = "Not Active"
trackTintList = trackStates
thumbTintList = trackStates
}, LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT))
}
}
}
And Cardy is:
class Cardy : ConstraintLayout {
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
val r = resources
val px = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 200f,
r.displayMetrics).toInt()
val myEditText = EditText(context).apply {
id = ConstraintLayout.generateViewId()
width = px
}
val myImageButton = ImageButton(context).apply({
background = null
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_save_black_24px))
})
val myButton = Button(context).apply {
text = "Press Me"
setBackgroundColor(Color.BLUE)
id = ConstraintLayout.generateViewId()
}
this.apply {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
background = ContextCompat.getDrawable(context, R.drawable.card)
addView(myEditText)
// addView(myButton)
addView(myImageButton)
}
val set = ConstraintSet().apply {
constrainHeight(myEditText.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myEditText.id, ConstraintSet.WRAP_CONTENT)
constrainHeight(myImageButton.id, ConstraintSet.WRAP_CONTENT)
constrainWidth(myImageButton.id, ConstraintSet.WRAP_CONTENT)
connect(myImageButton.id, ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT,0)
}
set.applyTo(this)
}
}

LinearLayout also could be an option, below code set a an EditText with ImageButton:
import android.content.Context
import android.support.v4.content.ContextCompat
import android.support.v7.widget.CardView
import android.widget.*
import android.widget.LinearLayout
class RespondTo : CardView {
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
val parent = LinearLayout(context)
parent.apply {
layoutParams = LinearLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, 1.0f).apply {
orientation = LinearLayout.HORIZONTAL
addView(EditText(context).apply {
id = generateViewId()
layoutParams = LinearLayout.LayoutParams(0,
LinearLayout.LayoutParams.WRAP_CONTENT, 0.9f).apply {
}
})
addView(ImageButton(context).apply({
layoutParams = LinearLayout.LayoutParams(0,
LinearLayout.LayoutParams.WRAP_CONTENT, 0.1f)
background = null
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_save_black_24px))
id = generateViewId()
layoutParams = RelativeLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT).apply {
addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
// addRule(RelativeLayout.LEFT_OF, myImageButton.id)
}
}))
}
}
this.addView(parent)
}
}

Related

Save MutableListOf<Checkbox> in Kotlin

I have a problem and I don't know how I could create a function to save the state of a MutableListOf when the application is closed and another one to restore it when it is opened again. Could someone help me?
Code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
val text = findViewById<TextView>(R.id.editTextTextPersonName)
val layout = findViewById<ConstraintLayout>(R.id.layout)
var numbers = 0
var checkboxes = mutableListOf<CheckBox>()
button.setOnClickListener {
val checkbox = CheckBox(this)
checkbox.text = text.text
text.text = ""
checkbox.textSize = 13f
val font = Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)
checkbox.typeface = font
layout.addView(checkbox)
val params = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT
)
params.setMargins(100, numbers * 75, 100, 100)
params.width = 1020
params.height = 120
checkbox.layoutParams = params
checkbox.id = View.generateViewId()
val constraintSet = ConstraintSet()
constraintSet.clone(layout)
constraintSet.connect(
checkbox.id,
ConstraintSet.TOP,
layout.id,
ConstraintSet.TOP,
numbers * 75
)
constraintSet.connect(
checkbox.id,
ConstraintSet.START,
layout.id,
ConstraintSet.START,
0
)
constraintSet.applyTo(layout)
numbers += 1
checkboxes.add(checkbox)
checkbox.setOnClickListener {
checkbox.animate().alpha(1f).withEndAction {
checkbox.isChecked = false
layout.removeView(checkbox)
numbers -= 1
checkboxes.remove(checkbox)
checkboxes.forEachIndexed { index, cb ->
val params = cb.layoutParams as ConstraintLayout.LayoutParams
params.setMargins(0, index * 75, 0, 0)
cb.layoutParams = params
val constraintSet = ConstraintSet()
constraintSet.clone(layout)
constraintSet.connect(
cb.id,
ConstraintSet.TOP,
layout.id,
ConstraintSet.TOP,
index * 75
)
constraintSet.applyTo(layout)
}
}.start()
}
}
}
}
I have tried many things but none of them have worked

OnClickListener on cardViews not working properly Kotlin

I've been having this problem for a couple of days, and I don't see what I'm doing wrong.
I am trying to add a cardView with some Views inside, to a LinearLayout, one for each note that I have in the DB. So far no problem, the issue is that I also want to add an OnClickListener to each of those cardViews that I am creating programmatically. So that each time the CardView is clicked it expands or collapses.
The problem is that when doing so, all the CardViews share the OnClickListener, and when clicking on one, all the CardViews expand, I want only the one I'm clicking to expand.
I hope I have explained myself, I share part of the code.
for (nota in notas) {
val card = CardView(view.context)
val note = TextView(view.context)
val date = TextView(view.context)
val lLHor = LinearLayout(view.context)
val lLVer = LinearLayout(view.context)
val im = ImageView(view.context)
lLVer.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1f
)
with(im) {
setImageDrawable(
ContextCompat.getDrawable(
view.context,
R.drawable.ic_arrow_down
)
)
foregroundGravity = Gravity.END
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 0f
)
}
lLHor.orientation = (LinearLayout.HORIZONTAL)
lLVer.orientation = (LinearLayout.VERTICAL)
with(note) {
setTextColor(Color.parseColor("#252323"))
text = nota.getString("Nota")
textSize = 20F
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1f
)
}
with(date) {
setTextColor(Color.parseColor("#252323"))
text = nota.getString("Fecha")
textSize = 20F
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 0f
)
}
card.layoutParams = lP
card.setBackgroundColor(Color.parseColor("#FFFFFF"))
card.setOnClickListener {
cardEC(card, im)
}
lLVer.addView(date)
lLVer.addView(note)
lLHor.addView(lLVer)
lLHor.addView(im)
card.addView(lLHor)
linearNotas.addView(card)
}
private fun cardEC(cardView: CardView, imageView: ImageView) {
val height: Int =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 28F, resources.displayMetrics)
.toInt()
val layPar = cardView.layoutParams
if (cardView.layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
layPar.height = height
cardView.layoutParams = layPar
imageView.setImageDrawable(
view?.let {
ContextCompat.getDrawable(
it.context,
R.drawable.ic_arrow_down
)
})
} else {
layPar.height = ViewGroup.LayoutParams.WRAP_CONTENT
cardView.layoutParams = layPar
imageView.setImageDrawable(
view?.let {
ContextCompat.getDrawable(
it.context,
R.drawable.ic_arrow_up
)
})
}
}
Edit: I add images to see the problem well
Card before press
Then when i press any cardView
Cards after press
you can set note visibility to GONE when card is not expanded
and set height of card view to 63 when the card is expanded
MainActivity
package com.example.testapp
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import com.example.testapp.entity.Note
import kotlinx.android.synthetic.main.activity_main.*
import org.w3c.dom.Text
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val notas = arrayListOf<Note>()
for (i in 0..20) {
notas.add(Note(i, "note$i"))
}
val linearNotas = main_lp
for (nota in notas) {
val card = CardView(this)
val note = TextView(this)
val date = TextView(this)
val lLHor = LinearLayout(this)
val lLVer = LinearLayout(this)
val im = ImageView(this)
lLVer.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1f
)
with(im) {
setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.ic_baseline_5g_24
)
)
foregroundGravity = Gravity.END
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 0f
)
}
lLHor.orientation = (LinearLayout.HORIZONTAL)
lLVer.orientation = (LinearLayout.VERTICAL)
with(note) {
setTextColor(Color.parseColor("#252323"))
text = nota.title
textSize = 20F
visibility = View.GONE
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1f
)
}
with(date) {
setTextColor(Color.parseColor("#252323"))
text = "date ${nota.title}"
textSize = 20F
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 0f
)
}
card.setBackgroundColor(Color.parseColor("#FFFFFF"))
card.setOnClickListener {
cardEC(card, im,note)
}
lLVer.addView(date)
lLVer.addView(note)
lLHor.addView(lLVer)
lLHor.addView(im)
card.addView(lLHor)
linearNotas.addView(card)
}
}
private fun cardEC(cardView: CardView, imageView: ImageView,note:TextView) {
val height: Int =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 63f, resources.displayMetrics)
.toInt()
val layPar = cardView.layoutParams
if (cardView.layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
note.visibility= View.VISIBLE
layPar.height = height
cardView.layoutParams = layPar
imageView.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.ic_baseline_arrow_drop_down_24
)
)
} else {
note.visibility= View.GONE
layPar.height = ViewGroup.LayoutParams.WRAP_CONTENT
cardView.layoutParams = layPar
imageView.setImageDrawable(
ContextCompat.getDrawable(
this,
R.drawable.ic_baseline_arrow_drop_up_24
)
)
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/main_lp"
/>
</ScrollView>
I "solved"
Although I still don't understand why the problem occurred, I found a way to bypass it. I leave the solution I found in case it helps someone.
Based on the help #DavudDavudov gave me.
I keep the height of the CardView as WrapContent. When I create the TextView that contains the note, I set the View.Gone visibility to it. Then in the cardEC function I change the visibility of the TextView to View.Visible or View.Gone depending on how it was. And since the CardView is in WrapContent, plus the animateLayoutChanges It works perfectly, it gives the sensation of opening a drop-down menu. I copy part of the code in case it helps.
with(note) {
setTextColor(Color.parseColor("#252323"))
text = nota.getString("Nota")
textSize = 20F
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1f
)
visibility = View.GONE
}
val noteContent = mutableListOf(note)
cardEC(card, im, noteContent)
private fun cardEC(cardView: CardView, imageView: ImageView, noteContent: MutableList<TextView>) {
cardView.setOnClickListener {
for (textView in noteContent){
if (textView.visibility == View.VISIBLE){
textView.visibility = View.GONE
imageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_arrow_down, null))
}else{
textView.visibility = View.VISIBLE
imageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_arrow_up, null))
}
}
}
}

Constraint Layout programmatically MATCH_CONSTRAINT width not working

I'm generating my (constraint)layout programmatically.
Everything shows up as it's supposed to, with proper constraints, but I can't make my views MATCH_CONSTRAINT width to spread as it's supposed to (like when you set 0dp when making it with XML).
I found this:
Set ConstraintLayout width to match parent width programmatically
helpful, but doesn't work at all.
Posting my layout generaitng function below:
private fun generateLayout(formData: Form) {
val decodedBytes = Base64.getDecoder().decode(formData.form)
val decodedString = String(decodedBytes)
val gson = Gson()
val formFields = gson.fromJson(decodedString, Array<FormField>::class.java).asList()
val constraintLayout: ConstraintLayout = findViewById(R.id.constraintLayout)
val constraintSet = ConstraintSet()
val layoutParams = ConstraintLayout.LayoutParams(0, WRAP_CONTENT)
var views: ArrayList<View> = ArrayList()
var formFieldsCounter: Int = 0
//parsing fields form model class to actual view
formFields.forEach {
var view: View? = null
view?.layoutParams = layoutParams
when (it.type) {
"textField" -> {
view = EditText(this)
view.hint = it.fieldName
}
"checkBox" -> {
view = CheckBox(this)
//view.isChecked = it.isChecked
}
}
view!!.id = it.id!!
view.visibility = View.VISIBLE
views.add(view)
constraintLayout.addView(view)
}
//set constraints between created views
constraintSet.clone(constraintLayout)
views.forEach {
constraintSet.constrainDefaultWidth(it.id, ConstraintSet.MATCH_CONSTRAINT_SPREAD)
//TOP CONSTRAINT
if (formFields[formFieldsCounter].layout_constraintTop_toSBottmOf == 0)
constraintSet.connect(
it.id,
ConstraintSet.TOP,
R.id.applicationNumberTextView,
ConstraintSet.BOTTOM
)
else if (formFields[formFieldsCounter].layout_constraintTop_toSBottmOf != null)
constraintSet.connect(
it.id,
ConstraintSet.TOP,
formFields[formFieldsCounter].layout_constraintTop_toSBottmOf!!,
ConstraintSet.BOTTOM
)
//BOTTOM CONSTRAINT
if (formFields[formFieldsCounter].layout_constraintBottom_toTopOf == 0)
constraintSet.connect(
it.id,
ConstraintSet.BOTTOM,
R.id.parent,
ConstraintSet.TOP
)
else if (formFields[formFieldsCounter].layout_constraintBottom_toTopOf != null)
constraintSet.connect(
it.id,
ConstraintSet.BOTTOM,
formFields[formFieldsCounter].layout_constraintBottom_toTopOf!!,
ConstraintSet.TOP
)
//LEFT CONSTRAINT
if (formFields[formFieldsCounter].layout_constraintStart_toSEndOf == 0)
constraintSet.connect(
it.id,
ConstraintSet.LEFT,
R.id.parent,
ConstraintSet.LEFT
)
else if (formFields[formFieldsCounter].layout_constraintStart_toSEndOf != null)
constraintSet.connect(
it.id,
ConstraintSet.LEFT,
formFields[formFieldsCounter].layout_constraintStart_toSEndOf!!,
ConstraintSet.RIGHT
)
//RIGHT CONSTRAINT
if (formFields[formFieldsCounter].layout_constraintEnd_toStartOf == 0)
constraintSet.connect(
it.id,
ConstraintSet.RIGHT,
R.id.parent,
ConstraintSet.RIGHT
)
else if (formFields[formFieldsCounter].layout_constraintEnd_toStartOf != null)
constraintSet.connect(
it.id,
ConstraintSet.RIGHT,
formFields[formFieldsCounter].layout_constraintEnd_toStartOf!!,
ConstraintSet.LEFT
)
formFieldsCounter++
}
constraintSet.applyTo(constraintLayout)
}
This is what i get:
It's pretty much the same what I get without setting
view?.layoutParams = layoutParams
or
constraintSet.constrainDefaultWidth(it.id, ConstraintSet.MATCH_CONSTRAINT_SPREAD)
I'm stuck with it for a while now. Thanks a million for any help :)

How to create tree layout using constraint layout in kotlin programatically?

I want to create a tree layout dynamically using constraint layout.
Something like this:
With tree child nodes retrieved at runtime.
Specifications:
1. All nodes can have a maximum of three child nodes.
2. The whole layout must be created programmatically.
3. Null is passed in data if a node does not have a child.
What I tried:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
createTree(treeNode)
}
private fun createTree(treeNode: TreeNode) {
val rootView: ConstraintLayout = dataBinding.root as ConstraintLayout
val treeView = createLayout(treeNode, 0)
if (treeView != null) {
val mainViewConstraintSet = ConstraintSet()
mainViewConstraintSet.clone(treeView)
mainViewConstraintSet.connect(
rootView.id,
ConstraintSet.START,
treeView.id,
ConstraintSet.START
)
mainViewConstraintSet.connect(
rootView.id,
ConstraintSet.END,
treeView.id,
ConstraintSet.END
)
mainViewConstraintSet.applyTo(treeView)
rootView.addView(treeView)
Log.e("Log", "Added Root View")
}
}
private fun createLayout(treeNode: TreeNode?, depth: Int): ConstraintLayout? {
if (depth > 1) {
return null
}
if (treeNode == null) {
return null
}
val layoutView = ConstraintLayout(context)
val layoutViewLayoutParams = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT
)
layoutViewLayoutParams.topMargin = 16
layoutViewLayoutParams.leftMargin = 16
layoutViewLayoutParams.rightMargin = 16
layoutViewLayoutParams.bottomMargin = 16
layoutView.layoutParams = layoutViewLayoutParams
layoutView.setBackgroundColor(Color.CYAN)
layoutView.id = ViewCompat.generateViewId()
val layoutNode = createNode(treeNode.user)
Log.e("Log", "Created node for " + treeNode.user.name)
val leftChildLayout = createLayout(treeNode.left, depth + 1)
val middleChildLayout = createLayout(treeNode.middle, depth + 1)
val rightChildLayout = createLayout(treeNode.right, depth + 1)
val mainViewConstraintSet = ConstraintSet()
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.TOP,
layoutView.id,
ConstraintSet.TOP,
16
)
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.BOTTOM,
layoutView.id,
ConstraintSet.BOTTOM,
16
)
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.START,
layoutView.id,
ConstraintSet.START,
16
)
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.END,
layoutView.id,
ConstraintSet.END,
16
)
if (leftChildLayout != null) {
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.BOTTOM,
leftChildLayout.id,
ConstraintSet.TOP,
16
)
}
if (middleChildLayout != null) {
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.BOTTOM,
middleChildLayout.id,
ConstraintSet.TOP,
16
)
}
if (rightChildLayout != null) {
mainViewConstraintSet.connect(
layoutNode.id,
ConstraintSet.BOTTOM,
rightChildLayout.id,
ConstraintSet.TOP,
16
)
}
if (leftChildLayout != null && middleChildLayout != null) {
mainViewConstraintSet.connect(
leftChildLayout.id,
ConstraintSet.END,
middleChildLayout.id,
ConstraintSet.START,
16
)
mainViewConstraintSet.connect(
middleChildLayout.id,
ConstraintSet.END,
leftChildLayout.id,
ConstraintSet.START,
16
)
}
if (rightChildLayout != null && middleChildLayout != null) {
mainViewConstraintSet.connect(
middleChildLayout.id,
ConstraintSet.END,
rightChildLayout.id,
ConstraintSet.START,
16
)
mainViewConstraintSet.connect(
rightChildLayout.id,
ConstraintSet.END,
middleChildLayout.id,
ConstraintSet.START,
16
)
}
mainViewConstraintSet.applyTo(layoutView)
// layoutView.setConstraintSet(mainViewConstraintSet)
if (leftChildLayout != null) {
layoutView.addView(leftChildLayout)
}
if (middleChildLayout != null) {
layoutView.addView(middleChildLayout)
}
if (rightChildLayout != null) {
layoutView.addView(rightChildLayout)
}
layoutView.addView(layoutNode)
return layoutView
}
private fun createNode(user: User): ConstraintLayout {
val node = ConstraintLayout(context)
node.id = ViewCompat.generateViewId()
val nodeTextView = TextView(context)
nodeTextView.id = ViewCompat.generateViewId()
nodeTextView.text = user.name
nodeTextView.textSize = 18f
nodeTextView.setTextColor(Color.BLACK)
nodeTextView.setTypeface(null, Typeface.BOLD)
val layoutParams = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT
)
nodeTextView.layoutParams = layoutParams
nodeTextView.setBackgroundColor(Color.GREEN)
val constraintSet = ConstraintSet()
constraintSet.clone(node)
constraintSet.connect(nodeTextView.id, ConstraintSet.TOP, node.id, ConstraintSet.TOP, 16)
constraintSet.connect(nodeTextView.id, ConstraintSet.LEFT, node.id, ConstraintSet.LEFT, 16)
constraintSet.applyTo(node)
node.addView(nodeTextView)
node.setBackgroundColor(Color.RED)
return node
}
The Tree Data Model:
data class TreeNode( val user: User, var parent: TreeNode?,
var left: TreeNode?, var middle: TreeNode?, var right: TreeNode?) { }

Adding views to ConstraintLayout programmatically

I know this question has been asked multiple times, but this seems so difficult to achieve through code.
What I am trying to achieve is to extend the ConstraintLayout and add two views to it. I have the following code:
class TestLayout #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val ids = mutableListOf<Int>()
init {
addTextView()
addTextView()
val set = ConstraintSet()
set.clone(this)
set.connect(ids[0], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 200)
set.connect(ids[1], ConstraintSet.TOP, ids[0], ConstraintSet.BOTTOM)
set.applyTo(this)
}
private fun addTextView() {
val view = TextView(context)
addView(view, LayoutParams(LayoutParams.MATCH_CONSTRAINT, LayoutParams.WRAP_CONTENT))
view.id = View.generateViewId()
view.text = view.id.toString()
ids.add(view.id)
}
}
When running this code the first view is properly displayed, but the second one is in the top left corner (as if there are no constraints on it).
What exactly am I missing here?
You'll need to add at least one horizontal & one vertical constraint to view adding in ConstraintLayout,
Here, it's not satisfied in init constructor:
set.connect(ids[0], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 200) // no vertical constraint (Top/Bottom), so view will add at 0 position of y axis
set.connect(ids[1], ConstraintSet.TOP, ids[0], ConstraintSet.BOTTOM) // No horizontal constraint so that view 2 will jump to position (0,0)
Do like this:
// Add constraints for View 1
set.connect(ids[0], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 200)
set.connect(ids[0], ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, 20) // Add top constraint to your view 1
set.connect(ids[0], ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 200) // Optional if you want symmetry of view 1 in layout
// Add constraints for view 2
set.connect(ids[1], ConstraintSet.TOP, ids[0], ConstraintSet.BOTTOM)
set.connect(ids[1], ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 200) // Add start constraint to view 2
set.connect(ids[1], ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 200) // Optional if you want symmetry of view 2 in layout
Read it from ConstraintLayout & ConstraintSet.

Categories

Resources