cant find a way to insert RecycleView inside fragment (Kotlin) - android

I'm am new to Kotlin I try to create a Shopping List app and I encountered a problem with a RecycleView inside a fragment.
In the fragment, I have a button that a user click and adds Item to the list but I got error when I tried to add the layout manager inside the OnCreateView function.
I tried this:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_shopping_list, container, false)
rv_shoppinglist.layoutManager = LinearLayoutManager(context) \\ this is where i get ERR
return view
}
and i got an ERR -> java.lang.IllegalStateException: rv_shoppinglist must not be null
So i added this line rv_shoppinglist.layoutManager = LinearLayoutManager(context)
on different function
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
rv_shoppinglist.layoutManager = LinearLayoutManager(activity)
}
and now the app works and when I user add the item to the list it does show the items on the RecycleView but instead of displaying them line underline it's displaying them with a screen size gap
Image:
edit:
This is the XML file for RV_CHILD:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
And this is an XML file for shopping_fragment
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".fragments.shopping_list">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_shoppinglist"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab_shoppiglist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="#drawable/ic_plus"
android:layout_margin="16dp" />
</RelativeLayout>
</FrameLayout>

You need to set relative layouts height to wrap_content otherwise it will take up the whole screen
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tv_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>

As a commenter pointed out, it appears that your viewholder layout height (RV_CHILD in your case) is set to match_parent when it should probably be wrap_content

Related

Show remaining views depending on radio button selection

I am working on an android app whereby I have a radio button, and depending on the selection of radio button, I want to show either of the two set of input views i.e. if radio button 1 is selected, I want to show a different set of input views, and if radion button 2 is selected, I want to show a different set of input views.
Till now what I've done is that in my activity, I have a radio button, and then I am creating both set of views (but not visible), and when either of radio button is selected, I make one set of views visible.
I believe that is not the "standard" way of doing it, so seeking help on what would be ideal in this case?
Shall I change the workflow to open a new activity depending on which radio button is selected?
The best approach is to separate each layout and its logic to separate files.
The best solution to do that is to use Fragments and each fragment will have its own layout xml file and Kotlin/Java class for coding.
Implementation
First, you have to create two fragments.
FirstFragment.kt
class FirstFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}
}
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<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="#336699"
tools:context=".FirstFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="First Fragment"
android:textColor="#color/white"
android:textSize="24sp"
android:textStyle="bold"/>
</FrameLayout>
SecondFragment.kt
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_second, container, false)
}
}
fragment_second.xml
<?xml version="1.0" encoding="utf-8"?>
<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="#996633"
tools:context=".SecondFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Second Fragment"
android:textColor="#color/white"
android:textSize="24sp"
android:textStyle="bold"/>
</FrameLayout>
Now, in your activity you need to create the RadioGroup with two RadioButtons, then add FragmentContainerView which will be the container for all fragments.
activity_main.xml
<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">
<RadioGroup
android:id="#+id/radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:orientation="horizontal"
android:gravity="center"
android:background="#cccccc"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<RadioButton
android:id="#+id/radio_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View 1"/>
<RadioButton
android:id="#+id/radio_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="View 2"/>
</RadioGroup>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/radio_group"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The final step is to implement the fragment changes in activity when the user changes the RadioButton selection.
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var radioGroup : RadioGroup
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
radioGroup = findViewById(R.id.radio_group)
radioGroup.setOnCheckedChangeListener { _, checkedId ->
when(checkedId){
R.id.radio_1 -> supportFragmentManager.beginTransaction().replace(R.id.fragment_container, FirstFragment()).commit()
R.id.radio_2 -> supportFragmentManager.beginTransaction().replace(R.id.fragment_container, SecondFragment()).commit()
}
}
radioGroup.check(R.id.radio_1) // To check the first radio by default
}
}
Results
When the first RadioButton is selected
When the second RadioButton is selected

Loading Dialog will not go transparent

