I would like to set the height and width of an video view to match_parent. My code looks something like the following. It works without the height and width attributes, but doing it as below give me a val cannot be reassigned error.
class VideoActivityUI : AnkoComponent<VideoActivity> {
companion object {
val ID_VIDEO = 11
}
override fun createView(ui: AnkoContext<VideoActivity>) = with(ui) {
videoView{
id = ID_VIDEO
height = matchParent
width = matchParent
}
}
}
You have to use lparams to set layout parameters, like this (you can omit the explicit parameter names if you want to):
videoView {
id = ID_VIDEO
}.lparams (width = matchParent, height = matchParent)
Alternatively, you can do it like this:
videoView {
id = ID_VIDEO
}.lparams {
height = matchParent
width = matchParent
}
The related wiki section for Anko can be found here.
Note that you have to have a ViewGroup around your VideoView as the root of the Activity's layout for it to have layout parameters available, because it gets different ones depending on whether it's in a FrameLayout, LinearLayout, or RelativeLayout.
For example, with a simple frameLayout, your code would look like this:
override fun createView(ui: AnkoContext<VideoActivity>) = with(ui) {
frameLayout {
videoView {
id = ID_VIDEO
}.lparams(matchParent, matchParent)
}
}
Related
I have a GridLayout which should show 25 Buttons spaced evenly. To be able to set an onClickListener without calling each one them I want to do that programmatically.
I made a layout resource file with the grid itself to bind it and being able to inflate it
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:grid="http://schemas.android.com/apk/res-auto"
android:id="#+id/bingo_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:columnCount="5"
android:rowCount="5"
tools:context=".BingoActivity" />
Now I'm creating the fields:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bingoField = (1).rangeTo(25).toSet().toIntArray()
binding = BingoActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.bingoGrid.alignmentMode = GridLayout.ALIGN_BOUNDS
val bingoFieldGrid = binding.bingoGrid
bingoFieldGrid.alignmentMode = GridLayout.ALIGN_BOUNDS
bingoField.forEach {
val button = createButton(it.toString())
val gridLayoutParams = GridLayout.LayoutParams().apply {
rowSpec = spec(GridLayout.UNDEFINED, GridLayout.CENTER, 1f)
columnSpec = spec(GridLayout.UNDEFINED, GridLayout.CENTER, 1f)
height = GridLayout.LayoutParams.WRAP_CONTENT
width = GridLayout.LayoutParams.WRAP_CONTENT
}
bingoFieldGrid.addView(button, gridLayoutParams)
}
#RequiresApi(Build.VERSION_CODES.M)
private fun createButton(buttonText: String): Button {
var isCompleted = false
return Button(baseContext).apply {
setBackgroundColor(getColor(R.color.red))
gravity = Gravity.CENTER
text = buttonText
setOnClickListener {
isCompleted = if (!isCompleted) {
setBackgroundColor(getColor(R.color.green))
true
} else {
setBackgroundColor(getColor(R.color.red))
false
}
}
}
}
So, the fields are auto generated without problems, but the spacing is not right:
I'm quite new to the old layouting, is there a way to easily achieve that?
You're creating two different types of LayoutParams which doesn't make sense. LinearLayout shouldn't be involved at all.
The way they work is each child should get a set of LayoutParams that match the type of LayoutParams that its parent ViewGroup uses. So in this case the parent is GridLayout, so each child should be added using an instance of GridLayout.LayoutParams.
The way GridLayout.LayoutParams work is you define a row Spec and a column Spec that describe how a child should take up cells. We want them to take the single next cell, so we can leave the first parameter as UNDEFINED. We need to give them an equal weight more than 0 so they all share evenly in the leftover space. I'm using 1f for the weight.
I'm using FILL with a size of 0 for the buttons so they fill their cells. The margins put some gap between them.
I'm setting height and width to 0 to prevent them from being oversized. If the rows or columns become too big to fit the screen, the layout goes way too big.
You might want to use MaterialButton instead of a plain Button, so you can easily tint the background color without simply making it a static solid color rectangle.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = BingoBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.bingoGrid.alignmentMode = GridLayout.ALIGN_BOUNDS
for (num in 1..25) {
val button = MaterialButton(this).apply {
setBackgroundColor(resources.getColor(R.color.blue_500))
gravity = Gravity.CENTER
text = num.toString()
setPadding(0)
}
val params = GridLayout.LayoutParams().apply {
rowSpec = spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f)
columnSpec = spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f)
width = 0
height = 0
setMargins((4 * resources.displayMetrics.density).toInt())
}
binding.bingoGrid.addView(button, params)
}
}
AndroidStudio was finnicky about importing the spec function. I had to manually add this at the top:
import android.widget.GridLayout.Spec.*
You could consider Google ConstraintLayout Flows:
To set the number of elements use app:flow_maxElementsWrap="5"
layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.helper.widget.Flow
android:id="#+id/flow"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:flow_horizontalGap="8dp"
app:flow_maxElementsWrap="5"
app:flow_verticalGap="8dp"
app:flow_verticalStyle="packed"
app:flow_wrapMode="chain" />
</androidx.constraintlayout.widget.ConstraintLayout>
Then add the buttons programmatically to the ConstraintLayout:
val root = findViewById<ViewGroup>(R.id.root)
val size = 25
val array = IntArray(size)
for (i in 0 until size) {
array[i] = i + 1
val button = Button(this).apply {
layoutParams = ViewGroup.LayoutParams(0, 0)
id = i + 1
text = (i + 1).toString()
}
root.addView(button)
}
val flow = findViewById<Flow>(R.id.flow)
flow.referencedIds = array
Hint: you could use WRAP_CONTENT for the button height to avoid stretching out the buttons height.
I want to resize the width of My button from Kotlin code.
I tried this
private var viewPagerPageChangeListener: ViewPager.OnPageChangeListener = object : ViewPager.OnPageChangeListener {
override fun onPageSelected(position: Int) {
addBottomDots(position)
if (position == layouts!!.size - 1) {
btnNext!!.text = getString(R.string.start)
btnNext!!.layoutParams = RelativeLayout.LayoutParams(315, 44)
btnSkip!!.visibility = View.GONE
} else {
btnNext!!.text = getString(R.string.next)
btnSkip!!.visibility = View.VISIBLE
}
}
I am using this in the View Pager.
After running my application, button is disappeared from the screen.
You can use View's scaleX and scaleY if you just want to simply stretch the view. Getting the actual width and height is a bit complicated as you have to take into account listening to a ViewTreeObserver
It's awesome to be learning ConstraintLayout and Kotlin. I have a constraintLayout popupView, which is the parent view of a TextView titleLabel. I'd like popupView height to adjust to the content of its child titleLabel TextView. titleLabel height can vary due to different text strings used.
Both titleLabel and popupView are set to WRAP_CONTENT for the height layout params, but the popupView isn't getting rendered. Adding a fixed height constraint to popupView's constraintSet will render it, but a fixed height will not work when titleLabel height changes.
Any ideas how to get it working? Here's what I have
open class PopupActivity(): AppCompatActivity() {
public var message:String = "This is a message string for the label"
val titleLabel: TextView by lazy {
val label = TextView(this)
label.gravity = Gravity.CENTER
label.setTextSize(Constants.FontSizePopupTitle)
return#lazy label
}
val popupView: ConstraintLayout by lazy {
val view = ConstraintLayout(this)
view.setBackgroundColor(Color.primary())
return#lazy view
}
val view: ConstraintLayout by lazy {
val v = ConstraintLayout(this)
return#lazy v
}
#SuppressLint("ResourceType")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
view.id = 1
popupView.id = 2
titleLabel.id = 5
var margin = 2 * Constants.SpacingStandard.toInt()
view.addView(popupView)
popupView.addView(titleLabel)
titleLabel.layoutParams = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,
ConstraintLayout.LayoutParams.WRAP_CONTENT)
titleLabel.text = message
popupView.layoutParams = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,
ConstraintLayout.LayoutParams.WRAP_CONTENT)
val popupConstraintSet = ConstraintSet()
popupConstraintSet.connect(popupView.id, START, view.id, START, margin)
popupConstraintSet.connect(popupView.id, END, view.id, END, margin)
popupConstraintSet.centerHorizontally(popupView.id, view.id)
popupConstraintSet.centerVertically(popupView.id, view.id)
view.setConstraintSet(popupConstraintSet)
setContentView(view)
}
}
You can fix this by switching the order of the view#id in the connect functions
popupConstraintSet.connect(view.id, START, popupView.id, START, margin)
popupConstraintSet.connect(view.id, END, popupView.id, END, margin)
popupConstraintSet.centerHorizontally(view.id, popupView.id)
popupConstraintSet.centerVertically(view.id, popupView.id)
OR set the ConstraintSet to the popUpView. The field already has the correct name ^^
popUpView.setConstraintSet(popupConstraintSet)
Here's what I ended up doing:
remove popupView.layoutParams; WRAP_CONTENT for height seems to be ignored...
add constrainHeight with WRAP_CONTENT to constraintSet
onCreate is now this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
view.id = 1
popupView.id = 2
titleLabel.id = 5
popupView.addView(titleLabel)
view.addView(popupView)
titleLabel.layoutParams = ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_CONSTRAINT,
ConstraintLayout.LayoutParams.WRAP_CONTENT)
titleLabel.text = message
var margin = 2 * Constants.SpacingStandard.toInt()
val popupConstraintSet = ConstraintSet()
popupConstraintSet.connect(popupView.id, START, view.id, START, margin)
popupConstraintSet.connect(popupView.id, END, view.id, END, margin)
popupConstraintSet.constrainHeight(popupView.id, ConstraintSet.WRAP_CONTENT)
popupConstraintSet.centerHorizontally(popupView.id, view.id)
popupConstraintSet.centerVertically(popupView.id, view.id)
view.setConstraintSet(popupConstraintSet)
setContentView(view)
}
}
How to set maximum expanded height in android support design bottom sheet?
The question is an extension to the above question, i want to set the max expanded height of the sheet but dynamically according to the screen size.
I have tried setting new layout params to the view implementing bottomsheet behaviour but it does nothing good.
Please use this and chill :)
const val BOTTOMSHEET_HEIGHT_TO_SCREEN_HEIGHT_RATIO = 0.80 //change according to your requirement
override onCreateDialog() in your bottomsheetFragment
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
?.apply {
val maxDesiredHeight =
(resources.displayMetrics.heightPixels * BOTTOMSHEET_HEIGHT_TO_SCREEN_HEIGHT_RATIO).toInt()
if (this.height > maxDesiredHeight) {
val bottomSheetLayoutParams = this.layoutParams
bottomSheetLayoutParams.height = maxDesiredHeight
this.layoutParams = bottomSheetLayoutParams
}
BottomSheetBehavior.from(this)?.apply {
this.state = BottomSheetBehavior.STATE_EXPANDED
this.skipCollapsed = true
}
}
}
return dialog
}
2021
I'm late but someone will need
Kotlin extenxion:
fun View.setupFullHeight(maxHeight: Double = 0.3) {
val displayMetrics = context?.resources?.displayMetrics
val height = displayMetrics?.heightPixels
val maximalHeight = (height?.times(maxHeight))?.toInt()
val layoutParams = this.layoutParams
maximalHeight?.let {
layoutParams.height = it
}
this.layoutParams = layoutParams
}
How to use:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : BottomSheetDialog(requireContext(), R.style.DialogRoundedCornerStyle) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dialog?.setOnShowListener {
val bottomSheetDialog = it as BottomSheetDialog
val parentLayout =
bottomSheetDialog.findViewById<View>(R.id.design_bottom_sheet)
parentLayout?.let { view ->
val behavior = BottomSheetBehavior.from(view)
view.setupFullHeight()
behavior.apply {
state = BottomSheetBehavior.STATE_EXPANDED
isDraggable = false
isCancelable = false
}
}
}
}
override fun onBackPressed() {
super.onBackPressed()
dialog?.dismiss()
}
}
}
The simplest solution is to set the maxHeight property of the bottom sheet like this.
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
bottomSheet.setMaxHeight((int) (displayMetrics.heightPixels * 0.65));
Finally found it,
This question troubled me a lot with no solution reported anywhere, and the answer lies in the behavior itself.
The minimum offset is the max value upto which the bottomsheet should move and we set the lower cap of the value to our desired height upto which we want the bottomsheet to move.
You can expose a function to set the value or do it direclty in our behavior.
To dynamically set the max expanded height for bottomsheet we need to increase the minimum offset value from 0 to our desired value in BottomSheetBehavior class, let me show the code.
Happy coding!!
// The minimum offset value upto which your bottomsheet to move
private int mMinOffset;
/**
* Called when the parent CoordinatorLayout is about the layout the given child view.
*/
#Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
int dynamicHeight = Utils.dpToPx(parent.getContext(), **your_value_in_dp**);
mMinOffset = Math.max(dynamicHeight, mParentHeight - child.getHeight());
mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset);
mAnchorOffset = Math.min(mParentHeight - mAnchorHeight, mMaxOffset);
if (mState == STATE_EXPANDED) {
ViewCompat.offsetTopAndBottom(child, mMinOffset);
anchorViews(mMinOffset);
}
}
I use Anko DSL layout in my kotlin activity. I can not set bottom margin to FAB. Right margin work.
In my activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(application as SamfantozziApp).dgaeacomponent().inject(this)
InvoiceListKtActivityUI(_rxBus).setContentView(this)
}
My Anko DSL layout InvoiceListKtActivityUI.kt
class InvoiceListKtActivityUI (val _rxBus: RxBus): AnkoComponent<InvoiceListKtActivity>{
override fun createView(ui: AnkoContext<InvoiceListKtActivity>): View = with(ui){
return relativeLayout{
padding = dip(5)
lparams {
width = matchParent
height = wrapContent
margin = 5
}
verticalLayout{
tabLayout{
lparams {
width = matchParent
height = wrapContent
}
id = R.id.tabs
}
viewPager{
lparams {
width = matchParent
height = matchParent
}
id = R.id.container
}
}
floatingActionButton{
lparams {
width = wrapContent
height = wrapContent
rightMargin = 40 //works
bottomMargin = 40 //does not work
alignParentBottom()
alignParentRight()
}
imageResource = android.R.drawable.ic_input_add
id = R.id.fabinvoice
onClick{
_rxBus.send(InvoiceListFragment.ClickFobEvent())
}
}
}
}
}
I think you are not setting the lparams correctly. The proper form is:
floatingActionButton {
imageResource = android.R.drawable.ic_input_add
}.lparams{
rightMargin = dip(16)
bottomMargin = dip(16)
alignParentBottom()
alignParentRight()
}