I'm new to Kotlin and im trying to build a HomePage where there is a BottomNavigation with 3 Fragment pages but in 1 of the pages I set a ListView and whenever I call the ID it gives the following errer(Caused by: java.lang.IllegalStateException: categoryListView must not be null)
Here is how Im calling it in my HomePage which is where the content appears:
class HomePage : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
lateinit var adapter : CategoryAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home_page)
adapter = CategoryAdapter(this, DataService.categories)
categoryListView.adapter = adapter
navView.setNavigationItemSelectedListener(this)
bottomNavigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
replaceFragment(HomeFragment())
}
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.Bottom_Nav_Home -> {
println("Home pressed")
replaceFragment(HomeFragment())
return#OnNavigationItemSelectedListener true
}
R.id.Bottom_Nav_Notifs -> {
println("Notification pressed")
replaceFragment(NotifsFragment())
return#OnNavigationItemSelectedListener true
}
R.id.Bottom_Nav_List -> {
println("List pressed")
replaceFragment(ListFragment())
return#OnNavigationItemSelectedListener true
}
}
false
}
private fun replaceFragment(fragment: Fragment) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.NavPageFragment, fragment)
fragmentTransaction.commit()
}
}
am I doing it correct? or am I supposed to call the categoryListView elsewhere? because I tried in my ListFragment which is where my activity code is and it gave a context error
here is how the Fragment activity looks:
<?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:id="#+id/Bottom_List"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragments.ListFragment">
<ListView
android:id="#+id/categoryListView"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
You are trying to get the categoryListView resolved inside the activity but it is (as far as I understand) part of the fragment. This simply does not work.
The CategoryAdapter and the ListView should be used inside your HomeFragment class and this will work then.
So move this code into your fragment:
adapter = CategoryAdapter(this, DataService.categories)
categoryListView.adapter = adapter
Update
I would move it into the onViewCreated() method. There you can simply rely on the fact that there is a view available with a context.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
adapter = CategoryAdapter(view.context, DataService.categories)
categoryListView.adapter = adapter // categoryListView will be not null here
}
Related
I want to populate a recycler view with a realtime firebase database query. The recycler´s fragment is on a bottom nav (the second destiny). The layout:
<TextView
android:id="#+id/tv_id_owner_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_menu_owner"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tv_id_owner_menu"
app:layout_constraintVertical_bias="1.0" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/btn_add_item_menu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:contentDescription="#string/add_item"
android:src="#drawable/ic_add_item"
app:layout_constraintBottom_toBottomOf="#+id/rv_menu_owner"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
In my gradle:
implementation 'com.google.firebase:firebase-database:19.7.0'
implementation 'com.firebaseui:firebase-ui-database:2.3.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
kapt "androidx.lifecycle:lifecycle-compiler:2.3.1"
// ViewModel Kotlin support
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
//Koin
implementation "org.koin:koin-android:2.0.1"
implementation "org.koin:koin-androidx-viewmodel:2.0.1"
//Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
The code in fragment:
class MenuOwnerFragment : Fragment() {
private var _binding: FragmentMenuOwnerBinding? = null
private val binding get() = _binding!!
private val firebaseOwnerViewModel: FirebaseOwnerViewModel by inject()
lateinit var ownersMenuDb: DatabaseReference
private lateinit var menuRecyclerView: RecyclerView
private lateinit var firebaseRecyclerAdapter: FirebaseRecyclerAdapter<MenuOwner, MenusViewHolder>
private val coroutineScope = CoroutineScope(Dispatchers.Main)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
_binding = FragmentMenuOwnerBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
firebaseOwnerViewModel.currentOwnerLD.observe(viewLifecycleOwner, Observer {
binding.tvIdOwnerMenu.text = it.id
})
val ownerId = binding.tvIdOwnerMenu.text.toString()
menuRecyclerView = view.findViewById(R.id.rv_menu_owner)
menuRecyclerView.layoutManager = LinearLayoutManager(requireContext())
loadFirebaseData(ownerId)
binding.btnAddItemMenu.setOnClickListener { view ->
findNavController().navigate(R.id.action_navigation_menu_owner_to_itemMenuFragment)
}
}
private fun loadFirebaseData(ownerId: String) {
ownersMenuDb = FirebaseDatabase.getInstance().getReference("Menus").child(ownerId)
firebaseRecyclerAdapter = object : FirebaseRecyclerAdapter<MenuOwner, MenusViewHolder>(
MenuOwner::class.java,
R.layout.menu_row,
MenusViewHolder::class.java,
ownersMenuDb
) {
override fun populateViewHolder(viewHolder: MenusViewHolder?, model: MenuOwner?, p2: Int) {
viewHolder?.binding?.itemName?.text = model?.itemName
viewHolder?.binding?.itemDescription?.text = model?.itemDescription
viewHolder?.binding?.itemPrice?.text = model?.itemPrice.toString()
Glide.with(requireContext()).load(model?.itemPhoto).into(viewHolder!!.binding.itemImage)
}
}
menuRecyclerView.adapter = firebaseRecyclerAdapter
}
class MenusViewHolder(mview: View) : RecyclerView.ViewHolder(mview) {
val binding = MenuRowBinding.bind(mview)
}
}
It works and compiles and if I put .child("a value from my firestore child") instead of .child(ownerId), the result it´s what I expect.
With .child(ownerId), the textView shows the right value in the app, but the recycler shows "null". Debuger said "val ownerId = binding.tvIdOwnerMenu.text.toString()" is "". I don´t know what I´m missing because this way:
firebaseOwnerViewModel.currentOwnerLD.observe(viewLifecycleOwner, Observer {
binding.tvIdOwnerMenu.text = it.id
})
of passing parameters works fine for me in other fragments/activities. I´d try with sinthetics (instead of view binding), findViewById, coroutines (to delay loadFirebaseData(ownerId)), all the compile ways with/without parameter ownerId, the code onCreateView, onViewCreated, the FIFA, the UEFA... It´s my first post, sorry if I´m not clear. Any suggestion? Thanks in advance
I am working on an Android app which has to load all QR created image from external storage to the fragment which contains a RecyclerView. All the work goes fine, but as I tried to load the image from the specific path from external storage directory, it fails to load and throws an exception..
I am stuck with this as I have given the path to it like
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
val charItemcreate: CharItemsCreate = arrayList.get(position)
val path: String = Environment.getExternalStorageDirectory().toString() + "Maximus/QRCreated/"
val bitmap = BitmapFactory.decodeFile(path)
//holder.images = charItemcreate.imageload.toIcon()
}
Here is the Layout I Created For RecyclerView..
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constmain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints">
<ImageView
android:layout_width="#dimen/_62sdp"
android:layout_height="#dimen/_62sdp"
android:id="#+id/imgrecyclerviewcreate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/txt_nametext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#id/imgrecyclerviewcreate"
android:text="#string/email"/>
<TextView
android:id="#+id/txt_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#id/imgrecyclerviewcreate"
app:layout_constraintTop_toBottomOf="#+id/txt_nametext"
android:text="#string/email"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The Adapter Class I have Created where i have Given the Images Folder path
class AdapterCreateHistory(var context: Context, var arrayList: ArrayList<CharItemsCreate>) :
RecyclerView.Adapter<AdapterCreateHistory.ItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
val viewHolder = LayoutInflater.from(parent.context)
.inflate(R.layout.createhistoryitem, parent, false)
return ItemHolder(viewHolder)
}
override fun getItemCount(): Int {
return arrayList.size
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
val charItemcreate: CharItemsCreate = arrayList.get(position)
val path: String = Environment.getExternalStorageDirectory().toString() + "Maximus/QRCreated/"
val bitmap = BitmapFactory.decodeFile(path)
//holder.images = charItemcreate.imageload.toIcon()
}
class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var images = itemView.findViewById<ImageView>(R.id.imgrecyclerviewcreate)
}
Here is the Kotlin Model Class
class CharItemsCreate {
var imageload: Bitmap? = null
constructor(imageload: Bitmap) {
this.imageload = imageload
}
}
Here is the Main Fragment I am Working on. In Here I have Initialez the RecyclerView and the CharItemClass
class FragmentViewPagerScanHistory : Fragment() {
private var charItemcreate: ArrayList<CharItemsCreate>? = null
private var customAdaptercreate: AdapterCreateHistory? = null
private var gridLayoutManager: GridLayoutManager? = null
//val TYPE_SAVED = 15
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_view_pager_scan_history, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
gridLayoutManager = GridLayoutManager(context, 1, LinearLayoutManager.VERTICAL, false)
recyclerviewcreatehistory?.layoutManager = gridLayoutManager
recyclerviewcreatehistory?.setHasFixedSize(true)
charItemcreate = setImages()
customAdaptercreate = AdapterCreateHistory(this.context ?: return, charItemcreate!!)
recyclerViewfragment?.adapter = customAdaptercreate
}
private fun setImages(): ArrayList<CharItemsCreate>? {
return charItemcreate
}
I am Unable to Load Images into RecyclerView as I Am Getting Exception . I have Attached the Debugger With this It Also Give me the Null Values...
I have Also Given the Read Permission in the Manifest
Here is the Logcat After It Crashed
Hello you can use Glide or picasso library, that will handle exception in very appropriate way,
here is the implementation of Glide :-
implementation 'com.github.bumptech.glide:glide:4.11.0'
This is is the code block :-
Glide.with(context).load(yourpath).centerCrop().error(image that place while error occure).into(imageview);
Am trying to use a spinner in a layout. I can add and select items to the spinner but I cannot retrieve the selected item. This is the main activity:
class pageNewPurchaseOrder : AppCompatActivity() {
lateinit var spinnerVendorX: androidx.appcompat.widget.AppCompatSpinner
var spinnerArray: java.util.ArrayList<CharSequence> = ArrayList<CharSequence>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_page_new_purchase_order)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
spinnerVendorX = findViewById(R.id.spinnerVendorX)
spinnerVendorX.adapter = ArrayAdapter(this#pageNewPurchaseOrder, android.R.layout.simple_spinner_item, spinnerArray)
spinnerVendorX.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
Log.d("aaa", "This code is not running!")
}
}
}
}
This is the layout activity
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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:background="?attr/colorPrimary"
android:fillViewport="true"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".pageNewPurchaseOrder"
tools:showIn="#layout/activity_page_new_purchase_order">
<androidx.appcompat.widget.AppCompatSpinner
android:padding="10dp"
android:background="#drawable/bgselect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/lbl2"
android:id="#+id/spinnerVendorX" />
</androidx.core.widget.NestedScrollView>
Am getting result this on the logcat when I select an item: E/ViewRootImpl(31835): sendUserActionEvent() mView == null.I get tj=he same results even when I use <Spinner instead of <androidx.appcompat.widget.AppCompatSpinner
User must have to add data in the ArrayList. Here, you are initialising arrayList and set it but not adding data in ArrayList. Please add data in ArrayList then try it. Thank you.
How can I replace the fragment when i click on an item inside a recyclerView
I've tried inside onClickListener of adapter
childFragmentManager
.beginTransaction()
.replace(R.id.framefragment2, Fragment2())
.commit()
with no succes it returns me
java.lang.IllegalArgumentException: No view found for id 0x7f0a0137 (com.test.justaapp:id/framefragment1) for fragment Fragmetnone{1ebd5fc #0 id=0x7f0a0137}
Here is mi xml of 1st fragment already inflated
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/framefragment1"
android:layout_width="match_parent"
android:layout_height="match_parent">
and the second frame layout
<FrameLayout 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"
android:background="#C0C0C0">
<FrameLayout
android:id="#+id/framefragment2"
android:layout_width="match_parent"
android:layout_height="match_parent">
I've been searching for hours and can't get it to work, any ideas? maybe I'm mimssing something very simple, any help would be apreciated Greets.
You can implement an interface on your ViewHolder, then you pass an implementation of this interface to your adapter from your activity. In your adapter your pass the implementation when you create the CustomViewHolder instance (create method).
In your activity/fragment:
private val adapter = YourAdapter(object: CustomViewHolder.Listener {
fun onClick() {
// change the fragment here with the fragment manager of your activity/fragment.
}
})
In your adapter:
class YourAdapter(private val listener: CustomViewHolder.Listener): RecyclerView.Adapter {
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return CustomViewHolder(
LayoutInflater.from(context).inflate(R.layout.view_layout, parent, false),
listener
)
}
}
ViewHolder implementation:
class CustomViewHolder(view: View, private val listener: Listener): ViewHolder(view) {
...
interface Listener {
fun onClick()
}
fun onCreate(parent: View, listener: Listener): CustomViewHolder {
// inflate the view and return an instance of CustomViewHolder
}
fun onBind() {
button.setOnClickListener {
listener.onClick()
}
}
}
I didn't test this code but the logic is here. You just need to adapt it to your project.
I have a MvxFragment that is bound to a view model that has two ICommand properties defined. This fragment contains an MvxListView and can be part of different activities/layouts dependant on the device size/orientation.
What I want to know is how to specify the ItemClick event command of the MvxBind property of the MvxListView dynamically or is there a better way to handle this use case? Should I use a separate fragment?
A similar use case to the one I am trying to achieve is within the Overview section of this Xamarin Dev Guide
View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<MvxListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource Courses; ItemClick Comamnd1"
local:MvxItemTemplate="#layout/coursetemplate" />
</LinearLayout>
ViewModel (Simpified)
public class MyViewModel : MvxViewModel
{
private MvxCommand<MyMessage> messageCommand;
public MyViewModel (IMvxMessenger messenger, IHttpClientBuilderService httpClientBuilder)
{
this.httpClientBuilder = httpClientBuilder;
this.messenger = messenger;
}
public ICommand Comamnd1 {
get { return new MvxCommand<Course> ((c) => ShowViewModel<MyOtherViewModel>(c)); }
}
public ICommand Command2 {
get
{
messageCommand = messageCommand ?? new MvxCommand<MyMessage>(c =>
{
var message = new MyMessage(this, c);
messenger.Publish(message);
});
return selectedCourse;
}
}
}
OK, you can do this my overriding the OnActivityCreated event for the fragment as below.
public override void OnActivityCreated (Bundle savedInstanceState)
{
base.OnActivityCreated (savedInstanceState);
MvxListView list = Activity.FindViewById<MvxListView>(Resource.Id.[theID]);
if (Activity.FindViewById<View>(Resource.Id.[fragmentID]) != null)
list.ItemClick = ((MyViewModel)ViewModel).Command1;
else
list.ItemClick = ((MyViewModel)ViewModel).Command2;
}
You can pull out the list by using the Activity.FindViewById function and then set the appropraite ICommand or IMvxCommand from the ViewModel via the list.ItemClick event