I have surprisingly been stuck on this one for a little while.
User Story:
The user should see a Loading Dialog that can be reused through the application with a transparent background so you only see the progress spinner and the text under the progress spinner.
Currently, I have a DialogFragment that inflates this XML to present itself:
<?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="wrap_content"
android:background="#android:color/transparent">
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1"
android:background="#android:color/transparent"
android:indeterminateDrawable="#drawable/loading_spinner"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:alpha="1"
android:background="#android:color/transparent"
android:text="Loading..."
android:textColor="#FFFFFF"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/progressBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
I am trying to set the transparency in the background and have had these results:
alpha set changes children elements to transparent as well
above XML setting does nothing and shows a white background
Setting it programmatically(See below) also does nothing and displays it white.
LoadingDialog():
class LoadingDialog(): DialogFragment() {
private var _binding: FragmentLoadingDialogBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.dialog?.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState:Bundle?
): View? {
_binding = FragmentLoadingDialogBinding.inflate(inflater, container, false)
return binding.root
}
//Always do this in Dialog to maintain memory management
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
How can I get the above LoadingDialog to present the Loading Progress Spinner and the Text without the white background?
I think you should call onCreate() method before setting the background to transparent after which you should set the view for your dialog. Try this
val dialogBinding = // inflate dialog here using dataBinding for example
val customDialog = AlertDialog.Builder(this, 0).create() // works with other dialogs as well
customDialog.apply {
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setView(dialogBinding?.root)
}.show()
I hope this helps you solve your problem. For more information about Dialogs in Android, please refer to my article on section.io
something else, consider removing the transparent background on your root viewGroup as shown below
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent">
edit to
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="wrap_content">

How to use binding in both a fragment and within its included layout?

Say I have a fragment defined as:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="cpaTabViewModel"
type="org.romco.example.CpaTabViewModel" />
</data>
<LinearLayout
android:id="#+id/fragment_pac_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/testTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#{cpaTabViewModel.testString}"
tools:text="test" />
<include
android:id="#+id/addPerformedPAField"
layout="#layout/add_performed_pa"
app:cpaTabViewModel="#{cpaTabViewModel}"/>
[...]
</LinearLayout>
</layout>
The included layout is add_performed_pa.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="cpaTabViewModel"
type="org.romco.example.CpaTabViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="24dp"
tools:showIn="#layout/cpa_tab_fragment">
<Spinner
android:id="#+id/inputActivitySpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textStart"
app:layout_constraintStart_toEndOf="#+id/cancelButton"
app:layout_constraintTop_toTopOf="#+id/cancelButton"
app:entries="#{cpaTabViewModel.activities}" <---- THIS CAUSES THE PROBLEM
tools:listitem="#layout/sample_textview" />
[...]
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The class representing the fragment is CpaTabFragment.kt:
class CpaTabFragment : Fragment() {
private lateinit var cpaTabFragBinding: CpaTabFragmentBinding
private val cpaTabViewModel: CpaTabViewModel by lazy {
ViewModelProvider(this, CpaTabViewModel.Factory(activity?.application!!))
.get(CpaTabViewModel::class.java)
.apply {
setPeriodType(arguments?.getSerializable(ARG_SECTION_TYPE) as PlanPeriodType)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
cpaTabFragBinding = DataBindingUtil.inflate(inflater, R.layout.cpa_tab_fragment, container, false)
cpaTabFragBinding.testTextView.text = "TEST HEADER"
cpaTabFragBinding.addPerformedPAField.root.visibility = View.GONE
cpaTabFragBinding.cpaTabViewModel = cpaTabViewModel
// (this next line is replaced by passing the viewModel in the <include app:cpaTabViewModel="#{cpaTabViewModel} - I hope this is correct, but that's beside the point here)"
// cpaTabFragBinding.addPerformedPAField.cpaTabViewModel = cpaTabViewModel
return cpaTabFragBinding.root
}
Obviously, I removed most of the unimportant (or at least what I'm hoping is unimportant) code above, leaving only what I think is relevant here.
Without actually using data-binding in the add_performed_pa.xml layout (the line marked with "THIS CAUSES THE PROBLEM"), the entire thing works fine. I can access the included layout as a binding just fine, change it's root visibility, even access any elements in it, etc.
However, as soon as I try to add any data-binding into the included .xml, app won't compile, and the error is the following:
...\app\build\generated\source\kapt\debug\org\romco\example\DataBinderMapperImpl.java:18: error: cannot find symbol
import org.romco.example.databinding.AddPerformedPaBindingImpl;
I can't really find anyone else with this problem. Can you please help?

Why viewBinding makes XML root full screen

I am wondering why adding viewBinding to my app, makes the XML root display in full screen when running the app. The root doesn't display in full screen in the designer view inside Android Studio.
Due to the root displaying in full screen, I had to add android:gravity="center" to vertically align the children in the center. While I don't think this should be a problem, I'm still interested in knowing why this is the case.
I have the following code;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center_vertical"
android:background="#color/colorAccent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="1"
android:textSize="30sp" />
<Button
android:id="#+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/roll"/>
</LinearLayout>
package com.example.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.diceroller.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
binding.rollButton.text = "testBind"
setContentView(binding.root)
}
}
I am not sure but I think that when You execute this setContentView(binding.root) You are setting a linear layout as fullscreen.
setContentView Android Docs
Set the activity content to an explicit view. This view is placed directly into the activity's view hierarchy. It can itself be a complex view hierarchy. When calling this method, the layout parameters of the specified view are ignored. Both the width and the height of the view are set by default to ViewGroup.LayoutParams#MATCH_PARENT. To use your own layout parameters, invoke setContentView(android.view.View, android.view.ViewGroup.LayoutParams) instead.
To do make it looks like in the preview You can write this code.
XML:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="#color/colorAccent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="1"
android:textSize="30sp" />
<Button
android:id="#+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Roll" />
</LinearLayout>
</FrameLayout>
</layout>
MainActivity.kt:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
binding.rollButton.text = "Test Roll"
setContentView(binding.root)
}
}
Tip: To convert fast Your layout to data binding layout use: Alt + Enter ➡ Convert to data binding layout

