Call to Initialise and Display Fragment from Adapter Class from Buttons/Menu Generated at Runtime - android

I'm trying to write a simple runtime user interface that's generated from the contents of files read from the SD card. Am using a RecyclerView to generate buttons, the user can then make a selection (via the button) before moving onto the next menu on the next fragment. This program combines, RecyclerViews, Viewbinding, Fragments and Filehandling (the filehandling being the least important).
For example (I can't get a photo of the first fragment running unfortunately):
would be generated from this text file:
Priory Hotel, Victoria Street, Bristol
Landsdown Hotel, Landsdown, Bristol
The Hilton, Walcot Street, Bristol
MerryFields, Milsom Street, Bristol
Loz's Kitchen, St Jame's Square, Bristol
The top button would contain the text Priory Hotel, Victoria Street, Bristol the second button Landsdown Hotel, Landsdown, Bristol the 3rd The Hilton, Walcot Street, Bristol and so forth, the number of buttons displayed being the same as the number of lines (number of clients) of text.
I'm hoping to generate the 'button/menu interface' at runtime. The next fragment would be another list of buttons, also generated from a different text file (in the same way) and so on and so forth.
Is doing this even a good idea ? Should I be using Fragments ?
Am using Android Studio and developing for Android 5.1 (Lollipop I think). The error comes when I call the onClickListener in the Adapter Class. I just can't get this to work and have tried the following Stack Overflow Answers:
This doesn't work and isn't kotlin,
nor this (again kotlin but I can convert). Can't call supportFragmentManager
This is unanswered
I have read this, watched this and read these SO posts: 1, 2, 3, 4. 5. And finally this for good measure.
Adapter Class.kt
// I've not included the imports for brevity
class MyAdapter(private val aList : ArrayList<String>, private val destination: Int) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_item,
parent, false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
Log.d("Loz", "start onBindViewHolder()")
val currentItem = aList[position]
holder.buttonInRecyclerV.text = currentItem
val context = holder.buttonInRecyclerV.context
Log.d("Loz", "onBindViewHolder() before setOnClickLIstener")
// **************************************************************
// According to the error message provided by Logcat the crash is
// this line below. The 2 log messages in this function are being
// outputted too.
// **************************************************************
holder.buttonInRecyclerV.setOnClickListener {v ->
v.findNavController().navigate(destination)
// The toast message gets printed if I get rid of all the navigation
// and just run 1 fragment
Toast.makeText(context,
"${aList[position]} button pressed", Toast.LENGTH_LONG).show()
}
}
class MyViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView)
{
private val binding = ListItemBinding.bind(itemView)
val buttonInRecyclerV : Button = binding.clientsNameButton
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var clientArrayList: ArrayList<String>
private lateinit var productArrayList: ArrayList<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
Log.d("Loz", "before dataInit()")
dataInitialise(view)
Log.d("Loz", "after dataInit()")
replaceFragment(savedInstanceState, FirstFragment(), clientArrayList, "clientsListBundle")
Log.d("Loz", "after 1st frag Init()")
// The following may well be a load of twoddle, I was desparate when I wrote the
// code below. However if I just have:
// replaceFragment(savedInstanceState, SecondFragment(), productArrayList, "productListBundle)
// uncommented and comment out the first call of replaceFragment() the second fragment
// is created no problem...
if (FirstFragment().lifecycle.currentState == Lifecycle.State.RESUMED) {
replaceFragment(
savedInstanceState,
SecondFragment(),
productArrayList,
"productListBundle"
)
}
Log.d("Loz", "after 2nd frag Init()")
}
// This function (again) seems to work without the navigation elements
// in the app, Logs outputted.
private fun replaceFragment(savedInstanceState: Bundle?, aFragment: Fragment, aList: ArrayList<String>, TOKEN: String){
if (savedInstanceState == null) {
val fragmentManager = supportFragmentManager
val bundle = Bundle()
Log.d("Loz", "after replaceFragment()")
bundle.putStringArrayList(TOKEN, aList)
aFragment.arguments = bundle
fragmentManager.commit {
setReorderingAllowed(true)
replace(R.id.frameLayout, aFragment)
}
}
}
private fun listFromFile(view: View, filename :String) : MutableList<String> {
// Had no problems with this, and it works as expected when there's no navigation
// component to this app.
}
private fun dataInitialise(view: View) {
clientArrayList = listFromFile(view, "NEades/Client List.txt") as ArrayList<String>
productArrayList = listFromFile(view, "NEades/Product List.txt") as ArrayList<String>
}
}
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=".MainActivity">
<?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=".MainActivity">
<!-- If I put this in neither fragment works, if I take it out, both -->
<!-- do independently -->
<!-- <androidx.fragment.app.FragmentContainerView-->
<!-- android:id="#+id/fragment_container_view"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:navGraph="#navigation/nav_graph">-->
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#id/frameLayout"
app:layout_constraintBottom_toTopOf="#id/frameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</FrameLayout>
</androidx.fragment.app.FragmentContainerView>
<!-- </androidx.fragment.app.FragmentContainerView-->
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#id/frameLayout"
app:layout_constraintBottom_toTopOf="#id/frameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</FrameLayout>
</androidx.fragment.app.FragmentContainerView>
</androidx.constraintlayout.widget.ConstraintLayout>
first_fragment.xml
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="always"
android:id="#+id/FirstFragment">
<FrameLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:context=".FirstFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/list_item"/>
</FrameLayout>
</androidx.core.widget.NestedScrollView>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingTop="1dp"
android:layout_marginHorizontal="2dp"
android:background="#color/white">
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/clientsNameButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:text="#string/default_value"
android:textSize="11sp"
android:textStyle="bold"
android:layout_marginStart="16dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"
android:background="#color/underline"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Eventually, if this is possible, the goal is to refactor even more.
My question: (other than should I be using activities other than fragments ?) is how to 'call' the second fragment from any of the buttons in the first fragment (from the Adapter class ?) so that the second fragment then initialises and displays it's contents (retrieved from a text file)

