Android place view (TreeView) on top of MainLayout - android

So I have this MainLayout which simply consists of a Floating Action Button (FAB) right now:
<RelativeLayout 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:id="#+id/lel"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="#+id/floatingActionButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:src="#drawable/ic_add_black_24dp" />
</RelativeLayout>
I am using this library to create a treeView similar to this
I create the AndroidTreeView object in the MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val treeView = AndroidTreeView(this, root) // 'this' is the context
// ...
setContentView(treeView.view)
}
This will obviously only display the content of the treeView and not the mainLayout.
How can I show the TreeView and the Button at the same time / render the TreeView on top of the MainLayout?
I tried to add the FAB to the view using addView:
val view = treeView.view as ViewGroup
view.addView(floatingActionButton2)
But this results in a java.lang.IllegalStateException: ScrollView can host only one direct child
In an example project the author of TreeView used Fragments for this. But is this really necessary in this case?

I solved this problem by inflating the MainLayout on top of the rootLayout. I tried a similar solution to this before but I probably used "R.id.content" instead of "android.R.id.content".
This code belongs to the bottom of the onCreate method of the MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
// ...
setContentView(treeView.view)
val rootLayout = findViewById<FrameLayout>(android.R.id.content)
View.inflate(this, R.layout.main_layout, rootLayout)
}

Related

How to use View Binding with Linear Layout?

I have a activity_main.xml file which looks like this.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
tools:context=".MainActivity"
android:id="#+id/main_linear_layout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:layout_gravity="center_horizontal"
android:textSize="30sp"
android:id="#+id/dice_text"/>
<Button
android:id="#+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="#string/roll"/>
</LinearLayout>
So when i try to create a view binding and use it in Activity file,
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val root = binding.root
setContentView(root.main_linear_layout)
buttonClicked()
}
fun buttonClicked(): Unit {
val roll_button = binding.mainLinearLayout.roll_button
val text = binding.mainLinearLayout.dice_text
roll_button.setOnClickListener {
var randomNumber = Random().nextInt(6)+1
text.text = randomNumber.toString()
}
}
}
i have some issues. Naturally since the Linear Layout has layout_gravity set to center, it should be centered in the screen, both vertically and horizontally. This is the case in the design editor of the xml which is how it is supposed to be.
But when i run the app, in my phone both the text and button, aligns center_horizontal but not vertically.
I tried giving center_vertical also but doesn't work.
But if i don't use the view binding and use findViewById , it works fine.
Edit: I have removed the mainLinearLayout from the binding. So its just
setContentView(root)
I doesn't work.This is how i want it to be and this is how it is present in the Design View of the xml ( I have removed text and added Image)
But, when i run it, i get this. enter image description here
P.S I have just started learning android development
Adding
android:gravity = "center"
worked!!
Try this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val root = binding.root
setContentView(root)
buttonClicked()
}
Source: https://developer.android.com/topic/libraries/view-binding#activities

View Binding - How do I get a binding for included layouts?