How to show menu item as action on a toolbar in a fragment

I have setup a basic menu and a toolbar like so:
home_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/notifications"
app:showAsAction="always"
android:title="#string/_ui_notifications"
android:icon="#drawable/ic_notifications_active"
/>
</menu>
fragment_home.xml <-This is where I want to show the toolbar and menu
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context="._fragments.dash.HomeFragment"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="64dp">
<Toolbar
android:id="#+id/homeToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/app_logo_new"
android:scaleType="fitCenter"
android:padding="16dp"/>
</Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="240dp"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
HomeFragment.kt <- This is the fragment where I've setup the toolbar
class HomeFragment : 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)
appToolbar = view.findViewById(R.id.homeToolbar)
appToolbar.title = ""
appToolbar.inflateMenu(R.menu.home_menu)
appToolbar.setOnMenuItemClickListener{
when(it.itemId){
R.id.notifications -> {
Toast.makeText(activity!!, "Notifications", Toast.LENGTH_SHORT).show()
true
}
else -> {
Log.d("Menu", "Terrible things have happened in menu")
false
}
}
}
return view
}
}
With this simple setup, I was able to get the toolbar working. Also clicking on the menu item "Notifications" gives out a toast as expected.
However, the menu is being displayed as a drop-down menu like so:
How do I make it into an action?
Edit:
I have disabled the default Action bar in themes by using the property .NoActionBar
-
You're using the native Toolbar in your layout; i.e., <Toolbar>. That's ignoring the app:showAsAction attribute in your menu XML, as it's in your app's namespace.
I would guess that you actually meant to use the library Toolbar, in which case you'd want to change that layout element to <androidx.appcompat.widget.Toolbar>, and to modify the relevant import statement in your HomeFragment class.
If you actually did mean to use the native Toolbar, for some reason, then you need to use the showAsAction attribute in the android namespace; i.e., android:showAsAction.

Categories

Resources