Related

How can we create a card stackview in Android using android native stackview?

This is what I created
And what I want is like this below
Actually in here, I want my cards should look like cards in a wallet (as shown in the 2nd image above) which I can scroll like the native stack card view as shown in first image, problem I am facing is I am not able to align cards vertically straight, cards get cut if I try to do so.
I want to achieve this behaviour using Android Native stackview widget only.
My code is as below:
ActivityMain.kt
class MainActivity : AppCompatActivity() {
lateinit var cardAdapter: CardAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val stackView = findViewById<StackView>(R.id.stack_view)
cardAdapter= CardAdapter(numberWord(),numberImage(),R.layout.item_card,this)
stackView.adapter=cardAdapter
}
// the method numberWord() is used to
// add the text below corresponding images.
private fun numberWord(): List<String> {
val word: MutableList<String> = ArrayList()
word.add("One")
word.add("Two")
word.add("Three")
word.add("Four")
word.add("Five")
return word
}
// the method numberWord() is used to call
// the images that are added to the stack.
private fun numberImage(): List<Int>{
val image: MutableList<Int> = ArrayList()
image.add(R.drawable.ic_filter)
image.add(R.drawable.ic_filter)
image.add(R.drawable.ic_filter)
image.add(R.drawable.ic_filter)
image.add(R.drawable.ic_filter)
return image
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<!--We have used RelativeLayout for layout-->
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<!--Add StackView-->
<StackView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="#+id/stack_view"
android:layoutDirection="ltr"
android:layout_width="400dp"
android:layout_height="300dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
CardAdapter.kt
class CardAdapter
(
var numberWord: List<String>,
var numberImage: List<Int>,
var itemLayout: Int,
var c: Context
) : ArrayAdapter<Any?>(
c, itemLayout, numberWord
) {
// getCount() is called to return
// the total number of words to be used
override fun getCount(): Int {
return numberWord.size
}
// getView() is called to get position,
// parent and view of the images.
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var convertView = convertView
if (convertView == null) {
convertView = LayoutInflater.from(parent.context).inflate(itemLayout, parent, false)
}
val word = numberWord[position]
val image = numberImage[position]
val textView = convertView!!.findViewById<TextView>(R.id.text_view)
val imageView = convertView.findViewById<ImageView>(R.id.image_view)
textView.text = word
imageView.setImageResource(image)
return convertView
}
}
item_card.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="180dp"
android:background="#drawable/border_bg"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_filter" />
<TextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:textStyle="bold" />
</LinearLayout>
you guys can refer android native stackview: https://developer.android.com/reference/android/widget/StackView
I tried many different solution, I guess we can achieve that with third party library but that's not what I want. I want to create solution with using native android stackview.
Any help will be great.