While working with view binding, I came across a couple of undocumented cases.
First: How do I get binding for included view layout parts? The main binding only sees items defined in the main layout.
Second: How do I get binding for merged layout parts. Again, the main binding only sees items in the main layout?
In case of:
Include with generic layout (not merge node), we need to assign ID to included part, this way in binding we will have access to included sub part
<include
android:id="#+id/your_id"
layout="#layout/some_layout" />
This way in your activity code:
private lateinit var exampleBinding: ActivityExampleBinding //activity_example.xml layout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
setContentView(exampleBinding.root)
//we will be able to access included layouts view like this
val includedView: View = exampleBinding.yourId.idOfIncludedView
//[...]
}
Include with merge block in external layout. We can't add ID to it because merge block is not a view.
Let's say we have such eternal merge layout (merge_layout.xm):
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
tools:showIn="#layout/activity_example">
<TextView
android:id="#+id/some_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
</merge>
To properly bind such merge layout we need to:
In your activity code:
private lateinit var exampleBinding: ActivityExampleBinding //activity_example.xml layout
private lateinit var mergeBinding: MergeLayoutBinding //merge_layout.xml layout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
//we need to bind the root layout with our binder for external layout
mergeBinding = MergeLayoutBinding.bind(exampleBinding.root)
setContentView(exampleBinding.root)
//we will be able to access included in merge layout views like this
val mergedView: View = mergeBinding.someView
//[...]
}
Regarding your first question, you can get a view binding for the included layout.
Here is a sample main_fragment.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
</LinearLayout>
And MainFragment.java can be like this:
public class MainFragment extends Fragment {
private MainFragmentBinding binding;
private ToolbarBinding toolbarBinding;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = MainFragmentBinding.inflate(inflater, container, false);
toolbarBinding = binding.toolbar;
return binding.getRoot();
}
#Override
public void onDestroy() {
super.onDestroy();
toolbarBinding = null;
binding = null;
}
}
Now you have two bindings: One corresponds to the main layout and the other corresponds to the included layout.
If you want to bind included layout then,
For Activity
YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(getLayoutInflater);
View view = mainLayoutBinding.getRoot();
YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);
For Fragment
YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(inflater,container,false);
View view = mainLayoutBinding.getRoot();
YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);
Make Sure if your main layout binding parent root is LinearLayout then,
includedLayoutBinding parent layout also be a linear layout
Suppose I included a layout in my activity_main.xml file as follows:
<include
android:id="#+id/ll_layout1"
layout="#layout/layout1"
android:visibility="gone" />
And suppose I wanted to change its visibility. I can do so as follows:
activityMainBinding.llLayout1.root.visibility = View.VISIBLE
In my case, I forgot to assign id to the include tag
Now, when you've assigned the id, you'll be able to get the binding object as,
YourMainLayoutBinding.YourIncludeTagIDLayoutBinding
Follow steps:-
private val binding : FragmentBinding
by viewBinding(FragmentBinding::bind)
make sure to do the following in "onViewCreated(view: View, savedInstanceState: Bundle?)"
val binding2 = binding.root.include_layout_id
e.g. val binding2 = binding.root.tool_bar_layout
Now access your include layout, views here. e.g.
binding2.textView.text = "your text"
Using the data binding library. Then wrap your XML layout with <layout> tag
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
...
</LinearLayout>
</layout>
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/ivImage"
... />
<TextView
android:id="#+id/tvTitle"
... />
</LinearLayout>
MainActivity.kt
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// Access include layout views
binding.toolbar.rootView.ivImage.setImageResource(R.drawable.ic_back_arrow)
binding.toolbar.rootView.tvTitle.text = getString(R.string.home)
...
}
In the include layout you must create a Container layout and put here the id.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Can't hide <include> item programmaticaly

Android Studio 3.6
in app/build.gradle:
android {
viewBinding.enabled = true
In my activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = QrBluetoothSwipeActivityBinding.inflate(layoutInflater)
}
in my qr_bluetooth_swipe_activity.xml
<include
android:id="#+id/blueToothBottonContainer"
layout="#layout/bluetooth_bottom_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
I want to hide blueToothBottonContainer programmaticaly.
So I try this:
private fun setBottomContainreViewMode(isQrScreenVisible: Boolean) {
if (isQrScreenVisible) {
binding.qrBottonContainer.
}
}
but not found method smt like setVisible
why?
setVisibility method belongs to a View class. <include> tag is not really a layout(View + ViewGroups). That's why you won't find setVisibility method on it.
To hide the included layout, you can give id to parent layout in your bluetooth_bottom_container layout and then change it's visiblity from binding.blueToothBottonContainer.{parent_layout_id}.

Why my foreach breaks when I try to draw dynamic buttons in Kotlin?

I need to draw dynamic buttons inside a foreach loop that retrieve data from my anko sqlite, the foreach only enter once and breaks and only draw one button in my layout, what I doing wrong? my code is this:
fun loadZones (ctx: Context, update: String, view: View, layout: LinearLayout) {
val zonesParser = rowParser{idzone: Int, zone: String -> Pair(idzone, zone)}
for (it in ctx.database.use {
select("tableplan")
.distinct()
.column("idzone")
.column("zone")
.orderBy("zone")
.parseList(zonesParser)
}) {
val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
val btnZone = layoutInflater.inflate(R.layout.zones_item, null) as MaterialButton
btnZone.text = it.second
btnZone.id = it.first
layout.addView(btnZone, layoutParams)
Log.e("PAIR", "FIN DEL CICLO")
continue
}
}
The data that retrieves from my query is this:
(2, LARRY)
(1, MADISON)
That's my activity, I need to draw the buttons in "lytZonesButtons" id
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".TablePlanFragment">
<com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:elevation="2dp"
tools:targetApi="lollipop" app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbarTablePlan"
style="#style/com.madison.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="#string/table_title_module">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_marginTop="56dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:orientation="horizontal"
android:background="#color/orangeLighter"
android:gravity="center_vertical"
android:padding="5dp" android:id="#+id/lytZonesButtons" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="112dp"
android:padding="5dp">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rc_tableplan"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.core.widget.NestedScrollView>
</FrameLayout>
and that's my button template that I called "zones_item":
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.button.MaterialButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" style="#style/com.madison.AppButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.MaterialComponents.Subtitle2"
tools:text="MADISON"
tools:targetApi="lollipop"
android:layout_margin="5dp"
/>
EDIT: I found the solution!
I don't now why my layout instance in the twice iteration of my loop throws NullPointerException but not shows in the log cat, my solution was put the loop code in onCreateView function, this is the code:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.activity_tableplan, container, false)
val iActivity = (activity as AppCompatActivity)
iActivity.setSupportActionBar(view.toolbarTablePlan)
iActivity.supportActionBar?.setDisplayShowTitleEnabled(true)
// view.rc_tableplan.setHasFixedSize(true)
// val gridLayoutManager = GridLayoutManager(context, 2, GridLayoutManager.HORIZONTAL, false)
// view.rc_tableplan.layoutManager = gridLayoutManager
val response = loadTablePlan(this.context!!, "no")
if (response.trim().toUpperCase() == "SUCCESS") {
val zonesParser = rowParser{idzone: Int, zone: String -> Pair(idzone, zone)}
for (zone in this.context!!.database.use {
select("tableplan")
.distinct()
.column("idzone")
.column("zone")
.orderBy("zone")
.parseList(zonesParser)
}) {
val layout:LinearLayout = view.lytZonesButtons
layout.let {
val btnZone = layoutInflater.inflate(R.layout.zones_item, layout, false) as MaterialButton
btnZone.text = zone.second
btnZone.id = zone.first
btnZone.requestLayout()
layout.addView(btnZone)
Log.e("PAIR", "FIN DEL CICLO")
}
}
}
return view
}
Thanks a lot for all people that tried help me, some admin can close my question please.
The hint is only one button is showing. Your trying to inflate the same view twice in the same spot.
You need to add an empty linearlayout in your xml. And in your loop change the buttonz..
var btnZone = findViewById(R.layout.btnZone)
button.text = "Pair"
btnZone.addView(button, layoutParams)
That's not the exact code (and probably not even the right syntax) but it shows you how you need to modify your loop.
Basicly you were attempting to inflate the same instance of the same view. When really your not inflating any views this way your just adding views.
Note
If you have a linearlayout in your xml when you add another button view to it it will add it below it. If you set the layout orientation to horizontal the button view then gets added beside the other one.
here's a link to an example.
Sorry I would make sure my code matched your code and variables with proper syntax but I am at work.

