How to start an activity from another acitivity when using databinding - android

I am using data binding in my project but whenever i want to start a new activity from my current one, it throws an error. I tried all the methods i can find on stackoverflow but none of those are seem to be working for me.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.app.erp, PID: 12624
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.app.erp/com.app.erp.teacher_view.ActivityAfterTeacherLoginNavigationTakeAttendance}: java.lang.NullPointerException: null cannot be cast to non-null type java.util.ArrayList<kotlin.String>{ kotlin.collections.TypeAliasesKt.ArrayList<kotlin.String> }
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3308)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3457)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7560)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.lang.NullPointerException: null cannot be cast to non-null type java.util.ArrayList<kotlin.String>{ kotlin.collections.TypeAliasesKt.ArrayList<kotlin.String> }
at com.app.erp.teacher_view.ActivityAfterTeacherLoginNavigationTakeAttendance.onCreate(ActivityAfterTeacherLoginNavigationTakeAttendance.kt:86)
at android.app.Activity.performCreate(Activity.java:7894)
at android.app.Activity.performCreate(Activity.java:7881)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3283)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3457) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044) 
at android.os.Handler.dispatchMessage(Handler.java:107) 
at android.os.Looper.loop(Looper.java:224) 
at android.app.ActivityThread.main(ActivityThread.java:7560) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
My Activity -
class ActivityAfterTeacherLoginNavigation : AppCompatActivity() {
private val fragmentViewModel : ActivityAfterTeacherLoginNavigationViewModel by viewModels()
private val db = FirebaseFirestore.getInstance()
private lateinit var teacherAttendanceArrayListRecyclerView: ArrayList<FragmentTeacherAttendanceLogRecyclerViewDataClass>
private lateinit var binding: ActivityAfterTeacherLoginNavigationBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_after_teacher_login_navigation)
binding.addAttendance = this
teacherAttendanceArrayListRecyclerView = arrayListOf<FragmentTeacherAttendanceLogRecyclerViewDataClass>()
val navController = findNavController(R.id.fragmentContainerView)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView)!!
val navigator = KeepStateNavigator(this, navHostFragment.childFragmentManager, R.id.fragmentContainerView)
navController.navigatorProvider += navigator
navController.setGraph(R.navigation.teacher_activity_bottom_navigation)
binding.bottomNavigationView.setupWithNavController(navController)
navController.addOnDestinationChangedListener { _, destination, _ ->
when ((destination as FragmentNavigator.Destination).className) {
FragmentTeacherAttendanceLog::class.qualifiedName -> {
binding.fab.show()
binding.fab.text = "Add Attendance"
}
else -> {
binding.fab.hide()
}
}
}
loadTeacherInfo()
loadBatchData()
getAttendanceData()
}
fun navigateToTakeAttendance(view: View) {
startActivity(Intent(this#ActivityAfterTeacherLoginNavigation, ActivityAfterTeacherLoginNavigationTakeAttendance::class.java))
}
My XML file -
<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="addAttendance"
type="com.app.erp.teacher_view.ActivityAfterTeacherLoginNavigation" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/parentFrameLayout"
tools:context=".teacher_view.ActivityAfterTeacherLoginNavigation">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/fab"
android:stateListAnimator="#null"
app:elevation="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:onClick="#{addAttendance::navigateToTakeAttendance}"
app:layout_anchor="#id/bottomNavigationView"
app:icon="#drawable/ic_baseline_post_add_24"
app:layout_anchorGravity="end"
android:translationY="-20sp"
android:layout_marginEnd="20sp"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:layout_gravity="bottom"
app:labelVisibilityMode="selected"
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
app:menu="#menu/teacher_activity_bottom_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/secondaryFragmentContainerTeacher" />
</FrameLayout>
Let me know if any other information is required. I am new to Data binding and it's concepts so i apologize if my approach is not good.
The code of the activity i want to start
class ActivityAfterTeacherLoginNavigationTakeAttendance : AppCompatActivity() {
private val db = FirebaseFirestore.getInstance()
private val fragmentViewModel: ActivityAfterTeacherLoginNavigationTakeAttendanceViewModel by viewModels()
private lateinit var title: TextView
private lateinit var binding: ActivityAfterTeacherLoginNavigationTakeAttendanceBinding
private lateinit var tempArrayListSemesterInfo: ArrayList<String>
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_after_teacher_login_navigation_take_attendance)
binding.title.text = "Attendance Details"
binding.proceedBut.setOnClickListener {
checkInput()
}
supportFragmentManager.popBackStack()
binding.semesterSelect.setOnClickListener{
if(binding.batchSelect.text.toString().isBlank()){
binding.batchSelect.setError("Please select Batch first",null)
binding.batchSelect.requestFocus()
}
}
binding.dateSelect.setText(getCurrentDate(),null)
binding.timeSelect.setText(getCurrentTime(),null)
fragmentViewModel.teacherCodeData.value = intent.getStringExtra("teacherCodeTxt").toString()
binding.teacherCode.setText(intent.getStringExtra("teacherCodeTxt").toString(),null)
fragmentViewModel.teacherNameData.value = intent.getStringExtra("teacherTxt").toString()
binding.teacherSelect.setText(intent.getStringExtra("teacherTxt").toString(),null)
val batchArrayList: ArrayList<String> = intent.getStringArrayListExtra("batchTxt") as ArrayList<String>
val batchArrayAdapter = ArrayAdapter<String>(this,
R.layout.exposed_dropdown_menu_item_layout,batchArrayList)
binding.batchSelect.setAdapter(batchArrayAdapter)
val courseArrayList: ArrayList<String> = intent.getStringArrayListExtra("courseTxt") as ArrayList<String>
val courseArrayAdapter = ArrayAdapter<String>(this,
R.layout.exposed_dropdown_menu_item_layout,courseArrayList)
binding.courseSelect.setAdapter(courseArrayAdapter)
tempArrayListSemesterInfo = arrayListOf<String>()
binding.batchSelect.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ ->
if(binding.batchSelect.text.isNotEmpty()){
binding.batchSelect.error = null
}
binding.semesterSelect.text.clear()
tempArrayListSemesterInfo.clear()
val selectedItem = parent.getItemAtPosition(position).toString()
db.collection("BatchInfo").whereEqualTo("Name",selectedItem)
.addSnapshotListener(object: EventListener<QuerySnapshot> {
#SuppressLint("NotifyDataSetChanged")
override fun onEvent(value: QuerySnapshot?, error: FirebaseFirestoreException?) {
if(error != null){
Log.e("Firestore Error",error.message.toString())
return
}
for(dc : DocumentChange in value?.documentChanges!!){
if(dc.type == DocumentChange.Type.ADDED){
tempArrayListSemesterInfo.addAll(dc.document.data.getValue("Semester") as Collection<String>)
val addStudentSemesterSelectAdapter = ArrayAdapter(this#ActivityAfterTeacherLoginNavigationTakeAttendance,
R.layout.exposed_dropdown_menu_item_layout, tempArrayListSemesterInfo)
binding.semesterSelect.setAdapter(addStudentSemesterSelectAdapter)
}
break
}
}
})
}
binding.courseSelect.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ ->
if (binding.courseSelect.text.isNotEmpty()) {
binding.courseSelect.error = null
}
val selectedItem = parent.getItemAtPosition(position).toString()
db.collection("CourseInfo").whereEqualTo("CourseName",selectedItem).get()
.addOnCompleteListener { itAlt ->
if(itAlt.isSuccessful){
for (doc in itAlt.result!!){
binding.courseCode.setText(doc.data.getValue("CourseCode").toString(),null)
}
}
}
}
binding.proceedBut.setOnClickListener {
checkInput()
}
}
}

