Fragment does not respond to onClick event on custom views - android

I have MainFragment, and inside my fragment I added my CustomButton custom view. And, onViewCreated, I setOnClickListener to the view. But, the fragmenet does not respond to the click.
ButtonWithImage
class ButtonWithImage(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
init {
val view = View.inflate(context, R.layout.item_custom_btn, this)
val attributes = context.obtainStyledAttributes(attrs, R.styleable.BadgeIcon)
view.btn_image.setImageDrawable(attributes.getDrawable(R.styleable.BadgeIcon_image))
view.btn_text.setText(attributes.getResourceId(R.styleable.BadgeIcon_text, 0))
attributes.recycle()
}
}
fragment_main.xml
...
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<corn.transaction.custom_view.ButtonWithImage
android:id="#+id/my_cards"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
app:image="#drawable/ic_my_cards"
app:text="#string/btn_text_my_cards"/>
<corn.transaction.custom_view.ButtonWithImage
android:id="#+id/transactions"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
app:image="#drawable/ic_transactions"
app:text="#string/btn_text_transactions"/>
</LinearLayout>
...
MainFragment.kt
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_main, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
my_cards.setOnClickListener { logMessage("loooooog") }
}

I think you can fix it by:
Adding focusable=false and clickable=false to your btn_image and btn_text
Adding focusable=true and clickable=true to your root view of this custom layout (also give it id -> lets say custom_button_container)
call my_cards.findViewById<LinearLayout>(R.id.custom_button_container).setOnClickListener { logMessage("loooooog") }

The optimal solution is making all views in XML clickable and focusable false.
You can use the following code:
my_cards.setOnClickListener { logMessage("loooooog") }
As all views are not clickable and focusable, they do ignore clicks, but not the custom view itself.

Related

How can I access a TextView declared in XML within my Fragment?

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

creating bottom sheet dialog with ConstraintLayout

I am trying to create modal bottom sheet dialog to show a list of items which will be scrollable.
dialog_bottom_example.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="#+id/container"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Items in LinearLayout is getting added dynamically based on list size as below:
ExampleDialogBottom:
class ExampleDialogBottom: BottomSheetDialogFragment() {
private lateinit var binding: DialogExampleBinding
private var skusItems = mutableListOf<SkuItem>();
companion object {
internal const val TAG = "ExampleDialogBottom"
#JvmStatic
fun newInstance(nonEligibleErxItem: NonEligibleErxItem) = NonEligibleBottomDialog
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DialogExampleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
configureSkuItems()
}
private fun configureSkuItems() {
if (!skuItems.isNullOrEmpty()) {
val inflater = LayoutInflater.from(context)
for (sku in skuItems) {
val skuItemView = SingleSkuItemViewBinding.inflate(inflater, null, false)
skuItemView.skuImage.setImageWithVisibility(sku.imageUrl)
skuItemView.skuTitle.setTextWithVisibility(sku.name)
binding.container.addView(skuItemView.root)
}
binding.container.visibility = View.VISIBLE
} else {
binding.container.visibility = View.GONE
}
}
ExmpaleActivity:
button.setOnClickListener {
ExampleDialogBottom.newInstance().show(getSupportFragmentManager(),
ExampleDialogBottom.TAG);
}
However, when I click on button, a transparent dialog shows up, no content is showing.
When I removed match-contraint and changed it match-parent or wrap-content, it's working as expected.
It would be great if someone could help? Thanks in advance.

android:onClick attribute is not working through data binding

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()}"/>

Kotlin doOnLayout not setting visibility.GONE

I have a custom ConstraintLayout Myword. when I set the visibility of myword1 to GONE inside kotlin fun doOnLayout, it becomes invisible but myword2 doesn't move down.
But if I set its visibility to GONE in XML or outside doOnLayout, it works as intended.
<learnprogramming.academy.relaf.Myword
android:id="#+id/myword1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<learnprogramming.academy.relaf.Myword
android:id="#+id/myword2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="#id/myword1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Source code:
lateinit var bannerword : MutableList<Myword>
lateinit var swippon : ConstraintLayout
fun updatewords{
swippon.doOnLayout {
if (swippon.height>1000) bannerword[0].visibility = View.GONE}
class PhrasesFragment: Fragment() {
companion object {
fun newInstance(): PhrasesFragment {
return PhrasesFragment()
}
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.inglesa_screen, container, false) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bannerword=mutableListOf(myword1,myword2)
swippon=parent
updatewords()
}
}
doOnLayout function when layout is laid out calculates parent Layout height and eventually removes myword1. I actually solved changing doOnLayout with Handler(Looper.getMainLooper()).post{} but is wrong with doOnLayout? How should i have used it in the correct way?

android BaseFragment with it's own xml and inherited fragment's view inside a parent's frameLayout

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!

Categories

Resources