Inflate multiples views into a fragment - android-layout

I was working on my app and i need to make a list of little groups of views like this :
(i couldn't post my image 'cause StackOverflow thing i'm not famous enought)
IMAGE
The fragment is shown , and correctly applied but the view group that i'm trying to display doesn't.
I can't find how to make my fragment apprear and make the buttons work (But that's not the point now , so I'll ask the question later)
class EditFragment : Fragment() {
lateinit var option : Spinner
lateinit var result : TextView
private lateinit var viewOfLayout: View
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewOfLayout = inflater.inflate(R.layout.edit_fragment, container, false)
val mainLayout = viewOfLayout.findViewById(R.id.scrollView1) as ScrollView
//create a view to inflate the layout_item (the xml with the textView created before)
val view = layoutInflater.inflate(R.layout.sensor_item, mainLayout, false)
val options = arrayOf("A","V")
option = view.spinner as Spinner
result = view.textView7 as TextView
option.adapter = ArrayAdapter<String>(activity,android.R.layout.simple_list_item_1,options)
return viewOfLayout
}
}
Here is the full code : https://pastebin.com/fYyXkupM
(I precise that my fragment is displaying properly)
-> I think my error is comming from there :
val view = layoutInflater.inflate(R.layout.sensor_item, mainLayout, false)
if i replace "mainLayout" by "container" it does works but it's not the fragment's child.
Thank you.

You haven't initialized option Spinner from viewOfLayout.

ok , even if that's a little specific , i found my answer :
val view = layoutInflater.inflate(R.layout.sensor_item, container, false)
val insertPoint = viewOfLayout.findViewById(R.id.box_Parent) as LinearLayout
insertPoint.addView(view2, 0)

Related

Invisible mode in Android studio (kotlin)

I'm working on a news app and I want my layout to become invisible when a user saves an article. Can anyone please tell me the right way to write this code?
2 things to note:
-when I run the app,the layout is visible in the "saved fragment"
but then when I add "hideSavedMessage" right next to the code that updates the recyclerView and I run the app, the layout becomes invisible.
I want the layout to be invisible only when the user saves an article.
PS: I know how the visible and invisible mode works. I have used it before. My major problem is not knowing the right place to write the code. And by layout, I mean the text view and image view that appears on the screen. I would appreciate any contributions. Thank you.
Here's my code
class SavedFragment : Fragment(R.layout.fragment_saved) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: SavedAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as NewsActivity).viewModel
setupRecyclerView()
newsAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article", it)
}
findNavController().navigate(
R.id.action_savedFragment_to_savedArticleFragment,
bundle
)
}
val itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position =
viewHolder.adapterPosition//get position of item we deleted so that we swipe to left or right
val article =
newsAdapter.differ.currentList[position]//from news adapter at the index of the position
viewModel.deleteArticle(article)
Snackbar.make(view, "Successfully deleted article", Snackbar.LENGTH_LONG)
.apply {
setAction("Undo") {
viewModel.saveArticle(article); hideSavedMessage()
}
show()
}
val isAtLastItem = position <= 0
val shouldUpdateLayout = isAtLastItem
if (shouldUpdateLayout) {
showSavedMessage()
}
}
}
ItemTouchHelper(itemTouchHelperCallback).apply {
attachToRecyclerView(rvSavedNews)
}
viewModel.getSavedNews().observe(viewLifecycleOwner, Observer { articles ->
newsAdapter.differ.submitList(articles)
})
}
private fun setupRecyclerView() {
newsAdapter = SavedAdapter()
rvSavedNews.apply {
adapter = newsAdapter
layoutManager = LinearLayoutManager(activity)
}
}
private fun hideSavedMessage() {
savedMessage.visibility = View.INVISIBLE
isArticleAdded = false
}
private fun showSavedMessage() {
savedMessage.visibility = View.VISIBLE
}
}
The problem is that code inside observer runs even at the beginning - when you run your app, right? If I understand your problem, you just have to manage to make the fun hideSavedMessage() not be running for the first time. You could for example instantiate a boolean in onCreate() and set it to false. Then, inside the observer, you could run the hideSavedMessage() only if that boolean is true - you would set it as true at the end of the observer. I hope you understand.

Navigate to a new instance of the same Fragment type after Jetpack Navigation 2.4.x update