BottomSheetDialogFragment is not showing up

I've followed this tutorial to implement BottomSheetDiaogFragment in my android application.
this is my bottom sheet layout (bottom_sheet.xml):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioGroup
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="#+id/rb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="#string/rb1" />
<RadioButton
android:id="#+id/rb2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:text="#string/rb2" />
</RadioGroup>
</android.support.constraint.ConstraintLayout>
BottomSheetDialogFragment class:
class BottomSheetTaskRepeat : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
}
activity:
private val bottomSheetTaskRepeat = BottomSheetTaskRepeat()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bottomSheetTaskRepeat.show(supportFragmentManager, "my_bottom_sheet")
}
The problem is that the bottom sheet is not showing up! Any help is appreciated.
This is a late Answer, I am writing for anyone who will face the same problem, This is what I found:
For some reasons non-constrained views heights do not work in BottomSheetDialogFragment. A view whose height is like wrap_content will not show.(But the shadow will be there), but when you specify its height like 80dp it works.
For this question go and change your RadioGroup height and specify it like to:
android:layout_height="200dp"
Hope that is helpful.
UPDATE:
Since the default container of BottomSheetDailogFragment is FrameLayout and is set to WRAP_CONTENT, You can override that on your Fragment onStart method like this (Kotlin):
override fun onStart() {
super.onStart()
val containerID = com.google.android.material.R.id.design_bottom_sheet
val bottomSheet: FrameLayout? = dialog?.findViewById(containerID)
bottomSheet?.let {
BottomSheetBehavior.from<FrameLayout?>(it).state =
BottomSheetBehavior.STATE_HALF_EXPANDED
bottomSheet.layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT
}
view?.post {
val params = (view?.parent as View).layoutParams as (CoordinatorLayout.LayoutParams)
val behavior = params.behavior
val bottomSheetBehavior = behavior as (BottomSheetBehavior)
bottomSheetBehavior.peekHeight = view?.measuredHeight ?: 0
(bottomSheet?.parent as? View)?.setBackgroundColor(Color.TRANSPARENT)
}
}
Try following:
Create and use the parameterless static method newInstance in your BottomSheetDialogFragment and call the show() method on it.
Try LinearLayoutCompat as a root layout instead of ConstraintLayout.
Try a colourful background to the root layout to get an idea.
Try match_parent height for the root layout.
Make sure that the dismiss() or cancel() is not being called on it immediately.
Check visibility.
If it contains a recyclerView, make sure that it has items, getItemCount is not returning 0, and that we are setting up the values properly!
Restart both: PC and Device if your changes are not reflecting because of Android Studio errors.
Instead of using supportFragmentManager you have to use childFragmentManager,if you are inflating from inner fragments.
private val bottomSheetTaskRepeat = BottomSheetTaskRepeat()
bottomSheetTaskRepeat.show(childFragmentManager, "my_bottom_sheet")

Categories

Resources