snapHelper.findSnapView() always returns null - android

I'm trying to implement left and right buttons on the home Fragment of my app, which should scroll to the next card of my RecyclerView. To do this I need to determine the current position of the view so that I can smooth scroll to this position +-1. But helper.findSnapView(linearLayoutManager) always returns null. When this should be fetched there is a
I/ViewConfigCompat: Could not find method getScaledScrollFactor() on ViewConfiguration
in the logcat, which I think is causing the problem. But searching online doesn't yield anything. The code is below. Please try to help me to understand the issue.
package com.example.storybook
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SnapHelper
import com.example.storybook.Model.Book
import com.example.storybook.Utils.MarginItemDecoration
import com.example.storybook.Utils.RecyclerViewAdapter
import kotlinx.android.synthetic.main.fragment.*
class Fragment : Fragment(), View.OnClickListener {
private var mBooks = mutableListOf<Book>()
private val helper: SnapHelper = LinearSnapHelper()
private val linearLayoutManager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val listOfBookFilenames: Array<String> = context?.assets?.list("PDFs")!!
for (filename in listOfBookFilenames) {
val tempBook = Book(filename)
mBooks.add(tempBook)
}
return inflater.inflate(R.layout.fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
books_recycler.apply {
layoutManager = linearLayoutManager
adapter = RecyclerViewAdapter(mBooks, context)
}
books_recycler.addItemDecoration(MarginItemDecoration(65))
helper.attachToRecyclerView(books_recycler)
left_button.setOnClickListener {onClick(left_button)}
right_button.setOnClickListener {onClick(right_button)}
}
override fun onClick(view: View) {
val snapView: View? = helper.findSnapView(linearLayoutManager)
var currentFocusPosition = RecyclerView.NO_POSITION
if (snapView == null) {
println("View is null")
} else {
currentFocusPosition = linearLayoutManager.getPosition(snapView)
}
when(view) {
right_button -> books_recycler.smoothScrollToPosition(currentFocusPosition + 1)
left_button -> books_recycler.smoothScrollToPosition(currentFocusPosition - 1)
}
}
}
I have already tried declaring the snapHelper and linearLayoutManagers locally, within the onViewCreated and onClick methods, and it seems to make no difference. This is my first ever app, so there might be a lot I don't understand.

Despite having tried every configuration several times, and rebuilding over an over again, I rebuilt just one more time with the above configuration and it just worked. Sometimes it works out that way.
If you're here from the future and you're having the same problem, try declaring your snapHelper and linearLayoutManager as private values outside the functions and try rebuilding again.

Related

Caused by: java.io.IOException: The Application Default Credentials are not available

I'm working on an android app that interfaces with a google sheet. I was following the api documentation for writing values to certain cells but I'm getting this error here:
Caused by: java.io.IOException: The Application Default Credentials are not available.
I have made sure to set my credentials in google cloud platform for my project and downloaded the .json file to src\main\resources in my android studio project.
here is the code that is supposed to write the data:
package com.example.frcscout22
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.Fragment
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.gson.GsonFactory
import com.google.api.services.sheets.v4.Sheets
import com.google.api.services.sheets.v4.SheetsScopes
import com.google.api.services.sheets.v4.model.ValueRange
import com.google.auth.http.HttpCredentialsAdapter
import com.google.auth.oauth2.GoogleCredentials
import java.util.*
class Data : Fragment(R.layout.fragment_data) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val spinner = view.findViewById<Spinner>(R.id.defense_spinner)
val aa = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, resources.getStringArray(R.array.Defenses))
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = aa
view.findViewById<Spinner>(R.id.defense_spinner).visibility = View.GONE
view.findViewById<TextView>(R.id.textView6).visibility = View.GONE
view.findViewById<EditText>(R.id.Team_Defended).visibility = View.GONE
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View = inflater!!.inflate(R.layout.fragment_data, container, false)
val checkBox = view.findViewById<CheckBox>(R.id.checkBox)
checkBox.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
view.findViewById<Spinner>(R.id.defense_spinner).visibility = View.VISIBLE
view.findViewById<TextView>(R.id.textView6).visibility = View.VISIBLE
view.findViewById<EditText>(R.id.Team_Defended).visibility = View.VISIBLE
} else {
view.findViewById<Spinner>(R.id.defense_spinner).visibility = View.GONE
view.findViewById<TextView>(R.id.textView6).visibility = View.GONE
view.findViewById<EditText>(R.id.Team_Defended).visibility = View.GONE
}
}
val clear = view.findViewById<Button>(R.id.button2)
clear.setOnClickListener(View.OnClickListener {
view.findViewById<EditText>(R.id.Match_Number).setText("")
view.findViewById<EditText>(R.id.Team_Number).setText("")
view.findViewById<EditText>(R.id.Auto_Points).setText("")
view.findViewById<EditText>(R.id.Teleop_Points).setText("")
view.findViewById<EditText>(R.id.Endgame_Points).setText("")
view.findViewById<EditText>(R.id.Team_Defended).setText("")
view.findViewById<Spinner>(R.id.defense_spinner).setSelection(0)
view.findViewById<CheckBox>(R.id.checkBox).isChecked = false
})
val table : MutableList<MutableList<Any>> = mutableListOf(mutableListOf("test", "test2"))
val values = ValueRange()
values.majorDimension = "ROWS"
values.setValues(table)
val button = view.findViewById<View>(R.id.button)
button.setOnClickListener(View.OnClickListener {
setRow("1fs1U9-LMmkmQbQ2Kn-rNVHIQwh6_frAbwaTp7MSyDIA", "Sheet1!A1:B1", values)
})
// Return the fragment view/layout
return view
}
private fun setRow(spreadSheetID: String, range: String, values: ValueRange) {
val credentials: GoogleCredentials = GoogleCredentials.getApplicationDefault().createScoped(
Collections.singleton(
SheetsScopes.SPREADSHEETS))
val requestInitializer: HttpRequestInitializer = HttpCredentialsAdapter(credentials)
val service: Sheets = Sheets.Builder(NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer).setApplicationName("FRCScout22").build()
service.spreadsheets().values().update(spreadSheetID, range, values)
.setValueInputOption("USER_ENTERED")
.execute();
}
}
Does anyone know why I may be getting this error? Thanks!!
Application Default Credentials (ADC) is a process that looks for the credentials in various places including the env var GOOGLE_APPLICATION_CREDNTIALS.
If GOOGLE_APPLICATION_CREDNTIALS is unset ADC fails and raises an error.

