How to write the adapter class for the following recycler view? - kotlin

I am trying to make a Todo app and I have done the Room part and able to store data now I want to display the data in the form of Recycler View but I am not getting how to write the adapter class corresponding to it. I looked for it in different sites I never got any satisfying answer.
**TodoFragViewModel.kt""
class TodofragViewModel(
val database: TodoDao, applicaltion: Application
): AndroidViewModel(applicaltion) {
// TODO: Implement the ViewModel
/**
* viewModelJob allows us to cancel all coroutines started by this ViewModel.
*/
private var viewModelJob = Job()
/**All coroutines can be cancelled by viewmodelJob.cancel() and Dispatcher.main is byDefault choice
*/
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val currenctTodo = MutableLiveData<Todo?>()
private val allTodo = database.getAllTodo()
init{
intializeThisTodo()
}
private fun intializeThisTodo(){
uiScope.launch {
currenctTodo.value=getFromDatabase()
}
}
private suspend fun getFromDatabase(): Todo? {
return withContext(Dispatchers.IO){
val info =database.getCurrentTodo()
info
}
}
private suspend fun insert(thisTodo: Todo) {
withContext(Dispatchers.IO) {
database.insert(thisTodo)
Log.i("Database","${database.getCurrentTodo()?.description} and ${database.getCurrentTodo()?.time}")
}
}
fun onAdded(time:String,description:String) {
uiScope.launch {
val thisTodo = Todo(time,description)
insert(thisTodo)
currenctTodo.value=getFromDatabase()
}
}
/**
* Called when the ViewModel is dismantled.
* At this point, we want to cancel all coroutines;
* otherwise we end up with processes that have nowhere to return to
* using memory and resources.
*/
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
todo_recycler_view
<androidx.cardview.widget.CardView 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/date_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/todo_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/date_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
TodoFrag.kt
class todofrag : Fragment() {
companion object {
fun newInstance() = todofrag()
}
private lateinit var viewModel: TodofragViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.todofrag_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val application = requireNotNull(this.activity).application
val dataSource= TodoDatabase.getInstance(application)?.InformationDatabaseDao
val viewModelFactory = dataSource?.let { TodoViewModelFactory(it, application) }
val viewModel=ViewModelProviders.of(this,viewModelFactory).get(TodofragViewModel::class.java)
add_button.setOnClickListener{
val currentDate: String = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(Date())
val currentTime: String = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date())
val time:String="${currentDate} \n ${currentTime}"
viewModel.onAdded(time,todo_text.text.toString())
}
}
}
Please let me know if any other files are added. By the way, I tried to use card view so that it looked good.

The developer documentation explains it pretty well.
This might not be perfectly suited for what you need, but it should be a good start. Specifically, I don't know all the fields for your Todo class, so make sure you account for those in this code.
Basically, you'll want to have a ViewHolder that represents your CardView
class TodoViewHolder(convertView: View) : RecyclerView.ViewHolder(convertView) {
val dateText = convertView.findViewById(R.id.date_text)
val description = convertView.findViewById(R.id.todo_description)
// whatever else you need access to
}
And you'll want to use DiffUtil for a better user experience. This allows for some animations when things in the list change, such as removing an item, editing an item, or adding an item.
private class TodoDiffCallback : DiffUtil.ItemCallback<Todo>() {
override fun areItemsTheSame(oldItem: Todo, newItem: Todo) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Todo, newItem: Todo) =
oldItem.dateText == newItem.dateText && oldItem.description == newItem.description
}
You'll want to extend ListAdapter and override its methods. onCreateViewHolder creates an instance of your TodoViewHolder for each view that is seen and onBindViewHolder allows you to add behavior to each item in the list. It is worth noting that you can pass parameter into the adapter in case you need to.
class MyListAdapter : ListAdapter<Todo, TodoViewHolder>(TodoDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = TodoViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.todo_recycler_view, parent, false))
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val todo = getItem(position)
holder.dateText = todo.dateText
holder.description = todo.description
// add whatever click listener and other stuff you need
}
}
In your fragment, when you access your RecyclerView, just add an instance of the adapter if it's null.
if (recyclerView.adapter == null) {
recyclerView.adapter = TotoListAdapter()
}
And when you want to add data (that you have retrieved from Room or your API) to the adapter (in the fragment/activity), just do the following:
(recyclerView.adapter as? TodoListAdapter)?.submitList(data)
On a side note, make sure to clean up your style (you can use the Reformat Code command in the Code menu), and you would want to rename the todo_recycler_view to something like todo_view. You'll want to have a RecyclerView layout in your fragment layout.