I'm updating Jetpack Navigation from 2.3.4 to 2.4.2 and believe I have found a breaking change in my project.
Let's say I have a MainFragment and a SecondFragment. Launching SecondFragment from MainFragment (with safeArgs) works as expected. However, I also need to be able to launch a new instance of SecondFragment from SecondFragment with new arguments. I'm constructing a new navigation action and telling the navController to navigate to it. Before updating, I could see that this navigation action was taken, and I hit onCreateView and other lifecycle methods in my SecondFragment class.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button = view.findViewById<Button>(R.id.myButton)
button.setOnClickListener {
val action = SecondFragmentDirections.newSecondFragment("coming from second")
Log.d("SecondFragment", "click ${action.arguments.get("myArg")}")
findNavController().navigate(action)
}
val text = view.findViewById<TextView>(R.id.message)
text.text = safeArgs.myArg
}
My log statement there is being logged and is correct, but it seems like the navigation action never happens, as I'm never hitting onCreateView or onViewCreated, and my SecondFragment does not update with the new arguments.
I have a small reproducible example here: https://github.com/rahobbs/NavExperiments/tree/main/app/src/main/java/com/example/navexperiments

how to change the text value in a textview, whilst in a fragment. Kotlin

Does anyone know of a way to change the value of a textview text, whilst in a fragment. I have tried findviewbyid, however i believe that this does not work whilst in a fragement. Does anyone have an idea on how to identify a textview and change the text value of it. Thanks
class Home : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_home, container, false)
val queue = Volley.newRequestQueue(view.context)
val url = "https://io.adafruit.com/api/v2/alex9301/feeds/test-data/data" //URL
var urlStuff = ""
val stringRequest = StringRequest(Request.Method.GET, url,
{ response ->
// Display the first 500 characters of the response string.
urlStuff = response
val jsonArray = JSONTokener(urlStuff).nextValue() as JSONArray
val id = jsonArray.getJSONObject(0).getString("value") // Get data
Log.i("Val: ", id)
//Updating info on app
val textView = view.findViewById(R.id.temp)
textView.text = "My Text"
},
{ Log.i("b", "That didn't work!") })
queue.add(stringRequest)
return view
}
}
findViewById also works in a fragment, like this:
val view = inflater.inflate(R.layout.fragment_home, container, false)
val textView: TextView = view.findViewById(R.id.temp)
textView.text = "My Text"

How to open a different activity on recyclerView in fragment?

