Here is my code for Fragment class.
class FragmentOne : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
// return inflater.inflate(R.layout.fragment_one, container, false)
val binding: FragmentOneBinding =
DataBindingUtil.inflate(inflater, R.layout.fragment_one, container, false)
return binding.root
}
fun onClicking(){
Toast.makeText(activity, "You clicked me.", Toast.LENGTH_SHORT).show()
}
}
And here is my code for Fragment XML.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".FragmentOne">
<data>
<variable
name="clickable"
type="com.example.fragmentpractise1.FragmentOne" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hola Gola"
android:layout_marginTop="40dp"
android:onClick="#{()-> clickable.onClicking()}"/>
</LinearLayout>
</layout>
Now what I am trying to understand, why android:onClick is not showing any toast result. On pressing the button nothing happens. I can show toast by setting onClickListener on button id in Fragment class but unable to show toast via onClick attribute in XML using databinding.
You're calling clickable.onClicking() in xml which is not set yet. When you instantiate a data binding object, you probably have to set its variables as well (like clickable in your example)
Set that variable after instantiation like this
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
// return inflater.inflate(R.layout.fragment_one, container, false)
val binding: FragmentOneBinding =
DataBindingUtil.inflate(inflater, R.layout.fragment_one, container, false)
binding.clickable = this // your fragment
return binding.root
}
Also using v instead of () inside onClick is a bit more rational because that's a lambda in Java syntax receiving one view argument. I suggest to change it to below for more readability
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hola Gola"
android:layout_marginTop="40dp"
android:onClick="#{ v -> clickable.onClicking()}"/>
Related
Here is the kotlin file of the fragment:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
val view = inflater.inflate(R.layout.fragment_blank, container, false)
view.textView1 <- Here is the problem
return view
}
Down here is xml file of the fragment. I hope it's all right...
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/purple_200"
tools:context=".BlankFragment">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="1"
android:gravity="center"
android:textSize="80sp" />
</FrameLayout>
textView = findViewById(R.id.yourId);
and next you can use it
Actually findViewById() is not available on the Fragment class.
However, you can use inflated view to access the findViewById() as shown on this example.
class MyFragment : Fragment() {
lateinit var textView: TextView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_my, container, false)
//use view to access findViewById() method
textView = view.findViewById(R.id.textView1)
textView.text = "Test 1"
return view
}
}
That said the recommended way would be to use Databinding or View Binding
I'm trying to use Data binding to run a function when an onClick event occurs, I'm hoping someone can tell me what I'm doing wrong here.
The log item in myClick doesn't run.
XML
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="myBinding"
type="com.example.deletebindingtest.MyFragment" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MyFragment"
android:orientation="vertical">
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button"
android:onClick="#{(view) -> myBinding.myClick()}"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
My Fragment
class MyFragment : Fragment() {
private lateinit var binding: FragmentMyBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_my,
container,
false
)
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
fun myClick() {
Log.i("TEST", "Its working")
}
}
When I click on the extension in the XML it takes me to myClick function.
a Quick fix is on onCreateView add binding.myBinding=this
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_my,
container,
false
)
binding.lifecycleOwner = viewLifecycleOwner
binding.myBinding=this // here
return binding.root
}
also I prefer listener binding you can check it here
First Fragment's code
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}
First Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainFragment">
<EditText
android:id="#+id/etSavedData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:hint="Enter the Text"/>
<Button
android:id="#+id/btnSaveData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dp"
android:text="Save"/>
</RelativeLayout>
Main Activity's code
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Set replace Main Activity content with the Fragment1 content
val mainFragment = MainFragment()
supportFragmentManager.beginTransaction().add(R.id.contain_fragment, mainFragment).commit()
val thedata = mainFragment.thisdata()
Log.e("Main Frag to Activity", thedata)
btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
supportFragmentManager.beginTransaction().add(R.id.contain_fragment, secondFragment).commit()
}
}
}
Main Activity XML
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contain_fragment"
tools:context=".MainActivity">
</RelativeLayout>
Second Fragment's code
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val secondInflater = inflater.inflate(R.layout.fragment_second, container, false)
return secondInflater
}
}
Second Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/holo_blue_dark"
tools:context=".SecondFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="#+id/tvDisplayText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Text displayed here." />
</FrameLayout>
Following Exception showing up in Logcat
Caused by: java.lang.NullPointerException: Attempt to invoke virtual
method 'void
android.widget.Button.setOnClickListener(android.view.View$OnClickListener)'
on a null object reference
at com.example.demo2.MainActivity.onCreate(MainActivity.kt:22)*
You either need to move your btnSaveData button into the MainActivity, or you should move the onClickListener into the MainFragment, along with some other changes. Something like:
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
activity.supportFragmentManager.beginTransaction().add(R.id.contain_fragment, secondFragment).commit()
}
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}
**Thanks Tash, that actually make sense, unfortunately it was still showing the same exception but I finally manage to resolve the issue by doing the following. **
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
mainInflater.btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
secondFragment?.let {
activity?.supportFragmentManager?.beginTransaction()?.replace(R.id.contain_fragment, secondFragment)?.commit()
}
}
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}
I have an abstract fragment that has it's own view (views the are common for inherited fragments)
I'm creating a fragment that inherit from that base fragment and it has it's own view (layout)
How can it be done?
I'm getting an error that says that I need to call remove view first...
The inherited Fragment's view should go into the: (Found in the parent's xml)
<FrameLayout
android:id="#+id/gamePadContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toTopOf="#+id/gamePadHistoryScroller"
app:layout_constraintTop_toTopOf="parent" />
Base Fragment (abstract):
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_gamepad, container, false)
val gamePadView = inflater.inflate(onFragmentLayoutRequest(), null)
gamePadView.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
joystickLeftCenter[0] = 0
joystickLeftCenter[1] = 0
joystickRightCenter[0] = 0
joystickRightCenter[1] = 0
gamePadButtonsMap[KeyEvent.KEYCODE_BUTTON_THUMBL]?.let {
joystickLeftCenter[0] = it.buttonView.left
joystickLeftCenter[1] = it.buttonView.top
}
gamePadButtonsMap[KeyEvent.KEYCODE_BUTTON_THUMBR]?.let {
joystickRightCenter[0] = it.buttonView.left
joystickRightCenter[1] = it.buttonView.top
}
gamePadView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
gamePadButtonsMap = onGamePadButtonMapRequest(gamePadView)
return rootView
}
Base fragment XML:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/gamePadContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toTopOf="#+id/gamePadHistoryScroller"
app:layout_constraintTop_toTopOf="parent" />
<!-- Buttons History -->
<HorizontalScrollView
android:id="#+id/gamePadHistoryScroller"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="28dp"
android:layout_marginBottom="24dp"
android:fillViewport="true"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.265"
tools:layout_editor_absoluteX="0dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/gamePadHistoryContainer"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginBottom="52dp"
android:background="#color/bg_buttons_history"
android:gravity="left"
android:orientation="horizontal" />
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
Inherit fragment:
class PS4Fragment : GamePadFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_ps4, container, false)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_gamepad, container, false)
val gamePadView = LayoutInflater.from(requireContext()).inflate(onFragmentLayoutRequest(), rootView.gamePadContainer, true)
gamePadButtonsMap = onGamePadButtonMapRequest(gamePadView)
rootView.gamePadContainer.addView(gamePadView)
return rootView
}
I've been using the same inflater.. now it's working as expected.
In the child fragment (Inherit fragment) there is no "onCreateView"
Now,
I have a base fragment with some views and component that all fragments have, and a unique view to each fragment :)
Thanks!
I am using a fragment within an activity and using data binding to bind its layout for both the activity and fragment as shown below:
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#id/FAB_fromHomeActivity_BottomAppBarAttached"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_add_dark"
android:backgroundTint="#color/colorAccent"
app:layout_anchor="#id/BottomAppBar_fromHomeActivity_Main"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity)
}
fragment.kt
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_goals, container, false)
return mBinding.root
}
Is there any way to access the FAB in the activity using the binding in the fragment ? (For example something like that mBinding.parent.FAB)
I can't find any information on this. Can anyone help ?