Android - ActivityMainBinding shows me wrong activity

everyone. I'm trying to display a line chart in a full-screen activity. Unfortunately I can't do that. I always get to see my activity_main.xml layout. I also have my LineChart in it, within a CardView. Everything works fine in this activity. But as soon as I switch to activity_details_vitali.xml via a button, I don't see a line chart, just my main_activity. I think I have a wrong binding here. However, I can't find the error
class DetailsVitali : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_details_vitali)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setLineChartData()
}
private fun setLineChartData() {
val linevalues = ArrayList<Entry>()
linevalues.add(Entry(20f, 0.0F))
linevalues.add(Entry(30f, 3.0F))
linevalues.add(Entry(40f, 2.0F))
linevalues.add(Entry(50f, 1.0F))
linevalues.add(Entry(60f, 8.0F))
linevalues.add(Entry(70f, 10.0F))
linevalues.add(Entry(80f, 1.0F))
linevalues.add(Entry(90f, 2.0F))
linevalues.add(Entry(100f, 5.0F))
linevalues.add(Entry(110f, 1.0F))
linevalues.add(Entry(120f, 20.0F))
linevalues.add(Entry(130f, 40.0F))
linevalues.add(Entry(140f, 50.0F))
val linedataset = LineDataSet(linevalues, "First")
//We add features to our chart
linedataset.color = resources.getColor(R.color.purple_200)
linedataset.circleRadius = 5f
linedataset.setDrawFilled(true)
linedataset.valueTextSize = 10F
linedataset.fillColor = resources.getColor(R.color.purple_500)
linedataset.setMode(LineDataSet.Mode.CUBIC_BEZIER);
//We connect our data to the UI Screen
val data = LineData(linedataset)
binding.getTheGraph.data = data
binding.getTheGraph.setBackgroundColor(resources.getColor(R.color.white))
binding.getTheGraph.animateXY(2000, 2000, Easing.EaseInCubic)
}
layout
<?xml version="1.0" encoding="utf-8"?>
LinearLayout 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:orientation="horizontal"
tools:context=".DetailsVitali">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
app:cardCornerRadius="10dp"
android:elevation="20dp"
android:layout_gravity="center_horizontal">
<com.github.mikephil.charting.charts.LineChart
android:id="#+id/graphDetail"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
EDIT
And I also don't have access to the ID of my LineChart in the DetailsVitali.class. binding.ID.data = data is the ID is from the activity_main.xml.
From your comment that your desired layout is defined in activity_details_vitali.xml, you should be loading ActivityDetailsVitaliBinding, not ActivityMainBinding.
And as mentioned by #ARiF, you can remove the first call to setContentView since you are overwriting it with the second call to setContentView.

Recyclerview view not populating