Launching activity from Fragment, Kotlin

I'm new to Kotlin/Android development and I'm making an app to display quizzes. Recently I decided to begin using fragments. On my MainActivity which has three fragments, I'd like one to have a method of clicking a subject and being taken to that particular quiz activity.
Note, there is only one quiz activity, but the intents pass a variable to display the relevant data for the quiz.
I had correctly implemented this when this page was not a fragment but struggling to find a solution this time.
Subject Fragment:
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.financialapp.InformationPage
import com.example.financialapp.databinding.FragmentModuleBinding
import android.content.Intent
class ModuleFragment : Fragment(com.quizapp.R.layout.fragment_module) {
private var _binding: FragmentModuleBinding ? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentModuleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val subjectOne = binding.tvEnglish
subjectOne.setOnClickListener {
sendIntent(0)
}
val subjectTwo = binding.tvGeography
subjectOne.setOnClickListener {
sendIntent(1)
}
val subjectThree = binding.tvHistory
subjectThree.setOnClickListener{
sendIntent(2)
}
...
}
private fun sendIntent(passedVariable: Int) {
val intent = Intent(this, SubjectPage::class.java)
intent.putExtra("subject", passedVariable)
startActivity(intent)
finish()
}
...
At present I have errors from Intent asking to create a function, same with finish().
Having looked through several tutorials I can't seem to see whether it's possible or not.
finish() is actually called on activity so you can use requireActivity() get hold of the hosting activity of your fragment & instead of using this in Intent params you can use requireContext()
Example:
private fun sendIntent(passedVariable: Int) {
val intent = Intent(requireContext(), MainActivity::class.java)
intent.putExtra("subject", passedVariable)
startActivity(intent)
requireActivity().finish()
}
Actually, as you started to use fragments you can avoid to start another activity.
Use childFragmentManager and just create for your quiz another fragment.
https://developer.android.com/reference/androidx/fragment/app/Fragment#getChildFragmentManager()

RecyclerView's ViewHolders becoming unclickable

