0

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):

These buttons 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)

Lozminda
  • 45
  • 8
  • Sorry it's a bit long, have tried to make it as concise as possible whilst including necessary detail. If you need gradle files etc let me know. Thanks – Lozminda Jan 31 '23 at 18:39

0 Answers0