Inside the loop you are trying to read a value that might be null value?.documentChanges . So you must use safe cast operator
add ? after as
for(dc : DocumentChange in value?.documentChanges!!){
if(dc.type == DocumentChange.Type.ADDED){
tempArrayListSemesterInfo.addAll(dc.document.data.getValue("Semester") as? Collection<String>)
val addStudentSemesterSelectAdapter = ArrayAdapter(this#ActivityAfterTeacherLoginNavigationTakeAttendance,
R.layout.exposed_dropdown_menu_item_layout, tempArrayListSemesterInfo)
binding.semesterSelect.setAdapter(addStudentSemesterSelectAdapter)
}
break
}

Related

I got this bug while tryimg to run my app "Error in Inflating Class Fragment"

Caused by: android.view.InflateException: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Error inflating class fragment Caused by: android.view.InflateException: Binary XML file line #25 in com.androiddevs.mvvmnewsapp:layout/activity_news: Error inflating class fragment Caused by: kotlin.UninitializedPropertyAccessException: lateinit property viewModel has not been initialized
at com.androiddevs.mvvmnewsapp.ui.NewsActivity.getViewModel(NewsActivity.kt:15) at com.androiddevs.mvvmnewsapp.ui.fragments.BreakingNewsFragment.onCreateView(BreakingNewsFragment.kt:29) at
These are the codes Involved
My activity News Xml
<FrameLayout
android:id="#+id/flFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<fragment
android:id="#+id/newsNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/news_nav_graph" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="56dp"
app:menu="#menu/bottom_navigation_menu"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Breaking News Fragment
class BreakingNewsFragment:Fragment() {
lateinit var binding: FragmentBreakingNewsBinding
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter:NewsAdapter
val TAG = "BreakingNewsFragment"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentBreakingNewsBinding.inflate(layoutInflater,container,false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
viewModel = (activity as NewsActivity).viewModel
viewModel.breakingNews.observe(viewLifecycleOwner, Observer { response ->
when(response){
is Resource.Success -> {
hideProgressBar()
response.data?.let { newsResponse ->
newsAdapter.differ.submitList(newsResponse.articles)
}
}
is Resource.Error ->{
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An error occured: $message")
}
}
is Resource.Loading ->{
showProgressBar()
}
}
})
}
private fun hideProgressBar(){
binding.paginationProgressBar.visibility = View.INVISIBLE
}
private fun showProgressBar(){
binding.paginationProgressBar.visibility = View.VISIBLE
}
private fun setupRecyclerView(){
newsAdapter = NewsAdapter()
binding.rvBreakingNews.apply {
adapter = newsAdapter
layoutManager = LinearLayoutManager(activity)
}
}
}
News Activity
class NewsActivity : AppCompatActivity() {
lateinit var viewModel: NewsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_news)
val newsRepository = NewsRepository(ArticleDatabase(this))
val viewModelProviderFactory = NewsViewModelProviderFactory(newsRepository)
viewModel = ViewModelProvider(this, viewModelProviderFactory).get(NewsViewModel::class.java)
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
val newsNavHostFragment = findViewById<View>(R.id.newsNavHostFragment)
bottomNavigationView.setupWithNavController(newsNavHostFragment.findNavController())
}
}