Prehisrory
I have a list of stocks (some objects) from RoomDB. Each one of them have "symbol", "name",
"price" and what's most important, "isFavourite" fields.
I made a ViewPager2 with two Fragments containing RecyclerView (actually there are just two instances of one class StocksFragment - one for all stocks, one for only favourite stocks). Each stock in RecyclerView is connected to the repository through Obsrver (data changes => stock's ViewHolder changes). Also each ViewHolder has own checkBox that changes "isFavourite" Stock field through StockListViewModel that calls StockRepository, that works directly with roomDB (with kotlin coroutines - sth like
fun getStocks(): LiveData<List<Stock>> = runBlocking{ stockDao.getStocks() })
Problem
When i click the same checkBox several times in a relatively small amount of time, all RecyclerView's ViewHolders become unclickable (neither the delete button nor the checkbox works). But i still can scroll RecyclerView How can i fix that?
I think i am doing something very inefficient but i dont know what.
Here is my StocksFragment code:
package com.nikitakrapo.android.happystocks
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class StocksFragment(var stockListType: StockListType) : Fragment() {
private val stockListViewModel: StockListViewModel by lazy{
ViewModelProvider(this).get(StockListViewModel::class.java)
}
private lateinit var stocksRecyclerView: RecyclerView
private var adapter: StocksAdapter? = StocksAdapter()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_stocks, container, false)
stocksRecyclerView = view.findViewById(R.id.recycler_view)
stocksRecyclerView.layoutManager = LinearLayoutManager(context)
stocksRecyclerView.setHasFixedSize(true)
stocksRecyclerView.adapter = adapter
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var stocks = if (stockListType == StockListType.favouriteStocksList)
stockListViewModel.favStockListLiveData
else stockListViewModel.stockListLiveData
stocks.observe(
viewLifecycleOwner,
{ stocks ->
stocks?.let {
adapter?.setStocks(stocks)
}
}
)
}
private inner class StockHolder(view: View) : RecyclerView.ViewHolder(view){
private lateinit var stock: Stock
val symbolTextView: TextView = itemView.findViewById(R.id.stock_symbol)
private val nameTextView: TextView = itemView.findViewById(R.id.stock_name)
private val priceTextView: TextView = itemView.findViewById(R.id.stock_price)
private val stockImageView: ImageView = itemView.findViewById(R.id.stock_image)
val stockDeleteButton: Button = itemView.findViewById(R.id.stock_delete)
val favouriteCheckBox: CheckBox = itemView.findViewById(R.id.is_favourite)
fun bind(stock: Stock, holder: StockHolder) {
this.stock = stock
symbolTextView.text = this.stock.symbol
nameTextView.text = this.stock.name
priceTextView.text = "$" + this.stock.priceUSD.toString()
favouriteCheckBox.isChecked = this.stock.isFavourite
holder.stockDeleteButton.setOnClickListener {
stockListViewModel.deleteStock(stock)
}
holder.favouriteCheckBox.setOnCheckedChangeListener { buttonView, isChecked ->
stockListViewModel.updateFavourite(holder.symbolTextView.text.toString(), isChecked)
}
}
}
private inner class StocksAdapter
: RecyclerView.Adapter<StockHolder>() {
private var stockList: List<Stock> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
: StockHolder {
val view = layoutInflater.inflate(R.layout.list_item_stock, parent, false)
return StockHolder(view)
}
override fun onBindViewHolder(holder: StockHolder, position: Int) {
val stock = stockList[position]
holder.bind(stock, holder)
}
public fun setStocks(stockList: List<Stock>){
this.stockList = stockList
notifyDataSetChanged()
}
override fun getItemCount() = stockList.size
}
}
Try OnClickListener instead
holder.favouriteCheckBox.setOnClickListener {
if ((it as CompoundButton).isChecked) {
stockListViewModel.updateFavourite(holder.symbolTextView.text.toString(), (it as CompoundButton).isChecked)
}
}
And provide stockListViewModel.updateFavourite code block. Is the problem reproduced when this line is commented out?
Calling stockListViewModel.deleteStock(stock) and stockListViewModel.updateFavourite(holder.symbolTextView.text.toString(), isChecked) in background thread like
withContext(Dispatchers.IO){stockListViewModel.deleteStock(stock)} might resolve the issue.

How do I use popBackStack() to return to a previous fragment in my Android app?

I'm trying to return back to a previous fragment from an activity but I cannot get the functionality to work.
Here is the relevant code from my activity class:
fun previousSubCountryListButtonClicked(view: View) {
val fragmentManager = supportFragmentManager
fragmentManager.popBackStack(R.id.navigation_scotland, POP_BACK_STACK_INCLUSIVE)
}
and here is my fragment class:
package com.riverstonetech.gositeuk.ui.scotland
import android.content.Intent
import android.content.Intent.getIntent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ProgressBar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.google.firebase.firestore.FirebaseFirestore
import com.riverstonetech.gositeuk.CountriesActivity
import com.riverstonetech.gositeuk.R
import com.riverstonetech.gositeuk.RegionActivity
import kotlinx.android.synthetic.main.fragment_scotland.*
class ScotlandFragment : Fragment() {
// Access a Cloud Firestore instance
val db = FirebaseFirestore.getInstance()
lateinit var adapter : ArrayAdapter<String>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_scotland, container, false)
(requireActivity() as CountriesActivity).initializeCustomActionBar(R.drawable.scotland_flag, R.string.title_regions)
var regions : ArrayList<String>
val docRef = db.collection("UKSites").document("Scotland")
val progressBar: ProgressBar = root.findViewById(R.id.regionsLoadingProgressBar)
docRef.get()
.addOnSuccessListener { document ->
progressBar?.visibility = ProgressBar.VISIBLE
if (document != null) {
regions = document.get("Regions") as ArrayList<String>
adapter = ArrayAdapter(requireContext(), R.layout.list_item, regions)
regionsListView.adapter = adapter
regionsListView.setOnItemClickListener { parent, view, position, id ->
val intent = Intent(activity!!, RegionActivity::class.java)
intent.putExtra("SUB_COUNTRY", regions[position])
startActivity(intent)
}
progressBar?.visibility = ProgressBar.GONE
} else {
Log.d("Debug", "No such document")
}
}
.addOnFailureListener { exception ->
Log.d("Debug", "get failed with ", exception)
}
return root
}
}
I am struggling to understand the official documentation on Android so I would appreciate any help possible. Do I need to add a transaction in my fragment class?
I used finish() instead of writing code to manipulate a fragment manager.

