I've followed some previous questions and I've even solved this issue in some other projects, but for some reason I can't solve it here.
I have an app which creates "tasks" and creates a countdown for each one.
I have a view model with a list, and its observable via LiveData
val tasksList = mutableListOf<Task>()
private val _tasksListData = MutableLiveData(tasksList)
val tasksListData : LiveData<MutableList<Task>>
get() = _tasksListData
fun addNewTask(task : Task){
tasksList.add(task)
_tasksListData.value = tasksList
}
I have already check that the items are created via a log statement. So that's working alright.
Then in the fragment I'm observing this live data and trying to add dynamically each tag, but for some reason these are not shown:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_history,
container,
false
)
taskListContainer = binding.tasksListContainer
tasksViewModel = ViewModelProvider(requireActivity()).get(TasksViewModel::class.java)
//Checks if the list has some items, otherwise displays a message
checkTasksList()
tasksViewModel.tasksListData.observe(viewLifecycleOwner,{
for (item in it) {
val view: IndividualTaskViewBinding = DataBindingUtil.inflate(
inflater, R.layout.individual_task_view, container, false
)
view.taskTitle.text = item.name
view.taskDateCreated.text = item.dateCreated
view.taskTertiaryText.text = item.cyclesCompleted.toString()
taskListContainer.addView(view.root)
}
checkTasksList()
})
setHasOptionsMenu(true)
return binding.root
}
private fun checkTasksList(){
if(taskListContainer.childCount == 0 ){
binding.emptyListText.setVisibility(View.VISIBLE)
} else{
binding.emptyListText.setVisibility(View.GONE)
}
}
}
Here's the layout for each individual task:
<?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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:minHeight="88dp">
<ImageView
android:id="#+id/task_item_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentStart="true"
android:paddingTop="8dp"
app:srcCompat="#drawable/tomato" />
<TextView
android:id="#+id/task_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_toEndOf="#id/task_item_icon"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:maxLines="1"
tools:text="Title"
android:textAppearance="?attr/textAppearanceSubtitle1"
/>
<TextView
android:id="#+id/task_date_created"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/task_title"
android:layout_gravity="center_vertical"
android:layout_toEndOf="#id/task_item_icon"
android:paddingEnd="16dp"
android:maxLines="1"
tools:text="Date created"
android:textAppearance="?attr/textAppearanceBody2"
/>
<TextView
android:id="#+id/task_tertiary_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/task_date_created"
android:layout_gravity="center_vertical"
android:layout_toEndOf="#id/task_item_icon"
android:paddingEnd="16dp"
android:maxLines="1"
tools:text="task_tertiary_text"
android:textAppearance="?attr/textAppearanceBody2"
/>
</RelativeLayout>
</layout>
And here's the layour of the fragment:
<?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"
tools:context=".FragmentHistory">
<!-- using constraint layout so I can have the views floating in the screen-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/empty_list_text"
style="#style/TextAppearance.MaterialComponents.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/list_empty_text"
android:textAlignment="center"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="#+id/scrollView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/scrollView" />
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
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_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0">
<LinearLayout
android:id="#+id/tasks_list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
As I mentioned before, the tasks are added to the list correclty, and the LiveData is also updated everytime a new task is added. But then the observer is not working, or the problem is when the layout is inflated.
Thank you very much for your help.
The entire project is here: https://github.com/arieldipietro/PomodoroTechnique
I just found that the problem is with the observer so I'm posting a new question Adding an Observer in a Tabbed Activity
Related
How to make Scrollview automatically scroll to the bottom when RadioButton is clicked
May I know How to make the Scroll view automatically scroll down to the next_button when any of the Radio Button is clicked ,
in the same time I need to call "setMeal() method from the ViewModel when the Radio Button is clicked.
<?xml version="1.0" encoding="utf-8"?>
<layout 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:context=".ui.MainMeal">
<data>
<variable
name="viewModel"
type="com.example.mymeal.model.OrderViewModel" />
<variable
name="mainMeal"
type="com.example.mymeal.ui.MainMeal" />
</data>
<ScrollView
android:layout_width="match_parent"
android:id="#+id/scrollView"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp">
<com.google.android.material.card.MaterialCardView
android:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:layout_constraintBottom_toTopOf="#+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioGroup
android:id="#+id/main_meal_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioButton
android:id="#+id/royal_lobster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='#{viewModel.menuItems["royal butter lobster"].name}'
android:onClick='#{() -> viewModel.setMeal("royal butter lobster")}'
android:textAppearance="?attr/textAppearanceBody1"
tools:text="Royal Butter Lobster" />
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/royal_lobster_image"
android:layout_width="match_parent"
android:layout_height="194dp"
android:layout_margin="8dp"
app:shapeAppearanceOverlay="#style/cut_corner"
app:srcCompat="#drawable/royal_butter_lobster" />
<TextView
android:id="#+id/royal_lobster_description"
style="#style/Widget.MenuItem.TextView.Info"
android:text='#{viewModel.menuItems["royal butter lobster"].description}'
tools:text="Entree 3 description" />
<TextView
android:id="#+id/royal_lobster_price"
style="#style/Widget.MenuItem.TextView.Info"
android:paddingTop="8dp"
android:text='#{viewModel.menuItems["royal butter
lobster"].getFormattedPrice()}'
tools:text="$0.00" />
<View
style="#style/Widget.MyMeal.Divider"
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/royal_lobster_description" />
<RadioButton
android:id="#+id/kebbeh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='#{viewModel.menuItems["kebbeh"].name}'
android:onClick='#{() -> viewModel.setMeal("kebbeh")}'
android:textAppearance="?attr/textAppearanceBody1"
tools:text="Kebbeh" />
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/kebbeh_image"
android:layout_width="match_parent"
android:layout_height="194dp"
android:layout_margin="8dp"
app:shapeAppearanceOverlay="#style/cut_corner"
app:srcCompat="#drawable/kebbeh" />
<TextView
android:id="#+id/kebbeh_description"
style="#style/Widget.MenuItem.TextView.Info"
android:text='#{viewModel.menuItems["kebbeh"].description}'
tools:text="Entree 4 description" />
<TextView
android:id="#+id/kebbeh_price"
style="#style/Widget.MenuItem.TextView.Info"
android:paddingTop="8dp"
android:text='#{viewModel.menuItems["kebbeh"].getFormattedPrice()}'
tools:text="$0.00" />
<View
style="#style/Widget.MyMeal.Divider"
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/kebbeh_description" />
<RadioButton
android:id="#+id/shish_tawook"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='#{viewModel.menuItems["shish tawook"].name}'
android:onClick='#{() -> viewModel.setMeal("shish tawook")}'
android:textAppearance="?attr/textAppearanceBody1"
tools:text="Shish Tawook" />
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/shish_tawook_image"
android:layout_width="match_parent"
android:layout_height="194dp"
android:layout_margin="8dp"
app:shapeAppearanceOverlay="#style/cut_corner"
app:srcCompat="#drawable/shish_tawook" />
<TextView
android:id="#+id/shish_tawook_description"
style="#style/Widget.MenuItem.TextView.Info"
android:text='#{viewModel.menuItems["shish tawook"].description}'
tools:text="Entree 4 description" />
<TextView
android:id="#+id/shish_tawook_price"
style="#style/Widget.MenuItem.TextView.Info"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text='#{viewModel.menuItems["shish tawook"].getFormattedPrice()}'
tools:text="$0.00" />
</RadioGroup>
</com.google.android.material.card.MaterialCardView>
<View
android:id="#+id/divider"
style="#style/Widget.MyMeal.Divider"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/cardView" />
<TextView
android:id="#+id/subtotal"
style="#style/Widget.MyMeal.TextView.Subtotal"
android:text='#{#string/subtotal(viewModel.subtotal)}'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/divider"
tools:text="Subtotal $5.00" />
<Button
android:id="#+id/next_button"
style="#style/Widget.Order.Button.Next"
android:onClick="#{()-> mainMeal.goToNextFragment()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/cancel_button"
app:layout_constraintTop_toBottomOf="#id/subtotal" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
This is the fragment class file
package com.example.mymeal.ui
class MainMeal : Fragment() {
// Binding object instance corresponding to the fragment_main_meal.xml layout.
private var _binding: FragmentMainMealBinding? = null
private val binding get() = _binding!!
//Get instance of OrderViewModel
private val sharedViewModel: OrderViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentMainMealBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = sharedViewModel
binding.mainMeal = this
}
//Navigate to the salad menu fragment
fun goToNextFragment() {
findNavController().navigate(R.id.action_mainMeal_to_saladMenu)
}
//Cancel the order
private fun cancelOrder() {
//Reset the values
sharedViewModel.resetOrder()
//Navigate back to the start fragment
findNavController().navigate(R.id.action_mainMeal_to_startFragment)
}
private fun doNothing() {
return
}
//confirmation before cancel the order
fun showDialog() {
MaterialAlertDialogBuilder(requireContext()).setMessage(getString(R.string.cancel_order))
.setCancelable(true)
.setNegativeButton(getString(R.string.yes)) { _, _ -> cancelOrder() }
.setPositiveButton(getString(R.string.no)) { _, _ -> doNothing() }.show()
}
/**
Clear out the binding object when the view hierarchy associated with the fragment
is being removed
*/
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Use the below code when the radio button is checked
scrollView.post(new Runnable() {
public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
}
});
Try this:
fun scrollToViewBottom(scrollView: ScrollView, childView: View) {
val delay: Long = 500 //delay to let finish with possible modifications to ScrollView
scrollView.postDelayed({ scrollView.smoothScrollTo(0, childView.bottom) }, delay)
}
Call the function:
radioButton.setOnClickListener {
if (radioButton.isChecked) {
println("checked")
scrollToViewBottom(ScrollViewExplore, Mypage)
}
}
This question already has answers here:
View Binding - How do I get a binding for included layouts?
(8 answers)
Closed 1 year ago.
I defined a layout in this way:
<?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:layout_width="match_parent"
android:layout_height="match_parent">
<eo.view.batterymeter.BatteryMeterView
android:id="#+id/batteryMeter"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
app:layout_constraintBottom_toTopOf="#id/colorInputView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include layout="#layout/include_battery_meters_with_indicator" />
<eo.view.colorinput.ColorInputView
android:id="#+id/colorInputView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/batteryMeter" />
</androidx.constraintlayout.widget.ConstraintLayout>
this's the layout file included:
<?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_color_edit">
<eo.view.batterymeter.BatteryMeterView
android:id="#+id/chargingBatteryMeter"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="#id/batteryMeter"
app:layout_constraintEnd_toStartOf="#id/criticalBatteryMeter"
app:layout_constraintStart_toStartOf="#id/batteryMeter"
app:layout_constraintTop_toTopOf="#id/batteryMeter" />
<eo.view.batterymeter.BatteryMeterView
android:id="#+id/criticalBatteryMeter"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="#id/chargingBatteryMeter"
app:layout_constraintEnd_toStartOf="#id/unknownBatteryMeter"
app:layout_constraintStart_toEndOf="#id/chargingBatteryMeter"
app:layout_constraintTop_toTopOf="#id/chargingBatteryMeter" />
<eo.view.batterymeter.BatteryMeterView
android:id="#+id/unknownBatteryMeter"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="24dp"
app:layout_constraintBottom_toBottomOf="#id/chargingBatteryMeter"
app:layout_constraintEnd_toEndOf="#id/batteryMeter"
app:layout_constraintStart_toEndOf="#id/criticalBatteryMeter"
app:layout_constraintTop_toTopOf="#id/chargingBatteryMeter" />
<androidx.constraintlayout.widget.Group
android:id="#+id/indicatorBatteryMeterGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="chargingBatteryMeter,criticalBatteryMeter,unknownBatteryMeter" />
</merge>
In the source code i deflate the layout:
private lateinit var binding: ActivityColorEditBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityColorEditBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
setupActionBar()
setupBatteryMeters()
setupColorInput()
}
For some reason items inside included file are not bound and thus references to such items are not working, for example in this method binding.indicatorBatteryMeterGroup is not recognized:
private fun setupBatteryMeters() {
when (colorType) {
ColorType.INDICATOR -> {
setupIndicatorBatteryMeters()
binding.batteryMeter.isInvisible = true
binding.indicatorBatteryMeterGroup.isVisible = true
}
else -> {
setupBattery(binding.batteryMeter)
binding.indicatorBatteryMeterGroup.isVisible = false
binding.batteryMeter.isVisible = true
}
}
}
Give your include element an ID:
<include layout="#layout/include_battery_meters_with_indicator"
android:id="#+id/battery_meters" />
Then you can access them as a property of a property of your binding, for instance, binding.batteryMeters.criticalBatteryMeter.
Don't put an ID on the merge element. IIRC, this causes it not to work properly. The ID has to be on the include.
So I'm doing a project and I'm completely lost. I have seen how to do data binding with TextViews but I am being asked to do it with EditText Views with Two Way Data Binding. I got up to here so far with it.
The XML File.
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="myShoe"
type="com.udacity.shoestore.product.Shoe" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary">
<TextView
android:id="#+id/title_detail_view"
style="#style/title_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:text="#string/add_shoe_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/shoe_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="#string/shoe_name_string"
android:inputType="text"
android:textSize="30sp"
android:text="#={myShoe.name}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_detail_view" />
<EditText
android:id="#+id/shoe_size"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/size_string"
android:inputType="number|numberDecimal"
android:textSize="15sp"
android:text="#={myShoe.size}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_name" />
<EditText
android:id="#+id/company_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/company_string"
android:inputType="text"
android:textSize="15sp"
android:text="#={myShoe.company}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_size" />
<EditText
android:id="#+id/shoe_description"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/description_string"
android:inputType="text"
android:textSize="15sp"
android:text="#={myShoe.description}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/company_name" />
<Button
android:id="#+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="#color/colorPrimaryDark"
android:text="#string/cancel_string"
android:textColor="#android:color/white"
app:layout_constraintBaseline_toBaselineOf="#+id/savee_button"
app:layout_constraintEnd_toStartOf="#+id/savee_button"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/savee_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="88dp"
android:backgroundTint="#color/colorPrimaryDark"
android:text="#string/save_string"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I was told to implement it to a fragment and it should work. But I'm not sure how. Here's the fragment
class ShoeDetailsFragment : Fragment() {
private val viewModel: ActivityViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentShoeDetailsBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_shoe_details,
container, false
)
//initializing the button and clearing the views once canceled
binding.cancelButton.setOnClickListener { v: View ->
v.findNavController().navigateUp()
binding.shoeName.text.clear()
binding.shoeSize.text.clear()
binding.companyName.text.clear()
binding.shoeDescription.text.clear()
}
//initializing the button and saving the info to transfer to the shoeList
binding.saveeButton.setOnClickListener { v: View ->
v.findNavController().navigateUp()
val name = shoe_name.text.toString()
val size = shoe_size.text.toString()
val brand = company_name.text.toString()
val details = shoe_description.text.toString()
viewModel.addShoe(name, size, brand, details)
}
return binding.root
}
}
I am open to any ideas to initialize binding properties so i can use it in both the layout and the fragment. Or am I looking at this the wrong way?
P.S. The XML File is being represented in this fragment
I also did this project for my NanoDegree.
Inside my ViewModel I created 3 variables for each EditText:
MutableLiveData - to update the value inside the viewModel
LiveData to expose value outside viewModel e.g. in a fragment (you won't really need this)
Public Variable to monitor value of MutableLiveData and expose this to your xml thus achieving the 2-Way Binding.
Then I and would create a Shared ViewModel to share data between ShoeDetailsFragment and ShoeListingFragment .
Inside the SharedViewModel
I created 3 variables for each EditText (this is just the first 2 Edittexts):
class MySharedViewModel : ViewModel() {
private val _name = MutableLiveData<String>()
val name: LiveData<String>
get() = _name
var edShoeName = ""
private val _size = MutableLiveData<Double>()
val size: LiveData<Double>
get() = _size
var edSize = ""
......}
For the xml I did exactly what you have done but used the 3rd variable for the 2-Way Data Binding:
<EditText
android:id="#+id/shoe_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="#string/shoe_name_string"
android:inputType="text"
android:textSize="30sp"
android:text="#={mySharedViewModel.edShoeName}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_detail_view" />
<EditText
android:id="#+id/shoe_size"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/size_string"
android:inputType="number|numberDecimal"
android:textSize="15sp"
android:text="#={mySharedViewModel.edCompany}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_name" />
I have seen you have included this line of code on your ShoeDetailFragment code:
binding.saveeButton.setOnClickListener { v: View -> ....}
In my case I did it inside SharedViewModel instead:
fun onSaveButtonClick() {
//check value entered for size and set it to 0 if blank
if (edSize == "") {
edSize = "0"
}
//update MutableLiveData with values read live from the EditText
_name.value = edShoeName
_size.value = edSize.toDouble()
//save shoeObject to the _shoeList MutableLiveData
_shoeList.value?.add(Shoe(edShoeName, edSize.toDouble(), edCompany, edDescription))
}
Using DataBinding I moved the onClick bit to xml:
<Button
android:id="#+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="75dp"
android:layout_marginBottom="75dp"
android:background="#drawable/custom_button_background"
android:onClick="#{()->sharedViewModel.onSaveButtonClick()}"
You can also refer to my project.
So I'm fairly new to Kotlin.
I am trying to create an onClickListener on an image button to open the share interface, so that the particular video from the recyclerView can be shared via SMS, etc.
I followed various tutorials on how to do this as I am trying to execute this within a fragment, and the app just keeps crashing when I try to open the fragment in question.
I get the following error
java.lang.ClassCastException: java.lang.Integer cannot be cast to android.widget.ImageButton
Here is what my fragment code looks like:
SearchFragment.kt
class SearchFragment : Fragment(), View.OnClickListener
{
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<ClipAdapter.ViewHolder>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View
{
val rootView = inflater.inflate(R.layout.fragment_search, container, false)
loadData()
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
val shareBtn = view.findViewById<ImageButton>(R.id.button_to_share)
shareBtn.setOnClickListener(this)
}
private fun loadData()
{
val service = TwitchServiceBuilder.buildService(TwitchService::class.java)
val requestCall = service.getClips("anerdfails")
requestCall.enqueue(object : Callback<List<Clip>>
{
override fun onResponse(
call: Call<List<Clip>>,
response: Response<List<Clip>>
)
{
if (response.isSuccessful)
{
//process data
recyclerView.layoutManager = GridLayoutManager(activity, 2)
recyclerView.adapter = ClipAdapter(response.body()!!)
} else
{
//output alert
AlertDialog.Builder(activity!!)
.setTitle("API error")
.setMessage("Response, but something went wrong ${response.message()}")
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setIcon(android.R.drawable.ic_dialog_alert)
.show()
}
}
override fun onFailure(call: Call<List<Clip>>, t: Throwable)
{
//process failure
AlertDialog.Builder(activity!!)
.setTitle("API error")
.setMessage("No response, and something went wrong $t")
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setIcon(android.R.drawable.ic_dialog_alert)
.show()
}
})
}
override fun onClick(v: View?)
{
Toast.makeText(activity, "Its toast!", Toast.LENGTH_SHORT).show()
}
}
And here are my 2 layouts for the RecyclerView:
fragment_search.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"
android:paddingStart="15dp"
android:paddingTop="?attr/actionBarSize"
android:paddingEnd="15dp"
tools:context=".ui.search.SearchFragment">
<androidx.appcompat.widget.SearchView
android:background="#drawable/search_bar"
android:id="#+id/clipSearch"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="5dp"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_constraintBottom_toTopOf="#+id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="75dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/clipSearch" />
</androidx.constraintlayout.widget.ConstraintLayout>
clip_layout.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="wrap_content"
tools:context=".ui.search.SearchFragment">
<VideoView
android:id="#+id/videoClip"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
app:layout_constraintDimensionRatio="w,2:3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/txtTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/videoClip"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtChannel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtTitle"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtGame"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtChannel"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtViews"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtGame"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal"
android:weightSum="2"
android:layout_marginTop="15dp"
app:layout_constraintTop_toBottomOf="#+id/txtViews">
<ImageButton
android:id="#+id/favouriteButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#null"
android:scaleType="fitCenter"
android:src="#drawable/ic_baseline_favorite_border_24" />
<ImageButton
android:id="#+id/button_to_share"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#null"
android:scaleType="fitCenter"
android:src="#drawable/ic_baseline_share_24" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Seems like a simple mistake on my part, but I'm pulling my hair out trying to work out what I've done wrong to cause the error on loading.
Any help would be appreciated.
I see you're trying to find the button "ShareBtn" inside the fragment which is totally wrong.
the "ShareBtn" doesn't belong to the fragment, it belongs to the viewHolder which you have created inside "ClipAdapter"
What you need to do is creating an interface inside "ClipAdapter" and create an object from it inside the Adapter
then call the method which is should the clickListener delegates the click to it
lastly, you should implement it inside the fragment and put whatever logic you want
This link will help you implement it
You are casting ID of view which is an Integer to ImageButton which is a View in this line
val shareBtn = R.id.button_to_share as ImageButton
You should use this instead
val shareBtn = findViewById<ImageButton>(R.id.button_to_share)
UPDATE
Also you should find views after fragment view got created. It means you should call `findViewById` inside `onViewCreated` and not inside `onCreateView`. If you try to find views before view of fragment gets created then you get `NullPointerException` since there is no view yet.
I've made a little sample of something I'm trying to do in a real app, and I can't figure out how to solve my issue. The current solution looks like this.
So I have a vertical list of movie categories, where within each genre, I have a horizontal list of movies. The first row, is showing The Exorcist as the first element, but it's not centered. The second row in Action, shows how it looks when I've scrolled a bit to the right. Last row is showing how the end of the row looks like.
I'd like to have the first and last of the rows, to be centered as well when they're "selected".
My main activity looks like this:
<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"
android:background="#android:color/white">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/moviesListRecyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/movie_category_item_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
The movie_category_item_view looks like this:
<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:id="#+id/movieListLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/movieCategoryTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="40dp"
android:textColor="#android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Horror" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/moviesRecyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:orientation="horizontal"
android:visibility="visible"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/movieCategoryTitle"
tools:listitem="#layout/movie_horizontal_item_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
And the movie_horizontal_item_view like this:
<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="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/movieTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Horror" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/image"
android:layout_width="237dp"
android:layout_height="209dp"
android:background="#android:color/black"
android:src="#drawable/ic_launcher_foreground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/movieTitle"
tools:src="#drawable/ic_launcher_foreground" />
</androidx.constraintlayout.widget.ConstraintLayout>
Hopefully what I'm asking for makes sense!
In case you want to try it out for yourself and see what I mean, it can be found on github here
use carouselview.CarouselView
<alirezat775.lib.carouselview.CarouselView
android:id="#+id/carousel_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"/>
the adapter
class MyAdapter(var ctx: Context) : CarouselAdapter() {
private var vh: CarouselViewHolder? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CarouselViewHolder {
val inflater = LayoutInflater.from(parent.context)
val v = inflater.inflate(R.layout.home_list_item, parent, false)
vh = MyViewHolder(v)
return vh as MyViewHolder
}
override fun onBindViewHolder(holder: CarouselViewHolder, position: Int) {
}
inner class MyViewHolder(itemView: View) : CarouselViewHolder(itemView) {
}
}
the activity
class MainActivity : AppCompatActivity() {
var carousel: Carousel? = null
val adapter = MyAdapter(this)
override fun onCreate(savedInstanceState: Bundle?) {
carousel = Carousel(this, carousel_view, adapter)
carousel!!.setOrientation(CarouselView.HORIZONTAL, false)
// carousel!!.autoScroll(true, 5000, true)
// carousel!!.scaleView(true)
}
}