I am using a recyclerView to show my listItems in the home fragment. But I have been stuck on how to open a different activity when items are clicked. How can I do for the next steps? Could you please help me to solve the issue? I would appreciate the help.
HomeFragment.kt
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
lateinit var recycle1:RecyclerView
private val list = ArrayList<Locations>()
private val adapter:Adapter = Adapter(list)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var v =inflater.inflate(R.layout.fragment_home, container, false)
recycle1 = v.findViewById(R.id.rv_item)
recycle1.layoutManager = LinearLayoutManager(activity)
list.clear()
testData()
val adapterr = Adapter(list)
recycle1.adapter = adapterr
adapter.notifyDataSetChanged()
recycle1.setHasFixedSize(true)
return v
}
private fun testData(){
list.add(Locations(R.drawable.book_cafe,"The Book Cafe","20 Martin Rd","Cafe"))
list.add(Locations(R.drawable.citysprouts,"City Sprouts","102 Henderson Road","Cafe"))
list.add(Locations(R.drawable.esplanade,"Library#esplande","8 Raffles Ave","Library"))
list.add(Locations(R.drawable.hf,"Library#Harbourfront","1 HarbourFront Walk","Library"))
list.add(Locations(R.drawable.mangawork,"MangaWork","291 Serangoon Rd","Cafe"))
list.add(Locations(R.drawable.orchard,"Library#orchard","277 Orchard Road","Library"))
list.add(Locations(R.drawable.rabbitandfox,"Rabbit&Fox","160 Orchard Rd","Cafe"))
list.add(Locations(R.drawable.sixlettercoffee,"T6 Letter Coffee","259 Tanjong Katong Rd","Cafe"))
}
}
Adapter.kt
class Adapter (val listItem:ArrayList<Locations>) : RecyclerView.Adapter<Adapter.RecycleViewHolder>(){
inner class RecycleViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
val itemImage: ShapeableImageView = itemView.findViewById(R.id.item_image)
val heading: TextView = itemView.findViewById(R.id.item_title)
val detail: TextView = itemView.findViewById(R.id.item_detail)
val category: TextView = itemView.findViewById(R.id.item_categories)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecycleViewHolder {
val view:View = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return RecycleViewHolder(view)
}
override fun getItemCount(): Int {
return listItem.size
}
override fun onBindViewHolder(holder: RecycleViewHolder, position: Int) {
val item = listItem[position]
holder.itemImage.setImageResource(item.itemImage)
holder.heading.text = item.headings
holder.detail.text = item.detail
holder.category.text = item.category
}
Locations. kt
data class Locations(var itemImage:Int,var headings :String,var detail :String,var category :String)
You have to take help of an interface to navigate :
First Create an interface , Suppose name it Navigate
interface Navigate {
fun onRecyclerViewItemClicked(location : Location)
}
Then you have to make use of this interface in the adapter as well as the fragment :
In your adapter , you have to do the following :
class Adapter (val listItem:ArrayList<Locations>) : RecyclerView.Adapter<Adapter.RecycleViewHolder>(){
//Declare listener object
var listener: Navigate? = null
inner class RecycleViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
val itemImage: ShapeableImageView = itemView.findViewById(R.id.item_image)
val heading: TextView = itemView.findViewById(R.id.item_title)
val detail: TextView = itemView.findViewById(R.id.item_detail)
val category: TextView = itemView.findViewById(R.id.item_categories)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecycleViewHolder {
val view:View = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return RecycleViewHolder(view)
}
override fun getItemCount(): Int {
return listItem.size
}
override fun onBindViewHolder(holder: RecycleViewHolder, position: Int) {
val item = listItem[position]
holder.itemImage.setImageResource(item.itemImage)
holder.heading.text = item.headings
holder.detail.text = item.detail
holder.category.text = item.category
// Now suppose you want to navigate to another fragment / Activity on click of the itemImage , then do the following
holder.itemImage.setOnClickListener{
listener?.onRecyclerViewItemClicked(item)
}
}
Now in your fragment extend the Navigate Class and implement the override the methods :
class HomeFragment : Fragment() , Navigate {
// Rest of your code
override fun onRecyclerViewItemClicked(location : Location){
// The argument location here is the information of the item that is clicked of
// the RecyclerView
// Here write your code to navigate to the next fragment / activity based on what
//you are using NavController / Fragment Manager
}
}
Set a clickListener in init {} block of your view holder
For eg.
inner class RecycleViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
val itemImage: ShapeableImageView = itemView.findViewById(R.id.item_image)
val heading: TextView = itemView.findViewById(R.id.item_title)
val detail: TextView = itemView.findViewById(R.id.item_detail)
val category: TextView = itemView.findViewById(R.id.item_categories)
init {
itemView.setOnClickListener { // start your activity using view context }
}
}
Never set your click listener in onBindViewHolder() method as click listener will set multiple times because this method method is called every time items are bound in recycler view.
Reference official doc

Hot to navigate to activity from fragment

This is my first post, so I will try to explain myself the best I can.
This is my first project in android with kotlin, and Im creating a simple app with 3 tabs. Im trying to make a simple navigation from a button in one of the 3 fragments associated with the 3 tab views to a new activity. This button have the function "push" associated.
I attach the code from the fragment, where you can see the function to navigate "push" which start the new activity. If I place that function in the onCreateView section, it works fine when I enter the fragment. I had no problem doing this navigation from an activity to another using a similar button and function but i dont know how fragments properly works, so i guess there is a thing relative to them i am missing. It seems like the function is not recognized outside the onCreate section, so maybe i have to declare it in another way:
class NotificationsFragment : Fragment() {
private var _binding: FragmentNotificationsBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
return binding.root
}
private val binding get() = _binding!!
fun push(view: View) {
val intent = Intent(activity, tutorialPrueba::class.java)
startActivity(intent)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
I was not able to find any solution that helps me in this case. As long this is my first post, i will thank any help or recommendation relative to the way I posted or how i can make it better. Also sorry for my poor english.
Thanks in advance.
If you are using the DataBinding, the onClick declared in the layout xml does not work. So to make the button, once pressed, call your method you have to do this:
private var _binding: FragmentNotificationsBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNotificationsBinding.inflate(inflater, container, false)
_binding.idOfButtonToPress.setOnClickListener{ //replace idOfButtonToPress with the real android:id of button
push()
}
return binding.root
}
private val binding get() = _binding!!
fun push() {
val intent = Intent(activity, tutorialPrueba::class.java)
startActivity(intent)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

Resources