I am really new at Kotlin. I tried to make slidable pages using TabLayout. I have 3 different Fragments and I need to add ListView or RecyclerView on that fragments but in this point I can't figure out where is I have to add adapter of ListView. I add adapter class on fragment's Kotlin file but I can't connect that and fragment.(I think) I got an error which is ListView cannot be null so I can say about my adapter didn't working correctly. How can I solve it?
Here I add my codes:
My SecondFragment.kt code:
class SecondFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val listView = view!!.findViewById<ListView>(R.id.main_listview)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val listView = view!!.findViewById<ListView>(R.id.main_listview)
return inflater.inflate(R.layout.fragment_second, container, false)
}
private class MyCustomAdapter(context: Context): BaseAdapter(){
private val mContext : Context
private val names = arrayListOf<String>("Test Name", "Steve Jobs", "Tim Cook", "Kobe King")
init {
mContext = context
}
// BURADA KAC TANE ROW OLDUGUNUN SOYLER..
override fun getCount(): Int {
return names.size
}
override fun getItem(position: Int): Any {
return "Test String"
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
//val textView = TextView(mContext)
//textView.text = "Here my first row for list view.."
//return textView
val layoutInflater = LayoutInflater.from(mContext)
val rowMain = layoutInflater.inflate(R.layout.row_for_list, parent, false)
val nameTextView = rowMain.findViewById<TextView>(R.id.name_textView)
nameTextView.text = names.get(position)
val positionTextView = rowMain.findViewById<TextView>(R.id.position_textview)
positionTextView.text = "Row Number: $position"
return rowMain
}
}
}
Here my ViewPagerAdapter.kt file code:
class ViewPagerAdapter(supportFragmentManager: FragmentManager) : FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()
override fun getCount(): Int {
return mFragmentList.size
}
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getPageTitle(position: Int): CharSequence? {
return mFragmentTitleList[position]
}
fun addFragment(fragment: Fragment, title: String){
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}
}
And here my MainActivity.kt file code:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpTabs()
}
private fun setUpTabs() {
val adapter = ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(HomeFragment(), "Home")
adapter.addFragment(SecondFragment(), "Second")
adapter.addFragment(ThirdFragment(), "Third")
viewPager.adapter = adapter
tabs.setupWithViewPager(viewPager)
tabs.getTabAt(0)!!.setIcon(R.drawable.ic_baseline_account_box_24)
tabs.getTabAt(1)!!.setIcon(R.drawable.ic_baseline_add_ic_call_24)
tabs.getTabAt(2)!!.setIcon(R.drawable.ic_baseline_adb_24)
}
}
You have to set to instantiate the adapter and set it to the listView, I also explained some things in the comments.
class SecondFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//don't do this in onCreate, do it in onViewCreated or onCreateView
//val listView = view!!.findViewById<ListView>(R.id.main_listview)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val root = inflater.inflate(R.layout.fragment_second, container, false)
//you can set adapter here if you want, but i did it in onViewCreated
//return it
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//get reference to ListView
val listView = root.findViewById<ListView>(R.id.main_listview)
//set adapter
listView.adapter = MyCustomAdapter(requireContext())
}
private class MyCustomAdapter(private val mContext: Context): BaseAdapter(){
//do initiation here as it needs to be done only once
private val layoutInflater: LayoutInflater = LayoutInflater.from(mContext)
private val names = arrayListOf<String>("Test Name", "Steve Jobs", "Tim Cook", "Kobe King")
// BURADA KAC TANE ROW OLDUGUNUN SOYLER..
override fun getCount(): Int {
return names.size
}
override fun getItem(position: Int): Any {
return "Test String"
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
//val textView = TextView(mContext)
//textView.text = "Here my first row for list view.."
//return textView
val rowMain = layoutInflater.inflate(R.layout.row_for_list, parent, false)
val nameTextView = rowMain.findViewById<TextView>(R.id.name_textView)
nameTextView.text = names.get(position)
val positionTextView = rowMain.findViewById<TextView>(R.id.position_textview)
positionTextView.text = "Row Number: $position"
return rowMain
}
}
}
Try this :
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view=inflater.inflate(R.layout.fragment_second, container, false)
val listView = view!!.findViewById<ListView>(R.id.main_listview)
val myListAdapter = MyCustomAdapter(container!!.context)
listView.adapter = myListAdapter
return view
}
Related
I have home fragment and I want it to go to another fragment which will show its "details", which passes the data of the clicked recyclerview item to that "details" fragment.
When I click the article it will move to detail article which passes the data.
As for the code, here's the adapter:
class ArticleAdapter(private val articleList: ArrayList<Article>) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = articleList[position]
holder.bind(data)
}
class MyViewHolder(private val binding: ItemArticleBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Article) {
Glide.with(binding.root.context)
.load(data.img)
.into(binding.articleImage)
binding.articleTitle.text = data.title
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
}
}
}
override fun getItemCount(): Int {
return articleList.size
}
}
Here's my detailFragment
class DetailArticleFragment : Fragment() {
private var _binding: FragmentDetailArticleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDetailArticleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val intent = Intent(binding.root.context, DetailArticleFragment::class.java)
val article = intent.getParcelableExtra<Article>(DETAIL_ARTICLE) as Article
Glide.with(this)
.load(article.img)
.into(_binding!!.articleImage)
_binding!!.articleTitle.text = article.title
_binding!!.articleDescription.text = article.content
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
const val DETAIL_ARTICLE = "detail_article"
}
}
You need to pass click listener interface to adapter, for example:
typealias OnArticleClick = (article: Article) -> Unit
class ArticleAdapter(
private val articleList: ArrayList<Article>
) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
var onArticleClick: OnArticleClick? = null
...
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
onArticleClick?.invoke(article)
}
...
}
And init onArticleClick in your home fragment with RecyclerView:
adapter.apply {
onArticleClick = {
// show details fragment
}
}
I made an app from which I get data from TMDB API, everything seems to work but when I start the app, it displays only hint text, after scrolling the View get's updated with the data from the API
This is how it looks before scrolling
And this is how it looks after scrolling down a a bit
This is how I implemented it:
HomeFragment.kt
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private lateinit var popularMovies: RecyclerView
private lateinit var popularMoviesAdapter: MoviesAdapter
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
_binding = FragmentHomeBinding.inflate(inflater, container, false)
popularMovies = binding.popularMovies
popularMovies.layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
popularMoviesAdapter = MoviesAdapter(listOf())
popularMovies.addItemDecoration(DividerItemDecoration(context,DividerItemDecoration.VERTICAL))
popularMovies.adapter = popularMoviesAdapter
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MoviesRepository.getPopularMovies(page = 1,onSuccess = ::onPopularMoviesFetched,onError = ::onError)
}
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.updateMovies(movies)
}
private fun onError() {
Toast.makeText(context, getString(R.string.error_fetch_movies), Toast.LENGTH_SHORT).show()
}
MovieAdapter.kt
class MoviesAdapter(
private var movies: List<Movie>
) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater
.from(parent.context)
val binding = MovieItemBinding.inflate(view)
return MovieViewHolder(binding)
}
override fun getItemCount(): Int = movies.size
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(movies[position])
}
fun updateMovies(movies: List<Movie>) {
this.movies = movies
notifyDataSetChanged()
}
inner class MovieViewHolder(private val binding: MovieItemBinding) : RecyclerView.ViewHolder(binding.root) {
private val poster: ImageView = itemView.findViewById(R.id.item_movie_poster)
fun bind(movie: Movie) {
binding.movieTitle.text =movie.title
binding.movieReleaseDate.text = movie.releaseDate
binding.movieOverview.text = movie.overview
binding.movieReleaseDate.text = movie.releaseDate
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${movie.posterPath}")
.transform(CenterCrop())
.into(poster)
}
}
make adapter initialization on onViewCreated instead of onCreateView
I am writing a simple Kotlin app and I want to send data from LessonsModule1Fragment to DetailsFragment and try to pass bundle variables. Depending on the position, the fragment should be inflated with the appropriate layout.
I'm trying to pass a position between fragments, but I always get null
LessonsModule1Fragment
class LessonsModule1Fragment : Fragment(), OnLessonClickListener {
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<RecyclerViewLessonAdapter.ViewHolder>? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val binding = inflater.inflate(R.layout.fragment_module1_lessons, container, false)
val recyclerView = binding.findViewById<RecyclerView>(R.id.lesson_recycler_view)
layoutManager = LinearLayoutManager(context)
recyclerView.layoutManager = layoutManager
adapter = RecyclerViewLessonAdapter(this)
recyclerView.adapter = adapter
return binding
}
override fun lessonClick(lesson: Lesson, position: Int) {
var bundle = Bundle()
bundle.putInt("POSITION", position)
var transaction = this.parentFragmentManager.beginTransaction()
var fragment = DetailsFragment()
fragment.arguments = bundle
transaction.replace(R.id.lesson_recycle_layout, fragment).commit()
findNavController().navigate(R.id.action_lessonsModule1Fragment_to_detailsFragment)
}
DetailsFragment
class DetailsFragment : Fragment() {
private var num: Int? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
num = arguments?.getInt("POSITION")
when (num) {
0 -> return inflater.inflate(R.layout.fragment_details, container, false)
1 -> return inflater.inflate(R.layout.fragment_details2, container, false)
2 -> return inflater.inflate(R.layout.fragment_details3, container, false)
}
return inflater.inflate(R.layout.fragment_details, container, false)
}}
RecyclerViewLessonAdapter
class RecyclerViewLessonAdapter (private val lessonClickListener: OnLessonClickListener) : RecyclerView.Adapter<RecyclerViewLessonAdapter.ViewHolder>() {
private val lessons = DataStorage.getLessonsList()
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemTitle: TextView = itemView.findViewById(R.id.lesson_title)
val itemDetails: TextView = itemView.findViewById(R.id.lesson_description)
val itemImage: ImageView = itemView.findViewById(R.id.lesson_image)
fun lessonBind(lesson: Lesson, clickListener: OnLessonClickListener) {
itemView.setOnClickListener {
clickListener.lessonClick(lesson, adapterPosition)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.lesson_card, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemImage.setImageResource(lessons[position].images)
holder.itemTitle.text = lessons[position].title
holder.itemDetails.text = lessons[position].details
val lesson = lessons[position]
holder.lessonBind(lesson, lessonClickListener)
}
override fun getItemCount() = lessons.size
}
OnLessonClickListener
interface OnLessonClickListener {
fun lessonClick(lesson: Lesson, position: Int)
}
Perhaps there is a more suitable way?
I used navigation arguments, added these lines and it worked. Thanks for the hint
val action = LessonsModule1FragmentDirections.actionLessonsModule1FragmentToDetailsFragment(position)
findNavController().navigate(action)
The listview shows in the first fragment properly, that is the first screen of my app.
but if I put the list view in another fragment that is not the first screen of the app. The app will shut down when i click other fragments.
Please kindly help to solve this issue.
here with my code of FourFragment:
class FourFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_four, container, false)
}
var adapter:TutorialAdapter? = null
var listOfTutorial = TutorialList().list
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = TutorialAdapter(context!!, mylist = listOfTutorial)
tutorialListView.adapter = adapter
}
fun openYoutubeLink(youtubeID: String) {
val intentApp = Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + youtubeID))
val intentBrowser = Intent(Intent.ACTION_VIEW, Uri.parse("http://www.youtube.com/watch?v=" + youtubeID))
try {
this.startActivity(intentApp)
} catch (ex: ActivityNotFoundException) {
this.startActivity(intentBrowser)
}
}
inner class TutorialAdapter : BaseAdapter {
var mylist=ArrayList<Tutorial>()
var context: Context?=null
constructor(context: Context, mylist:ArrayList<Tutorial>):super() {
this.context=context
this.mylist=mylist
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
val tutorialget = mylist[position]
var inflater = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var myView = inflater.inflate(R.layout.tutorial_layout,null)
myView.title.text = tutorialget.name
myView.youtubeIcon.setImageResource(tutorialget.image!!)
myView.youtubeIcon.setOnClickListener {
openYoutubeLink(youtubeID = tutorialget.youtubeLink!!)
}
return myView
}
override fun getItem(position: Int): Any {
return mylist[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return mylist.size
}
}
}
Here with my code of MainActivity:
class MainActivity : AppCompatActivity() {
private var tabLayout: TabLayout? = null
var viewPager: ViewPager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Action Bar
val actionBar = supportActionBar
actionBar!!.setDisplayShowHomeEnabled(true)
actionBar.setBackgroundDrawable(ColorDrawable(Color.parseColor("#00FFFFFF")))
actionBar.setIcon(R.drawable.title)
actionBar.setDisplayShowTitleEnabled(false)
viewPager = findViewById(R.id.viewpager) as ViewPager
setupViewPager(viewPager!!)
tabLayout = findViewById(R.id.tabs) as TabLayout
tabLayout!!.setupWithViewPager(viewPager)
val headerView = (getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
.inflate(R.layout.custom_tab, null, false)
val linearLayoutOne = headerView.findViewById(R.id.ll) as LinearLayout
val linearLayout2 = headerView.findViewById(R.id.ll2) as LinearLayout
val linearLayout3 = headerView.findViewById(R.id.ll3) as LinearLayout
val linearLayout4 = headerView.findViewById(R.id.ll4) as LinearLayout
tabLayout!!.getTabAt(0)!!.setCustomView(linearLayoutOne)
tabLayout!!.getTabAt(1)!!.setCustomView(linearLayout2)
tabLayout!!.getTabAt(2)!!.setCustomView(linearLayout3)
tabLayout!!.getTabAt(3)!!.setCustomView(linearLayout4)
}
private fun setupViewPager(viewPager: ViewPager) {
val adapter = ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(OneFragment(), "ONE")
adapter.addFragment(TwoFragment(), "TWO")
adapter.addFragment(ThreeFragment(), "THREE")
adapter.addFragment(FourFragment(), "FOUR")
viewPager.adapter = adapter
}
internal inner class ViewPagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getCount(): Int {
return mFragmentList.size
}
fun addFragment(fragment: Fragment, title: String) {
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}
override fun getPageTitle(position: Int): CharSequence {
return mFragmentTitleList[position]
}
}
}
your ListView instance is null. You should instantiate tutorialListView inside onCreateView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view: View = inflater.inflate(R.layout.fragment_four, container, false)
tutorialListView = view.findViewById<ListView>(R.id.recycler_view)
return view
}
In the activities i was using intent to move and pass data. How can i make same thing between two fragments?
MainFragment:
class MainMarket: Fragment(){
var adapter : MainAdapter?=null
var listOfMkabala = ArrayList<MainMarketTickClass>()
companion object {
fun newInstance():Fragment{
var fb : MainMarket = MainMarket()
return fb
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.main_marker,container,false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
listOfMkabala .add ( MainMarketTickClass("123"))
listOfMkabala .add ( MainMarketTickClass(" 123"))
listOfMkabala .add ( MainMarketTickClass("123"))
adapter = MainAdapter (this.context!!,listOfMkabala)
list.adapter = adapter
}
}
My adapter:
class MainAdapter: BaseAdapter {
var listOfData= ArrayList<MainMarketTickClass>()
var context:Context?=null
constructor(context:Context, listOfMkabaln: ArrayList<MainMarketTickClass>):super(){
this.listOfData=listOfMkabaln
this.context=context
}
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
val mo3d = listOfData[p0]
var inflator = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var myView = inflator.inflate(R.layout.maintick, null)
myView.textView.text = mo3d.title!!
return myView
}
override fun getItem(p0: Int): Any {
return listOfData[p0]
}
override fun getItemId(p0: Int): Long {
return p0.toLong()
}
override fun getCount(): Int {
return listOfData.size
}
}
here, try this
Bundle bundle = new Bundle();
bundle.putString("id", "0");
fragment.setArguments(bundle);
...