API is fetching all the JSON data but my app displays only one in recycler view what should i do?

I am making a simple recipe search app using recipe search API but the problem is the app fetches all the data from API but my app displays only one in recycler view then what should I do? Please help
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var recipeViewModel: RecipeViewModel
lateinit var mainBinding: ActivityMainBinding
lateinit var recipeAdapter: RecipeAdapter
lateinit var recipeItemList: ArrayList<Recipes>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
recipeViewModel =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory
.getInstance(application)
)[RecipeViewModel::class.java]
recipeItemList = arrayListOf()
mainBinding.recyclerView.layoutManager = LinearLayoutManager(this)
recipeViewModel.recipeLiveData.observe(this, Observer { recipeItems ->
recipeItemList.add(recipeItems)
recipeAdapter = RecipeAdapter(this, recipeItemList)
mainBinding.recyclerView.adapter = recipeAdapter
Log.d("RESPONSE", recipeItems.toString())
Log.d("List size", recipeAdapter.itemCount.toString())
})
searchRecipeName()
}
private fun searchRecipeName() {
mainBinding.searchRecipeFabBtn.setOnClickListener {
val view = layoutInflater.inflate(R.layout.recipe_search_layout, null)
val searchRecipeET = view.findViewById<EditText>(R.id.searchRecipeET)
val searchRecipeBtn = view.findViewById<Button>(R.id.searchRecipeBtn)
val bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.apply {
this.setContentView(view)
this.show()
}
searchRecipeBtn.setOnClickListener {
val recipeName = searchRecipeET.text.toString()
searchRecipeName(recipeName, searchRecipeET, bottomSheetDialog)
}
}
}
private fun searchRecipeName(
recipeName: String,
searchRecipeET: EditText,
bottomSheetDialog: BottomSheetDialog
) {
if (recipeName.isEmpty()) {
searchRecipeET.error = "Please enter recipe name"
} else {
recipeViewModel.getRecipes(recipeName)
bottomSheetDialog.dismiss()
}
}
}
RecipeAdapter.kt
class RecipeAdapter(val context: Context, val recipesList: ArrayList<Recipes> = arrayListOf()) : RecyclerView.Adapter<RecipeAdapter.RecipeViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
val layoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(R.layout.recipe_items_layout, null, false)
return RecipeViewHolder(view)
}
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
val currentItem = recipesList[position]
holder.recipeImageView.load(currentItem.hits[3].recipe.image)
holder.recipeNameText.text = currentItem.hits[4].recipe.label
}
override fun getItemCount(): Int {
return recipesList.size
}
class RecipeViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val recipeImageView: ImageView = itemView.findViewById(R.id.recipeImageView)
val recipeNameText: TextView = itemView.findViewById(R.id.recipeNameText)
}
}
RecipeViewModel.kt
class RecipeViewModel() : ViewModel() {
private val recipeRepository: RecipeRepository =
RecipeRepository(RetrofitInstance.provideApiService)
val recipeLiveData: LiveData<Recipes>
get() = recipeRepository.recipeLiveData
fun getRecipes(q:String) = viewModelScope.launch {
recipeRepository.getRecipes(q)
}
}
RecipeRepository.kt
class RecipeRepository(private val apiService: ApiService) {
private val recipeMutableLiveData: MutableLiveData<Recipes> = MutableLiveData()
val recipeLiveData: LiveData<Recipes>
get() = recipeMutableLiveData
suspend fun getRecipes(q:String){
val response = apiService.getRecipes(q)
recipeMutableLiveData.value = response.body()
}
}
APIService.kt
interface ApiService {
#GET("/api/recipes/v2?type=public&app_id=${APP_ID.appId}&app_key=${API_KEY.apiKey}")
suspend fun getRecipes(#Query("q") q:String): Response<Recipes>
}
Logs
2022-03-01 17:06:00.267 14222-14222/com.yash1307.digitalrecipebook D/RESPONSE: Recipes(hits=[Hit(recipe=Recipe(calories=308.34999999999997, dietLabels=[Low-Carb, Low-Sodium], healthLabels=[Sugar-Conscious, Low Potassium, Kidney-Friendly, Keto-Friendly, Vegetarian, Pescatarian, Paleo, Mediterranean, Dairy-Free, Gluten-Free, Wheat-Free, Peanut-Free, Tree-Nut-Free, Soy-Free, Fish-Free, Shellfish-Free, Pork-Free, Red-Meat-Free, Crustacean-Free, Celery-Free, Mustard-Free, Sesame-Free, Lupine-Free, Mollusk-Free, Alcohol-Free, No oil added, Sulfite-Free, FODMAP-Free, Kosher], image=https://www.edamam.com/web-img/20f/20f0c2553240a2c6bc639d64df3f9df4.jpg, label=Poached Eggs, totalNutrients=TotalNutrients(CA=CAX(label=Calcium, quantity=120.69999999999999, unit=mg), CHOCDF=CHOCDFX(label=Carbs, quantity=1.5499999999999998, unit=g), CHOLE=CHOLEX(label=Cholesterol, quantity=799.8, unit=mg), ENERC_KCAL=ENERCKCALX(label=Energy, quantity=308.34999999999997, unit=kcal), FAT=FATX(label=Fat, quantity=20.4465, unit=g), FE=FEX(label=Iron, quantity=3.764, unit=mg), K=KX(label=Potassium, quantity=296.8, unit=mg), MG=MGX(label=Magnesium, quantity=25.849999999999998, unit=mg), NA=NAX(label=Sodium, quantity=305.40000000000003, unit=mg), PROCNT=PROCNTX(label=Protein, quantity=27.004, unit=g)))), Hit(recipe=Recipe(calories=786.91, dietLabels=[Low-Carb], healthLabels=[Sugar-Conscious, Low Potassium, Kidney-Friendly, Vegetarian, Pescatarian, Peanut-Free, Tree-Nut-Free, Soy-Free, Fish-Free, Shellfish-Free, Pork-Free, Red-Meat-Free, Crustacean-Free, Celery-Free, Mustard-Free, Sesame-Free, Lupine-Free, Mollusk-Free, Alcohol-Free, Sulfite-Free, Kosher, Immuno-Supportive], image=https://www.edamam.com/web-img/943/943f98393348d0daf5f239e328c0bb5d.jpg, label=Moonstruck eggs, totalNutrients=TotalNutrients(CA=CAX(label=Calcium, quantity=142.34, unit=mg), CHOCDF=CHOCDFX(label=Carbs, quantity=28.896199999999997, unit=g), CHOLE=CHOLEX(label=Cholesterol, quantity=472.57000000000005, unit=mg), ENERC_KCAL=ENERCKCALX(label=Energy, quantity=786.91, unit=kcal), FAT=FATX(label=Fat, quantity=67.6459, unit=g), FE=FEX(label=Iron, quantity=3.5434, unit=mg), K=KX(label=Potassium, quantity=238.37999999999997, unit=mg), MG=MGX(label=Magnesium, quantity=37.84, unit=mg), NA=NAX(label=Sodium, quantity=424.57, unit=mg), PROCNT=PROCNTX(label=Protein, quantity=17.622700000000002, unit=g)))), Hit(recipe=Recipe(calories=451.61312499999997, dietLabels=[], healthLabels=[Sugar-Conscious, Low Potassium, Kidney-Friendly, Keto-Friendly, Vegetarian, Pescatarian, Mediterranean, Dairy-Free, Peanut-Free, Tree-Nut-Free, Soy-Free, Fish-Free, Shellfish-Free, Pork-Free, Red-Meat-Free, Crustacean-Free, Celery-Free, Mustard-Free, Sesame-Free, Lupine-Free, Mollusk-Free, Alcohol-Free, Sulfite-Free, Kosher], image=https://www.edamam.com/web-img/558/558ccc3d6e43aaf065322133ad6287b0.jpg, label=Poached Eggs, totalNutrients=TotalNutrients(CA=CAX(label=Calcium, quantity=197.9872197236699, unit=mg), CHOCDF=CHOCDFX(label=Carbs, quantity=29.56533125, unit=g), CHOLE=CHOLEX(label=Cholesterol, quantity=744.0, unit=mg), ENERC_KCAL=ENERCKCALX(label=Energy, quantity=451.61312499999997, unit=kcal), FAT=FATX(label=Fat, quantity=21.014962499999996, unit=g), FE=FEX(label=Iron, quantity=5.603875052450463, unit=mg), K=KX(label=Potassium, quantity=394.53073990789, unit=mg), MG=MGX(label=Magnesium, quantity=53.41204561348624, unit=mg), NA=NAX(label=Sodium, quantity=728.8926375, unit=mg), PROCNT=PROCNTX(label=Protein, quantity=31.436606249999997, unit=g)))), Hit(recipe=Recipe(calories=2863.0874999990874, dietLabels=[Low-Carb], healthLabels=[Sugar-Conscious, Peanut-Free, Tree-Nut-Free, Soy-Free, Fish-Free, Shellfish-Free, Crustacean-Free, Celery-Free, Mustard-Free, Sesame-Free, Lupine-Free, Mollusk-Free, Alcohol-Free, Sulfite-Free], image=https://www.edamam.com/web-img/48a/48ae883aa945c01b0b8c590d40e6fd34.jpg, label=Eggs Benedict, totalNutrients=TotalNutrients(CA=CAX(label=Calcium, quantity=584.8499999983388, unit=mg), CHOCDF=CHOCDFX(label=Carbs, quantity=89.18834999989487, unit=g), CHOLE=CHOLEX(label=Cholesterol, quantity=1377.6524999998987, unit=mg), ENERC_KCAL=EN
App image
Here is the image that displays only one item
Recipe Items Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="6dp"
app:cardElevation="6dp"
app:cardUseCompatPadding="true">
<ImageView
android:id="#+id/recipeImageView"
android:layout_width="140dp"
android:layout_height="140dp"
android:scaleType="centerCrop"
android:src="#drawable/ic_baseline_search_24" />
<TextView
android:id="#+id/recipeNameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="12dp"
android:textSize="20sp"
tools:text="Recipe Name" />
</com.google.android.material.card.MaterialCardView>
</RelativeLayout>
activity_main.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"
tools:context=".activities.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="409dp"
android:layout_height="729dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/searchRecipeFabBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="#+id/recyclerView"
app:layout_constraintEnd_toEndOf="#+id/recyclerView"
app:layout_constraintHorizontal_bias="0.957"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/recyclerView"
app:layout_constraintVertical_bias="0.950"
app:srcCompat="#drawable/ic_baseline_search_24"
android:focusable="true"
android:contentDescription="#string/search_recipes" />
</androidx.constraintlayout.widget.ConstraintLayout>
Reicpes.kt (data class)
data class Recipes(
val hits: ArrayList<Hit>,
)
Hit.kt (data class)
data class Hit(
val recipe: Recipe
)
Recipe.kt (data class)
data class Recipe(
val calories: Double,
val dietLabels: List<String>,
val healthLabels: List<String>,
val image: String,
val label: String,
val totalNutrients: TotalNutrients,
)
in your Adapter you trying to get recipesList size which is contain only one item hits in it
override fun getItemCount(): Int {
return recipesList.size
}
I think you need to use recipesList.hits.size instead of recipesList.size
override fun getItemCount(): Int {
return recipesList.hits.size
}

refresh data in a Firestore Paging Adapter using Swipe Refresh Layout

I am using FirestorePagingAdapter to show data in my app but when I use Swipe Refresh Layout did not work with FirestorePagingAdapter, swipe refresh layout.isVisible = true in LOAD_MORE when I don't scroll
how to work with a Swipe Refresh Layout in FirestorePagingAdapter?
ViewHolde.class:
class ViewHolde(item: View) : RecyclerView.ViewHolder(item){
val username = item.findViewById(R.id.iduser) as TextView
val title = item.findViewById(R.id.idtitle) as TextView
val decription = item.findViewById(R.id.iddes) as TextView
}
User.class:
class User {
lateinit var userphoto: StorageReference
lateinit var username:String
lateinit var title:String
lateinit var notes: String
lateinit var id: String
var isSelected:Boolean = false
constructor(){
}
constructor( id: String?,username:String?,title: String?, notes: String?) {
this.title= title!!
this.id= id!!
this.username= username!!
this.notes= notes!!
}
}
RecyclerActivity.class:
class RecyclerActivity : AppCompatActivity(),SwipeRefreshLayout.OnRefreshListener {
var mylist=ArrayList<User>()
var rv:RecyclerView? = null
var currentuser = FirebaseAuth.getInstance().currentUser!!.uid
val str= FirebaseStorage.getInstance().reference
private var mAuthStateListener: FirebaseAuth.AuthStateListener? = null
var db = FirebaseFirestore.getInstance();
private val notebookRef2 = db.collection("ProfileData")
private val mQuery = notebookRef2.orderBy("title", Query.Direction.ASCENDING)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(2)
.setPageSize(6)
.build()
val options = FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, User::class.java)
.build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
rec()
var swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setOnRefreshListener(this);
swipeContainer = findViewById(R.id.swipeContainer)as SwipeRefreshLayout
swipeContainer.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
swipeContainer.setOnRefreshListener {
rv?.invalidate();
swipeContainer.setRefreshing(false);
}
}
override fun onRefresh() {
Handler().postDelayed( Runnable() {
swipeContainer.setRefreshing(false);
}, 3000);
}
fun rec(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
rv!!.setHasFixedSize(true)
rv!!.layoutManager = LinearLayoutManager(this)
db.collection("ProfileData").orderBy("title", Query.Direction.DESCENDING)
.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
if (querySnapshot != null) {
for(da in querySnapshot) {
val note = da.toObject(Note::class.java)
var title = note.title
var description = note.notes
var username = note!!.username
var id = note!!.id
mylist.add(User(id!!,username!!, title!!, description!!))
}
}
}
setupadapter()
}
fun setupadapter(){
rv=findViewById(R.id.idrecycler) as? RecyclerView
var mAdapter = object : FirestorePagingAdapter<User, ViewHolde>(options) {
override fun onLoadingStateChanged(state: LoadingState) {
swipeRefreshLayout2.isVisible = false
when (state) {
LoadingState.LOADING_INITIAL -> {
swipeRefreshLayout2.isVisible = true
}
LoadingState.LOADING_MORE -> {
swipeRefreshLayout.isVisible = true
}
LoadingState.LOADED -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.ERROR -> {
swipeRefreshLayout.isVisible = false
}
LoadingState.FINISHED -> {
swipeRefreshLayout.isVisible = false
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolde {
var v = LayoutInflater.from(parent.context)
.inflate(R.layout.iktem_list, parent, false)
return ViewHolde(v)
}
override fun onBindViewHolder(holder: ViewHolde, position: Int, p2: User) {
holder.username.text = mylist.get(position).username
holder.title.text = mylist.get(position).title
holder.decription.text = mylist.get(position).notes
}
}
rv!!.adapter = mAdapter
}
}
ActivityRecycler.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/iddrawer2"
tools:context=".RecyclerActivity">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_height="match_parent"
android:layout_width="match_parent" >
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#4C00CF"
android:id="#+id/idtoolbar2"
tools:ignore="MissingConstraints"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_marginTop="50dp"
android:layout_height="460dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idrecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="#+id/swipeRefreshLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="340dp"
android:visibility="gone"
/>
<ProgressBar
android:id="#+id/swipeRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="450dp"
android:visibility="gone"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:headerLayout="#layout/header"
android:id="#+id/idnavigatuonv2"
android:layout_gravity="start"
app:menu="#menu/draw_menu"/>
</androidx.drawerlayout.widget.DrawerLayout >
So I'm using the adapter to show transaction history and I have a button to show the filtered transaction history which has status == success.
On button click, I'm updating the adapter the following way:
baseQuery = firestore.collection("transactions")
.whereEqualTo("status", "success")
.orderBy("__name__", Query.Direction.DESCENDING);
options = new FirestorePagingOptions.Builder<ItemTxn>()
.setLifecycleOwner(this)
.setQuery(baseQuery, config, ItemTxn.class)
.build();
adapter.updateOptions(options);
https://github.com/firebase/FirebaseUI-Android/issues/1726#issuecomment-573033035

Cannot call method ViewModel from xml with dataBinding

I want to start increase the count by every second pass. When I want stop the counter by using method stopCount(), I can't call it by OnClick in xml.
I got this error:
e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
.
<data>
<variable name="mainViewModel" type="com.ali.mvvm_livedata.MainViewModel"/>
<variable name="count" type="String"/>
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#={mainViewModel.liveData}"
android:onClick="#{() -> mainViewModel.stopCount()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#={mainViewModel.liveData}"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</android.support.constraint.ConstraintLayout>
Here my ViewModel
class MainViewModel : ViewModel() {
private val timer = Timer()
private lateinit var timmer: TimerTask
val liveData: MutableLiveData<String> = MutableLiveData()
fun getTime(): MutableLiveData<String> {
return liveData
}
fun count() {
var tempCount = 0
timmer = object : TimerTask() {
override fun run() {
tempCount++
Log.i("currentCount", tempCount.toString())
}
}
timer.scheduleAtFixedRate(timmer, 1000, 1000)
}
internal fun stopCount() {
timmer.cancel()
}
MainActivity code
#Inject
lateinit var mModelFactory: ViewModelFactory
private lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
mainViewModel = ViewModelProviders.of(this, mModelFactory).get(MainViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.mainViewModel = mainViewModel
binding.lifecycleOwner = this
mainViewModel.count()
}
you have to remove the internal(front of the fun stopCount)

onClick not working in mvvm in android databinding

I am trying to implement mvvm and databinding in kotlin first time. I followed some tutorial and able to implement same. But now button click is not working which I have written in mvvm.
Here is activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.abc.abc.presentation.user.viewmodels.LoginViewModel"/>
</data>
<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=".presentation.user.views.LoginActivity"
android:layout_margin="24dip">
<Button
android:layout_width="match_parent"
android:layout_height="40dip"
android:background="#drawable/btn_user_create_account"
android:layout_above="#+id/or_layout"
android:layout_marginBottom="24dp"
android:text="#string/create_account"
android:id="#+id/btn_create_account"
android:layout_marginEnd="12dip"
android:layout_marginStart="12dip"
android:gravity="center_horizontal|center_vertical"
android:textColor="#color/text_color"
android:textAllCaps="false"
android:onClick="#{() -> viewmodel.redirectToRegisterActivity()}"
android:fontFamily="#font/vollkron"
android:textStyle="bold"
/>
<Button
android:layout_width="match_parent"
android:layout_height="40dip"
android:background="#drawable/btn_user_login_reg"
android:layout_above="#+id/btn_create_account"
android:layout_marginBottom="12dp"
android:text="#string/login"
android:id="#+id/btn_login"
android:layout_marginEnd="12dip"
android:layout_marginStart="12dip"
android:gravity="center_horizontal|center_vertical"
android:textColor="#color/white"
android:textAllCaps="false"
android:onClick="#{() -> viewmodel.executeEmailLogin(context)}"
android:fontFamily="#font/vollkron"
android:textStyle="bold"/>
</RelativeLayout>
Here is my ViewModel code :
class LoginViewModel(application: Application) : AndroidViewModel(application) {
var email: ObservableField<String>? = null
var password: ObservableField<String>? = null
var progressDialog: SingleLiveEvent<Boolean>? = null
var launchRegisterActivity: SingleLiveEvent<Boolean>? = null
var userLogin: LiveData<User>? = null
init {
progressDialog = SingleLiveEvent<Boolean>()
launchRegisterActivity = SingleLiveEvent<Boolean>()
email = ObservableField("")
password = ObservableField("")
userLogin = MutableLiveData()
}
fun redirectToRegisterActivity() {
launchRegisterActivity?.value = true
}
fun executeEmailLogin(context: Context) {
progressDialog?.value = true
val user = User(
email = email.toString(),
password = password.toString(),
)
userLogin = UserRepository.getInstance().registerUserUsingEmail(context, user)
}
}
Here is my Login Activity
class LoginActivity : AppCompatActivity() {
var binding: ActivityLoginBinding? = null
var viewModel: LoginViewModel? = null
var customProgressDialog: CustomProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
FacebookSdk.sdkInitialize(applicationContext);
binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
customProgressDialog = CustomProgressDialog(this)
viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
observeViewModel(viewModel!!)
}
private fun observeViewModel(viewModel: LoginViewModel) {
viewModel.progressDialog?.observe(this, Observer {
if (it!!) customProgressDialog?.show() else customProgressDialog?.dismiss()
})
viewModel.launchRegisterActivity?.observe(this, Observer {
if (it!!) startActivity(Intent(this, RegisterActivity::class.java))
})
viewModel.userLogin?.observe(this, Observer { user ->
// redirect user to home scree
Toast.makeText(this, "welcome, ${user?.user_name}", Toast.LENGTH_LONG).show()
})
}
}
Can you please help me to understand what things I am doing wrong here. Do I implemented mvvm correctly?
Any button click is not working.
I think, You forgot to bind viewmodel to databinding.
Add this in onCreate of LoginActivity :
binding.lifecycleOwner = this
binding.viewmodel = viewModel
Try adding
binding.lifecycleOwner = this
after inflating your layout.

Categories

Resources