I'm a Kotlin newbie learning how to create simple recyclerview apps. My code is supposed to list the integers 1..10 in vertically stacked cells. However, it only lists the first item. I've consulted several tutorials and reviewed my code several times(after long breaks), but I can't see anything wrong in my code.
I got the bright idea early today to print Log statements. Examining them, I note that my onBindViewHolder function is only called once. What blunder am I making?
Here is my log output:
D/QuoteAdapter: value is: 1
D/QuoteAdapter: index is: 0
D/QuoteAdapter: Size is: 10
my activity:
class MainActivity : AppCompatActivity() {
lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mRecyclerView = findViewById(R.id.recyclerView)
mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.adapter = QuoteAdapter()
//mRecyclerView.setHasFixedSize(true)
}
}
my adapter:
class QuoteAdapter : RecyclerView.Adapter<QuoteViewHolder>() {
private val listOfInts = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
private val TAG = "QuoteAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item_row, parent, false)
return QuoteViewHolder(view)
}
override fun getItemCount(): Int {
Log.d(TAG, "Size is: ${listOfInts.size.toString()}")
return listOfInts.size
}
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
val item = listOfInts[position]
Log.d(TAG, "value is: ${item.toString()}")
Log.d(TAG, "index is: ${position.toString()}")
holder.listTitle.text = item.toString()
}
}
my viewholder:
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val listTitle = itemView.findViewById(R.id.itemString) as TextView
}
my layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
my main layout is shown below:
<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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
In your "my layout" try this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Notice layout_height of LinearLayout has changed to wrap_content
Also doubt you need the android:orientation="vertical" on your ViewHolders item xml unless you will add more than just 1 TextView in the future.
Like Zain says, you can just use a TextView on its own in a layout file, which will also fix the problem (so long as its height is wrap_content!)
There are actually a few included with Android - type android.R.layout. and you'll see a few things, like simple_list_item_1 which is just a styled TextView (you can ctrl+click the reference or whatever to look at the file). Can be nice if you just want to make a quick thing!
The ID of the TextView in android.R.layout.simple_list_item_1 is #android:id/text1 - note the android prefix, because its part of the android resources, not your app's. Which means you have to reference the ID in the same way as the layout, with android at the front: android.R.id.text1
You can get rid of the LinearLayout in the list item layout, and only keep the TextView.
So, replace your list item layout with:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

ViewPager child to swipe next the parent

I have a ViewPager2 which I'm using with a RecyclerView Adapter. I'm binding the children of each viewpager item via the ViewHolder and everything is working okay.
I have a bunch of elements in this ViewPager item XML, some RadioButton components and a Button. I want this button to move it to the next item.
I know how to do that externally, assigning a sibling to the ViewPager2 and then setting a click-listener in the activity and then comparing currentItem of the ViewPager with the adapter's total item count.
I want the "Move to next" button inside the ViewPager as I want to change it based on the inputs supplied to the RadioButton components.
I'm currently stuck at failing getting the ViewPager item's button in the activity to set the click-listener. Is there any workaround to get the child element via the adapter itself?
Here's the activity_quiz.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=".ui.quiz.view.QuizActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/quiz_list"
android:layout_width="match_parent"
android:layout_height="600dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="16dp"
tools:listitem="#layout/quiz_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
The quiz_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:padding="#dimen/bigSpacing"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/question"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choices">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_1"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_2"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_3"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_4"/>
</RadioGroup>
<Button
android:id="#+id/result_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Next question" />
</LinearLayout>
The MyAdapter class (kotlin)
class MyAdapter: RecyclerView.Adapter<MyAdapter.MyViewHolder> {
override fun onCreateViewHolder(...) {
...
}
override fun getItemCount(): Int {
...
}
override fun onBindViewHolder(...) {
...
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
fun bind(someData: SomeData) {
itemView.question.text = somedata.question
itemView.choice_1.text = somedata.choice_1
itemView.choice_2.text = somedata.choice_2
itemView.choice_3.text = somedata.choice_3
itemView.choice_4.text = somedata.choice_4
val answerKey = someData.answerKey
var rightOrWrong = ""
itemView.choices.setOnCheckedChangeListener {_, checkedID ->
val checkedIndex: Int = itemView.choices.indexOfChild(itemView.choices.findViewById(checkedID))
if(checkedIndex + 1 == answerKey.toInt()) {
rightOrWrong = "Correct! Next Question."
} else {
rightOrWrong = "Incorrect :/ Next Question."
}
itemView.result_button.text = rightOrWrong
}
}
}
And the QuizActivity.kt file
class QuizActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quiz)
val myAdapter = MyAdapter()
quiz_list.adapter = myAdapter
// quiz_list.result_button.setOnClickListener {
// No idea how to get the children correctly, as this one throws errors
// }
}
}
First, why do you want to use RecyclerView.Adapter<MyAdapter.MyViewHolder> as an adapter for viewpager2? There is FragmentStateAdapter a built-in class implementation of PagerAdapter that uses a Fragment to manage each page, which is recommended for viewpager2.
Second, you are not inflating the views in MyViewHolder, I don't know if you left them intentionally for this post.
You cant access child views like this quiz_list.result_button.setOnClickListener {} while using something like RecyclerView, Viewpagger. You can access them in the ViewHolder after you inflate them, you can set any listener there aswell

