I am trying to do the following:
In activity, replace the container with a fragment (TestFragment)
This fragment's layout contains a container which is replaced by another fragment (TestSubFragment)
Clicking on the TestSubFragment makes the activity add a new TestFragment over the root container
TestActivity.kt
class TestActivity : AppCompatActivity(), TestSubFragment.OnFragmentInteractionListener {
override fun onFragmentInteraction(id: Int) {
supportFragmentManager.beginTransaction().add(R.id.container, TestFragment.newInstance(id)).addToBackStack(null).commit()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
supportFragmentManager.beginTransaction().replace(R.id.container, TestFragment.newInstance(1)).addToBackStack(null).commit()
}
}
TestFragment.kt
class TestFragment : Fragment() {
private var id: Int? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater!!.inflate(R.layout.fragment_test, container, false)
activity.supportFragmentManager.beginTransaction().replace(R.id.sub_fragment_container, TestSubFragment.newInstance(id!!)).commit()
return v
}
companion object {
fun newInstance(id: Int): TestFragment {
val fragment = TestFragment()
fragment.id = id
return fragment
}
}
}
TestSubFragment.kt
class TestSubFragment : Fragment() {
private var mListener: OnFragmentInteractionListener? = null
private var id: Int? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater!!.inflate(R.layout.fragment_sub_test, container, false)
v.id_text.text = id.toString()
v.id_text.setOnClickListener { _ -> mListener?.onFragmentInteraction(v.id_text.text.toString().toInt() + 1) }
return v
}
override fun onAttach(context: Context?) {
super.onAttach(context)
if (context is OnFragmentInteractionListener) {
mListener = context
}
}
interface OnFragmentInteractionListener {
fun onFragmentInteraction(id: Int)
}
companion object {
fun newInstance(id: Int): TestSubFragment {
val fragment = TestSubFragment()
fragment.id = id
return fragment
}
}
}
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="ckl.happens.TestActivity">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
fragment_test.xml
<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="#ffffff"
tools:context="ckl.happens.TestFragment">
<FrameLayout
android:id="#+id/sub_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</FrameLayout>
fragment_sub_test.xml
<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="#ffffff"
tools:context="ckl.happens.TestFragment">
<TextView
android:id="#+id/id_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_blank_fragment" />
</FrameLayout>
Problem is that the replace fragment line in TestFragment.kt is finding the first R.id.sub_fragment_container from the xml hierarchy so it is replacing the incorrect container instead of the last/new container.
I tried adding tag in fragmenttransaction or change R.id.sub_fragment_container to v.sub_fragment_container.id but no luck.
I don't want to change add() to replace() in onFragmentInteraction because the fragment will be recreated and I want to keep everything in the fragment unchanged when the user back to that fragment.
I can't find detailed article on nesting fragments for my case.
I am working with Kotlin but I can also understand Java. Thank you!
I solved my problem without getting the new container id.
I decided to remove fragmenttransaction and add the fragment in the xml. Then in the fragment (parent fragment) that contains the sub-fragment, get the sub-fragment with childFragmentManager.findFragmentById(R.id.fragment). Finally update the content of the sub-fragment from the parent fragment.
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
val fragment1 = TestFragment.newInstance(1)
supportFragmentManager.beginTransaction().replace(R.id.container, fragment1).commit()
val fragment2 = TestFragment.newInstance(2)
supportFragmentManager.beginTransaction().add(R.id.container, fragment2).addToBackStack(null).commit()
val fragment3 = TestFragment.newInstance(3)
supportFragmentManager.beginTransaction().add(R.id.container, fragment3).addToBackStack(null).commit()
}
}
class TestFragment : Fragment() {
private var id: Int? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater!!.inflate(R.layout.fragment_test, container, false)
val subFragment = childFragmentManager.findFragmentById(R.id.fragment) as TestSubFragment
subFragment.view?.findViewById<TextView>(R.id.id_text)?.text = id.toString()
return v
}
companion object {
fun newInstance(id: Int): TestFragment {
val fragment = TestFragment()
fragment.id = id
return fragment
}
}
}
class TestSubFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_sub_test, container, false)
}
}
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="ckl.happens.TestActivity">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
fragment_test.xml
<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="#ffffff"
tools:context="ckl.happens.TestFragment">
<fragment
android:id="#+id/fragment"
android:name="ckl.happens.TestSubFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
fragment_sub_test.xml
<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="#ffffff"
tools:context="ckl.happens.TestFragment">
<TextView
android:id="#+id/id_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="" />
</FrameLayout>
Related
I have create an activity and have 2 buttons. On click of each button fragment opens.
But when I do that fragment UI gets overlapped with activity UI.
I want to know if we can have UI components in both activity and fragment.
Also if have to keep the activity UI blank and create a new fragment from the activity for the UI.
Please let me kow how I can solve this.
Example of the code :
MainActivity.kt
override fun onClick(view: View?) {
Log.d(TAG, "onClick: ")
when(view!!.id){
R.id.song_list->
{
Toast.makeText(this, "Songs list will be shown", Toast.LENGTH_SHORT).show()
openFragment("Songs")
}
R.id.artist_list->
{
Toast.makeText(this, "Artist list will be shown", Toast.LENGTH_SHORT).show()
openFragment("Artist")
}
}
}
fun openFragment(fragmentName: String){
Log.d(TAG, "openFragment: ")
if(fragmentName.equals("Songs")) {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.main_fragment, SongsFragment())
transaction.disallowAddToBackStack()
transaction.commit()
}else{
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.main_fragment, ArtistFragment())
transaction.commit()
}
}
MainActivity 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">
<FrameLayout
android:id="#+id/main_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/song_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Songs List"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="#+id/artist_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Artist List"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/song_list"
android:layout_marginTop="5dp"/>
SongsFragment.kt
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate: ")
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_songs, container, false)
Log.d(TAG, "onCreateView: ")
}
SongsFragment XML:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".SongsFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Please find the list of songs here" />
</androidx.constraintlayout.widget.ConstraintLayout>
As i can see you are missing some constrains on the Frame Layout that holds your fragments, if you set it as you like you can have your buttons and the fragment of your choice at the same time.
I'm new in Android Studio and having trouble opening fragment on click CardView in other fragment. I have Navigation View in layout and navigate to other fragment (fragment_home, fragment_gallery, fragment_slideshow, and other fragment layout.). But I have to create CardView in fragment_home to click for open some fragment layout (Gallery and Slideshow).
I have layout:
1. fragment_home.xml
<?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"
tools:context=".ui.home.HomeFragment">
<androidx.cardview.widget.CardView
android:id="#+id/cardOpenGallery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_margin="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#ccc">
<TextView
android:id="#+id/textOpenGallery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textAlignment="center"
android:textSize="30sp"
android:textColor="#000"
android:text="Open Gallery"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
2. fragment_gallery.xml
<?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=".ui.gallery.GalleryFragment">
<!-- other layout element here -->
</FrameLayout>
3. fragment_slideshow.xml
<?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=".ui.gallery.SlideshowFragment">
<!-- other layout element here -->
</FrameLayout>
And Kotlin code,
1. MainActivity.kt
import ...
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow), drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
2. HomeFragment.kt
import ...
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
//I have problem here-----
val myCard1 = root.findViewById(R.id.cardOpenGallery) as CardView
myCard1.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
inflater.inflate(R.layout.fragment_gallery, container, false)
}
})
return root
}
}
3. GalleryFragment.kt
import ...
class GalleryFragment : Fragment() {
private lateinit var galleryViewModel: GalleryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
galleryViewModel =
ViewModelProviders.of(this).get(GalleryViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_gallery, container, false)
val textView: TextView = root.findViewById(R.id.text_gallery)
galleryViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
}
4. SlideshowFragment.kt
import ...
class SlideshowFragment : Fragment() {
private lateinit var slideshowViewModel: SlideshowViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
slideshowViewModel =
ViewModelProviders.of(this).get(SlideshowViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_slideshow, container, false)
val textView: TextView = root.findViewById(R.id.text_slideshow)
slideshowViewModel.text.observe(viewLifecycleOwner, Observer {
textView.text = it
})
return root
}
}
How to implement action for CardView click to open other fragment?, Please help and Thank for your any solution. Thanks.
You need to open another fragment here. For this you need to replace the fragment container in MainActivity with the desired fragment.
Replace this
myCard1.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
inflater.inflate(R.layout.fragment_gallery, container, false)
}
})
with this
myCard1.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
((Activity) getContext).supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, GalleryFragment())
.commit()
}
})
I hope this will work for you.
Happy Coding :)
You're just trying to inflate the fragment_gallery inside your homeFragment. I don't think it's the right way to do it. Although, I'll suggest the approach I use.
Inside your activity_main.xml add the FrameLayout. which will be useful to add/replace the desired fragment into it.
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Now in your Activity, create the following function. It will help you to replace the desired fragment.
private fun replaceFragment(fragmentInstance: Fragment) {
supportFragmentManager
.beginTransaction()
.addToBackStack(fragmentInstance.javaClass.canonicalName)//optional
.replace(R.id.container, fragmentInstance)
.commit()
}
Now, inside onCreate load the homeFragment (as it is your first fragment to show)
override fun onCreate(savedInstanceState: Bundle?) {
val fragment = HomeFragment()
replaceFragment(fragment)
}
At this stage, you'll be able to load homeFragment successfully. Now next step is to create a callback interface. Every time you click on any card from the homeFragment you just have to provide the required information back to the mainActivity. It is good practice to leaving the responsibility of loading fragments on the activity.
interface OnCardClickListener {
fun onCardClick(fragment: Fragment)
}
implement this listener in MainActivity.
class MainActivity: OnCardClickListener{
It will override the onCardClick. now inside onCardClick call the replace method.
override onCardClick(fragment: Fragment){
replaceFragment(fragment)
}
Inside HomeFragment, Create an instance of OnCardClickListener
var listener: OnCardClickListener? = null
then in onAttach.
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as OnCardClickListener
}
Till this point, we have implemented the mechanism required to load fragment. Now only one final step is pending which is opening the desired fragment onClick of cardView. for that do as follow:
myCard1.setOnClickListener{
listener.onCardClick(GalleryFragment())
}
//If there's one more card available.
myCard2.setOnClickListener{
listener.onCardClick(SlideshowFragment())
}
Why do I get a NullPointerException in my ViewHolder's bindItems() method?
I've highlighted the line where I get the NullPointerException. The blogpost_author ID exists, as you can see in the XML, so what's the problem here? How is findViewById<TextView>(R.id.blogpost_author) returning null?
Adapter and ViewHolder code:
class BlogPostAdapter(val blogList: ArrayList<BlogPost>) : RecyclerView.Adapter<BlogPostAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : BlogPostAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return blogList.size
}
override fun onBindViewHolder(holder: BlogPostAdapter.ViewHolder, position: Int) {
holder.bindItems(blogList[position])
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(blogPost: BlogPost) {
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author) // THIS LINE - NULL POINTER EXCEPTION
val blogPostTitle = itemView.findViewById<TextView>(R.id.blogpost_title)
blogPostAuthor.text = blogPost.author
blogPostTitle.text = blogPost.title
}
}
}
Activity code:
class BlogPostListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.blog_post_list)
// Get the RecyclerView from XML itself
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
// Add a layout manager - What does a layout manager do?
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
// Create an array list to store blogposts using the the data class blogPost
val blogPosts = ArrayList<BlogPost>()
// Add some dummy data to the list
blogPosts.add(BlogPost(123, "First Blog Post", "John"))
blogPosts.add(BlogPost(456, "Second Blog Post", "Bob"))
blogPosts.add(BlogPost(789, "Third Blog Post", "Mary"))
// Create an adapter
val adapter = BlogPostAdapter(blogPosts)
// Add the adapter to the recyclerview
recyclerView.adapter = adapter
}
}
Kotlin data class:
data class BlogPost(val id: Int, val title: String, val author: String)
XML for RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.topzap.android.kotlinlistapptest.BlogPostListActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
XML for CardView layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/blogpost_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="AuthorPlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Large"
/>
<TextView
android:id="#+id/blogpost_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="TitlePlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Medium"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
You may be inflating the wrong layout within your RecyclerView.
This line within your onCreateViewHolder method:
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
You are inflating the blog_post_list.xml, which I'm assuming is the wrong layout file due to the fact you're also inflating that layout within your BlogPostListActivity here:
setContentView(R.layout.blog_post_list)
So when this line is called:
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author)
It is looking for the id 'blogpost_author' within R.layout.blog_post_list and as you can see there is no blogpost_author TextView within that layout so it returns null.
To sort it out, it should be straight forward and just change the layout resource that you're assigning to each ViewHolder within your onCreateViewHolder method with the correct layout for your CardView layout.
Which means the line should read something like:
val v = LayoutInflater.from(parent.context).inflate(R.layout.your_card_layout, parent, false)
I'm kinda new to Android App Development. Well, I'm playing around with RecyclerView. I have a parent recyclerview with a modal layout. Now the modal layout has a recyclerview (child recyclerview). I have managed to create adapters and scroll the list of main recyclerview. Unfortunately I don't find a way to scroll the child recyclerview. Here is the code that I'm playing around:
I've already tried setting the adapter of child recyclerview in onBindViewHolder method of parent recyclerview adapter.
Also, I tried setting the attributes nestedScrollingEnabled=true, descendantFocusability=blocksDescendants and focusableInTouchMode=true for child recyclerView.
Here's my activity_main.xml:
<?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="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Parent recyclerview model (model.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Testing" />
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="#android:color/darker_gray"
android:layout_marginVertical="8dp"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/modalRecyclerView"
android:layout_width="wrap_content"
android:layout_height="100dp" />
</LinearLayout>
Child recyclerview model (child_modal.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testing"/>
</LinearLayout>
In the MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity, RecyclerView.VERTICAL, false)
adapter = ModalAdapter()
}
}
ModalAdapter:
class ModalAdapter : RecyclerView.Adapter<ModalAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context).inflate(R.layout.model, parent, false)
return ViewHolder(inflater)
}
override fun getItemCount(): Int {
return 5
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.view.modalRecyclerView.adapter = ChildModalAdapter()
holder.view.modalRecyclerView.layoutManager = LinearLayoutManager(holder.view.context, RecyclerView.VERTICAL, false)
}
class ViewHolder(val view: View): RecyclerView.ViewHolder(view)
}
ChildModalAdapter:
class ChildModalAdapter : RecyclerView.Adapter<ChildModalAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context).inflate(R.layout.child_modal, parent, false)
return ViewHolder(inflater)
}
override fun getItemCount(): Int {
return 10
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
class ViewHolder(val view: View): RecyclerView.ViewHolder(view)
}
Internal recyclerview doesn't scroll while the parent recyclerview scrolls fine. I'm trying to find a way to make the internal recyclerview to scroll along with parent recyclerview (I want both the recyclerviews to scroll).
Okay, I fixed this by setting a listener.
Here's the code below that fixed it:
val mScrollChangeListener = object : RecyclerView.OnItemTouchListener {
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_MOVE -> {
rv.parent.requestDisallowInterceptTouchEvent(true)
}
}
return false
}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
}
modalRecyclerView.addOnItemTouchListener(mScrollChangeListener)
I've added this code in the onBindViewHolder() of parent RecyclerView's adapter.
I have this drawer which opens normally but the scrolling in it is very slow, and I don't know why knowing that the image it has on top of it is not HD image, and available in the 4 sizes, I've done various examples in Java and it was fine, I wonder if the issue is that I'm using Kotlin in the adapter.
I would appreciate any help.
The activity's layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:fitsSystemWindows="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/gradient_background"
android:orientation="vertical">
<include
android:id="#+id/top"
layout="#layout/top_bar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize" />
<TextView
android:layout_below="#+id/top"
android:id="#+id/tvTradesTicker"
android:layout_width="match_parent"
android:layout_height="#dimen/trades_ticker_height"
android:background="#color/colorPrimaryLight"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:focusable="true"
android:freezesText="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="#dimen/small_margin"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#color/darkGray"
android:textSize="#dimen/font"
android:visibility="visible" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvGrid"
android:layout_below="#+id/tvTradesTicker"
android:layout_above="#+id/llFooter"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
layout="#layout/footer"
android:id="#+id/llFooter"
android:layout_width="match_parent"
android:layout_height="#dimen/footer_height"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/white"
android:fitsSystemWindows="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:paddingTop="#dimen/big_margin_padding"
android:orientation="vertical">
<include
layout="#layout/item_drawer_header"
android:id="#+id/header"
android:background="#color/white"
android:layout_width="match_parent"
android:layout_height="#dimen/drawer_header_height" />
<ListView
android:id="#+id/lvItems"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#color/white"
android:divider="#android:color/transparent"
android:paddingTop="#dimen/small_margin" />
</LinearLayout>
</android.support.design.widget.NavigationView>
The Drawer's Adapter:
class DrawerListAdapter : BaseAdapter {
private var drawerItems: MutableList<DrawerItem> = mutableListOf()
private var context: Context? = null
constructor(context: Context, notesList: MutableList<DrawerItem>) : super() {
this.drawerItems = notesList
this.context = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
val view: View?
val vh: ViewHolder
val inflater = LayoutInflater.from(context)
val type = getItemViewType(position)
if (convertView == null) {
view = if (type == 0){
inflater.inflate(R.layout.item_drawer_section, parent, false)
}else{
inflater.inflate(R.layout.item_drawer_child, parent, false)
}
vh = ViewHolder(view)
view.tag = vh
} else {
view = convertView
vh = view.tag as ViewHolder
}
val drawerItem = drawerItems[position]
vh.name.text = drawerItem.name
if (type == 0){
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.blue))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.colorAccent))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.white))
}else if (type == 1){
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.colorPrimary))
vh.separator.visibility = View.VISIBLE
}else{
vh.rel.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setBackgroundColor(ContextCompat.getColor(context!!, R.color.white))
vh.name.setTextColor(ContextCompat.getColor(context!!, R.color.colorPrimary))
vh.separator.visibility = View.GONE
}
Actions.overrideFonts(context!!, vh.rel)
return view
}
override fun getItem(position: Int): DrawerItem {
return drawerItems[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return drawerItems.size
}
override fun getItemViewType(position: Int): Int {
if (drawerItems[position].isHeader)
return 0
else if (!drawerItems[position].isLast)
return 1
else
return 2
}
override fun getViewTypeCount(): Int {
return 3
}
}
private class ViewHolder(view: View) {
val rel: RelativeLayout = view.findViewById(R.id.rel) as RelativeLayout
val name: TextView = view.findViewById(R.id.name) as TextView
val separator = view.findViewById<View>(R.id.separator)
}
What should I change, the activity layout, or the adapter or what i realy have no idea.
EDIT
I set the visibility of the marquee text view in the layout to gone, and everything worked just fine, does anybody know why? because I don't want to replace this text view with a horizontal recycler view
SOLVED.
Changed the approach completely, I stopped the marquee and applied cross fade animation to the text view, with a handler that changes the object being set in the text view at each iteration, and now it looks perfect and way more elegnat