Related

Artefacts when implementing expandable items in RecyclerView

I'm trying to make a simple RecyclerView with expandable items using Kotlin. Basically a list of bins which expand out to show descriptions of what should go in them. I tried to follow the Android Studio Recycler View example as closely as possible. The problem is when I expand each item, some artefacts would show.
Unexpanded view
Expanded view
Note: The expanded view is the result of pressing on "Recycling Bin", the artefact is made up of the bin's description text and also "Garden Waste Bin" somehow being duplicated.
Below is my implementation
Adapter class
class BinAdapter(private val dataSet: List<Bin>) : RecyclerView.Adapter<BinViewHolder>() {
var expandedPosition = -1
var previousExpandedPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BinViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.bin_row_item, parent, false)
return BinViewHolder(view)
}
override fun onBindViewHolder(viewHolder: BinViewHolder, position: Int) {
viewHolder.itemView.isActivated
viewHolder.titleView.text = dataSet[position].name
viewHolder.descriptionView.text = dataSet[position].description
val isExpanded = position == expandedPosition
viewHolder.descriptionView.visibility = if (isExpanded) View.VISIBLE else View.GONE
viewHolder.itemView.isActivated = isExpanded
if (isExpanded) {
previousExpandedPosition = position
}
viewHolder.itemView.setOnClickListener {
expandedPosition = if (isExpanded) -1 else position
notifyItemChanged(previousExpandedPosition)
notifyItemChanged(position)
}
}
override fun getItemCount() = dataSet.size
Fragment class
class BinLookupFragment : Fragment() {
private var _binding: FragmentBinLookupBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentBinLookupBinding.inflate(inflater, container, false)
binding.binRecyclerView.adapter = BinAdapter(BinList(resources))
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
ViewHolder class
class BinViewHolder(itemView: View) : ViewHolder(itemView) {
val titleView: TextView
val descriptionView: TextView
init {
titleView = itemView.findViewById(R.id.titleView)
descriptionView = itemView.findViewById(R.id.descriptionView)
}
}
Row item layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/titleView"
android:layout_width="#dimen/bin_list_width"
android:layout_height="#dimen/bin_list_item_title_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/descriptionView"
android:layout_width="#dimen/bin_list_width"
android:layout_height="#dimen/bin_list_item_description_height"
app:layout_constraintTop_toBottomOf="#id/titleView"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment layout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/bin_recycler_view"
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"
android:layout_marginTop="200dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Has anyone come across this before or have some debugging tips?

No Adapter Attached RecyclerView

I want to implement my app. My app went smoothly, but I also got an error with 2022-05-31 14:14:32.663 8626-8626/id.kotlin.belajar E/RecyclerView: No adapter attached; skipping layout; skipping layout.
E/RecyclerView: No adapter attached; skipping layout; skipping layout
Home Activity:
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
recyclerView.adapter = HomeAdapter(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
Home Adapter:
class HomeAdapter (private val results: List<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Home Presenter:
class HomePresenter(private val view: HomeView, datasource: HomeDatasource) {
fun discoverMovie(){
view.onShowLoading()
val dataSource = Networkmodule.providesHttpAdapter(client =
OkHttpClient()).create(HomeDatasource::class.java)
dataSource.discoverMovie().enqueue(object : Callback<HomeResponse> {
override fun onResponse(call: Call<HomeResponse>, response: Response<HomeResponse>){
view.onHideLoading()
view.onResponse(((response.body()?.results ?: emptyList()) as List<Result<String>>))
}
override fun onFailure(call: Call<HomeResponse>, t:Throwable){
view.onHideLoading()
view.onFailure(t)
}
})
}
}
I need your help.
This is my .xml files:
activity_home:
<?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="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.HomeActivity">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:title="Belajar"
app:titleTextColor="#color/black"/>
<ProgressBar
android:id="#+id/pb_home"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_home" />
</androidx.constraintlayout.widget.ConstraintLayout>
I can see two major problems here :
Adapter instantiation
You do not need to re-instantiate the adapter each time you receive data.
Just instanciate it in the onCreate method with an empty list and update the list each time you have new data (don't forget to call notify method).
And attach the adapter to the recyclerView in the onCreate method as well
Exemple
In your activity you need to :
Declare and instantiate your Adapter as a class attribute.
In the onCreate method attach it to the recyclerView
On the onResponse method call a new method of your adapter called " update "
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
private val homeAdapter = HomeAdapter(emptyList())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
recyclerView.adapter = homeAdapter
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
homeAdapter.update(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
In your adapter :
Make results a mutableList
Create the update method
Clear the results list
Add your newResults list to your list
notifyDataSetChanged to redraw the list with new values
class HomeAdapter (private val results: MutableList<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
fun update(newResults: List<Result>) {
results.apply {
clear()
addAll(newResults)
}
notifyDataSetChanged()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Layout manager missing ?
We don't have your xml code so I don't know if you added the layoutManager on the xml like this (assuming you want a linearManager) :
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
You can do it programaticaly using the layoutManager attribute on the recyclerView :
recyclerView.layoutManager = LinearLayoutManager()
You can set it in the onCreate method (if it is an activity) or onViewCreated (if it's a fragment) before attaching your adapter to the recyclerView
To avoid errors like that, the best way is to setup your recyclerView as soon as possible in the activity (onCreate) or fragment (onViewCreated) lifecycle.

Using recycleview in android

I read some document about using recyclingview for activity. Now i try to use recycleview to my fragment. the problem is my fragment look empty when i execute.
fragment:
class KategoriFragment : Fragment() {
var araclarKategori = ArrayList<AracMarka>()
private lateinit var galleryViewModel: GalleryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
veriKaynaginiDoldur()
galleryViewModel =
ViewModelProviders.of(this).get(GalleryViewModel::class.java)
var root = inflater.inflate(R.layout.fragment_category, container, false)
veriKaynaginiDoldur()
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
/
return root
}
fun veriKaynaginiDoldur(): ArrayList<AracMarka> {
var aracLogo = arrayOf(R.drawable.opellogo, R.drawable.chevroletlogo)
var aracismi = resources.getStringArray(R.array.arabaisim)
for (i in 0 until min(aracismi.size, aracLogo.size)) {
var eklenecekaracKategori = AracMarka(aracismi[i], aracLogo[i])
araclarKategori.add(eklenecekaracKategori)
}
return araclarKategori
}
}
I create an adapter. I think there is no problem on it.
adapter:
class AracMarkaAdapter(tumKategori: ArrayList<AracMarka>) :
RecyclerView.Adapter<AracMarkaAdapter.AracMarkaViewHolder>() {
var araclar = tumKategori
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AracMarkaViewHolder {
var inflater = LayoutInflater.from(parent.context)
var arackategori = inflater.inflate(R.layout.arac_kategori, parent, false)
return AracMarkaViewHolder(arackategori)
}
override fun getItemCount(): Int {
return araclar.size
}
override fun onBindViewHolder(holder: AracMarkaViewHolder, position: Int) {
holder.aracismi.text=araclar.get(position).aracAdi
holder.aracLogo.setImageResource(araclar.get(position).aracLogo)
}
class AracMarkaViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var teksatirKategori= itemView
var aracismi= teksatirKategori.tvaracAdi
var aracLogo=teksatirKategori.img_arac_sembol
}
}
fragment 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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvarackategori"
android:layout_width="0dp"
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>
So, when i click button, fragment opens but it is empty. Do you have any idea about it?
After modifying the data in your list in veriKaynaginiDoldur(), you need to call myAdapter.notifyDataSetChanged() so it knows to rebind the data. Or you could call veriKaynaginiDoldur() before you instantiate your adapter.
Edit: Your other error is in your for loop within veriKaynaginiDoldur(). You are making a range using the size of the araclarKategori list when it is still zero.
Instead of
for (i in 0..araclarKategori.size - 1)
use
for (i in 0 until min(aracLogo.size, aracismi.size))
you have to call this veriKaynaginiDoldur() function after the below
lines of code below I have mentioned please check
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
veriKaynaginiDoldur()

Kotlin FragmentStatePagerAdapter shows empty views

I am trying to set up ViewPager adapter inside a fragment to show another fragments. The problem is that when called ViewPager automatically calls the first two fragments while displaying only blank page.
This is how ViewPager is set:
class SwipePagerFragment : Fragment() {
private lateinit var pager: ViewPager
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_swipe_pager, container, false)
pager = view.findViewById(R.id.fragmentSwipePager_pager)
val pagerAdapter = ScreenSlidePagerAdapter(childFragmentManager)
pager.adapter = pagerAdapter
return view
}
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
override fun getCount(): Int = 3
override fun getItem(position: Int): Fragment = when (position) {
0 -> MeFragment()
1 -> AccountFragment()
3 -> SearchFragment()
else -> ContactsFragment()
}
}
override fun onDestroyView() {
val frContainer = (activity as MainActivity).findViewById<View>(R.id.activityMain_fragment) as ViewGroup
frContainer.removeAllViews()
super.onDestroyView()
}
}
This is what I get in logcat:
... D/FRG ME: # onCreateView
... D/FRG Account: # onCreateView
I think you are trying to add 4 fragments in ViewPager and provided getCount() = 3
override fun getCount(): Int = 4
Also, make changes in Adapter as follows,
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) :
FragmentStatePagerAdapter(fm) {
override fun getCount(): Int = 4
override fun getItem(position: Int): Fragment = when (position) {
0 -> MeFragment()
1 -> AccountFragment()
2 -> SearchFragment()
else -> ContactsFragment()
}
}
Oh, LOL, I have figured it out.
I have set layout for Fragment I used for ViewPager as follows:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
android:id="#+id/fragmentSwipePager_rootScroll"
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:padding="5dp"
tools:context=".SwipePagerFragment">
<android.support.v4.view.ViewPager
android:id="#+id/fragmentSwipePager_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
</ScrollView>
The problem was in "layout_height" property of ScrollView as it was set to "wrap_content" and with no actual content with non-zero height inside it resulted in drawing zero-height line and displaying Fragments contents inside it. Changing "layout_height" to "match_parent" solves my problem.

Convert MainActivity with actionbar/toolbar and floatingaction button to Anko

I am trying to learn how to use Kotlin/Anko.
I have gone thru the examples here and also cloned the template project and can understand how to do some basic stuff, but as an exercise I wanted to convert this simple activity (generated from a blank activity in Android Studio and converted to Kotlin) to use Anko as well. There are not a lot of examples around for Anko, most are just copies of what is on the above referenced github page.
Can someone demonstrate how to go about and convert the following into Anko DSL?
MainActivity.kt
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolBar = findViewById(R.id.toolbar) as Toolbar
setSupportActionBar(toolBar)
val fab = findViewById(R.id.fab) as FloatingActionButton
fab.setOnClickListener { view -> Snackbar.make(view, "Replace this with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == R.id.action_settings) {
println("settings clicked on ")
return true
}
return super.onOptionsItemSelected(item)
}
}
main_activity.xml
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
android:src="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.gmail.npnster.mykotlinfirstproject.MainActivity"
tools:showIn="#layout/activity_main">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="#+id/hello"
/>
</RelativeLayout>
menu_main.xml
<menu 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"
tools:context="com.gmail.npnster.mykotlinfirstproject.MainActivity">
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="#string/action_settings"
app:showAsAction="never" />
</menu>
You can use ankoView method to create Views without DSL methods inside DSL context.
For example, to create a NavigationView one can use
ankoView({ NavigationView(it) }) {
lparams(width = wrapContent, height = matchParent, gravity = Gravity.START)
// more initialization follows
}
This way you can instantiate FloatingActionButton and AppBarLayout, just call their constructors inside ankoView's first argument function. For your convenience, you can make yourself DSL-like functions like in the manual:
fun floatingActionButton(init: FloatingActionButton.() -> Unit) = ankoView({ FloatingActionButton(it) }, init)
Creating a Toolbar is even easier: there is a DSL toolbar method in org.jetbrains.anko.appcompat.v7.
When using Anko DSL, to include another layout, as you did with content_main, one can either use Anko include function or just write a function which will fill in a ViewGroup. You can use this template:
fun ViewGroup.myLayout() {
textView("123")
// more DSL code here
}
Then just call myLayout() inside some ViewGroup initializer.
I know it's a bit late answer, but I hope it helps someone. I did the layout this way (of course some styling is still needed):
class MainUI(val adapter: MainUIAdapter) : AnkoComponent<MainActivity> {
override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
coordinatorLayout {
fitsSystemWindows = true
appBarLayout {
toolbar {
setTitleTextColor(Color.WHITE) // so far still needed
id = R.id.toolbar
}.lparams(width = matchParent, height = matchParent)
}.lparams(width = matchParent)
relativeLayout {
id = R.id.container
recyclerView { // just an example
id = R.id.recycler_view
adapter = this#MainUI.adapter
layoutManager = LinearLayoutManager(ctx)
}
}.lparams(width = matchParent, height = matchParent) {
behavior = ScrollingViewBehavior()
}
floatingActionButton {
onClick { doSomething() }
imageResource = R.drawable.ic_add_white_24dp // the plus sign
}.lparams {
gravity = Gravity.BOTTOM or Gravity.END
margin = dip(16)
}
}
}
}
and used in MainActivity like that:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainUI(MainUIAdapter(people)).setContentView(this)
toolbar = find<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
}