Why my foreach breaks when I try to draw dynamic buttons in Kotlin?

I need to draw dynamic buttons inside a foreach loop that retrieve data from my anko sqlite, the foreach only enter once and breaks and only draw one button in my layout, what I doing wrong? my code is this:
fun loadZones (ctx: Context, update: String, view: View, layout: LinearLayout) {
val zonesParser = rowParser{idzone: Int, zone: String -> Pair(idzone, zone)}
for (it in ctx.database.use {
select("tableplan")
.distinct()
.column("idzone")
.column("zone")
.orderBy("zone")
.parseList(zonesParser)
}) {
val layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
val btnZone = layoutInflater.inflate(R.layout.zones_item, null) as MaterialButton
btnZone.text = it.second
btnZone.id = it.first
layout.addView(btnZone, layoutParams)
Log.e("PAIR", "FIN DEL CICLO")
continue
}
}
The data that retrieves from my query is this:
(2, LARRY)
(1, MADISON)
That's my activity, I need to draw the buttons in "lytZonesButtons" id
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".TablePlanFragment">
<com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:elevation="2dp"
tools:targetApi="lollipop" app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbarTablePlan"
style="#style/com.madison.Toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="#string/table_title_module">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_marginTop="56dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:orientation="horizontal"
android:background="#color/orangeLighter"
android:gravity="center_vertical"
android:padding="5dp" android:id="#+id/lytZonesButtons" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="112dp"
android:padding="5dp">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rc_tableplan"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.core.widget.NestedScrollView>
</FrameLayout>
and that's my button template that I called "zones_item":
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.button.MaterialButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" style="#style/com.madison.AppButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.MaterialComponents.Subtitle2"
tools:text="MADISON"
tools:targetApi="lollipop"
android:layout_margin="5dp"
/>
EDIT: I found the solution!
I don't now why my layout instance in the twice iteration of my loop throws NullPointerException but not shows in the log cat, my solution was put the loop code in onCreateView function, this is the code:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.activity_tableplan, container, false)
val iActivity = (activity as AppCompatActivity)
iActivity.setSupportActionBar(view.toolbarTablePlan)
iActivity.supportActionBar?.setDisplayShowTitleEnabled(true)
// view.rc_tableplan.setHasFixedSize(true)
// val gridLayoutManager = GridLayoutManager(context, 2, GridLayoutManager.HORIZONTAL, false)
// view.rc_tableplan.layoutManager = gridLayoutManager
val response = loadTablePlan(this.context!!, "no")
if (response.trim().toUpperCase() == "SUCCESS") {
val zonesParser = rowParser{idzone: Int, zone: String -> Pair(idzone, zone)}
for (zone in this.context!!.database.use {
select("tableplan")
.distinct()
.column("idzone")
.column("zone")
.orderBy("zone")
.parseList(zonesParser)
}) {
val layout:LinearLayout = view.lytZonesButtons
layout.let {
val btnZone = layoutInflater.inflate(R.layout.zones_item, layout, false) as MaterialButton
btnZone.text = zone.second
btnZone.id = zone.first
btnZone.requestLayout()
layout.addView(btnZone)
Log.e("PAIR", "FIN DEL CICLO")
}
}
}
return view
}
Thanks a lot for all people that tried help me, some admin can close my question please.
The hint is only one button is showing. Your trying to inflate the same view twice in the same spot.
You need to add an empty linearlayout in your xml. And in your loop change the buttonz..
var btnZone = findViewById(R.layout.btnZone)
button.text = "Pair"
btnZone.addView(button, layoutParams)
That's not the exact code (and probably not even the right syntax) but it shows you how you need to modify your loop.
Basicly you were attempting to inflate the same instance of the same view. When really your not inflating any views this way your just adding views.
Note
If you have a linearlayout in your xml when you add another button view to it it will add it below it. If you set the layout orientation to horizontal the button view then gets added beside the other one.
here's a link to an example.
Sorry I would make sure my code matched your code and variables with proper syntax but I am at work.

Categories

Resources