Kotlin recyclerview gave me null pointer exception

i started learing Kotlin after using Java, curently trying to create recyclerview using Kotlin, but everytime reach this code:
recyclerview!!.layoutManager = LinearlayoutManager(context)
inside a fragment, it always returns null for me, and by converting existing Java Code using converter in android studio returns error.Can anyone help me why this is always happened? code below is my current fragment code:
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_notes.*
import java.sql.Timestamp
class NotesFragment : Fragment() {
companion object {
fun newInstance(): NotesFragment {
return NotesFragment()
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
val messageList : MutableList<message> = prepareMessage()
recycler_view!!.layoutManager = LinearLayoutManager(context)
recycler_view!!.adapter = NotesAdapter(context)
(recycler_view!!.adapter as NotesAdapter).setMessage(messageList)
return view
}
private fun prepareMessage(): MutableList<message> {
var messageList : MutableList<message> = ArrayList()
for(i in 0..10){
val timestamp: Long = System.currentTimeMillis()/1000L
val message = message(0,
"Judul Catatan ke $i",
resources.getString(R.string.lipsum),
timestamp
)
messageList.add(message)
}
return messageList
}
}
Thats because the views are not created yet in onCreateView. Since you are using kotlinx.android.synthetic you need to access it inside onViewCreated.Like this
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recycler_view = view.findViewById(R.id.your_recycler_id)
recycler_view?.layoutManager = LinearLayoutManager(context)
recycler_view?.adapter = NotesAdapter(context)
val messageList : MutableList<message> = prepareMessage()
(recycler_view?.adapter as NotesAdapter).setMessage(messageList)
}
Try this...
You should declare your recyclerview object, before accessing their properties.
.....
private var recycler_view: RecyclerView? = null
.....
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater?.inflate(R.layout.fragment_notes, container, false)
recycler_view = view.findViewById(R.id.your_recycler_id)
recycler_view?.layoutManager = LinearLayoutManager(context)
recycler_view?.adapter = NotesAdapter(context)
val messageList : MutableList<message> = prepareMessage()
(recycler_view?.adapter as NotesAdapter).setMessage(messageList)
return view
}
have you declare your recylerview like this
polishrecyler = view .findViewById<View>(R.id.polish_recyler) as RecyclerView
I just now had a similar situation.
In my project, I have a RecyclerView working perfectly. In my case, it has five files involved: Foo.kt (the model), FooList.kt (the AppCompatActivity class), FooListAdapter.kt, foo_list.xml (the layout with the RecyclerView) and foo_list_row.xml (my custom row for the ReyclerView). Not sure if it's important or not, but the icon for Foo.kt is while the icon for the FooList.kt and FooListAdapter.kt is
I needed another RecyclerView, so I simply created five new files: Bar.kt, BarList.kit, BarListAdapter.kit, bar_list.xml and bar_list_row.xml. I copied/pasted the code from one to the other, and made the appropriate Foo->Bar changes in the code. This is when I got the same error as the OP. Looking in the import section, the import for RecyclerView was flagged as "superfluous". Odd.
Most of the suggested answers here is to declare the variable and use findViewById to reference it. But, my working RecyclerView doesn't do that, so why should it be necessary in the second RecyclerView? It doesn't make sense.
I continued to search and found this solution:
Save the code found in two files I had created manually (BarList.kit
(the AppCompatActivity file) and the 'corresponding' bar_list.xml)
Delete those files.
Right-click on the package name and select New > Activity > Empty Activity. This re-created the files I had just deleted with exactly the same names.
Restore the code saved from step 1 to the appropriate files.
Now both my RecyclerViews work correctly.
I guess when you use the New > Activity > Empty Activity, Android Studio does some work in the background which is not done if you just create the files manually.

Categories

Resources