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
}
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 have created viewpager layout in which i am passing three different layout which need be update when any API is been called all three any time whether that page is been selected or not.
MainActivity.java
int[] layouts = new int[]{R.layout.one, R.layout.two, R.layout.three};
MyAdapter adapter = new MyAdapter(this, layouts);
MyAdapter.java
it is been extends to PagerAdapter
View view = layoutInflator.inflate(mLayouts[position], container, false);
container.addView(view);
How to initialize of all the id of different layout and how I can update the text or layout when API is been called.
Please try this way according to your requirement you can also create 3 childFragment with different layout and add them in list
class MyFragment : Fragment{
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return super.onCreateView(inflater, container, savedInstanceState)
initPager()
}
var fragmentAdapter: PagerAdapter? = null
fun initPager() {
fragmentAdapter = PagerAdapter(
childFragmentManager)
val fragment1 = ChildFragment("1")
fragmentAdapter!!.addFragment(fragment1,"title")
val fragment2 = ChildFragment("2")
fragmentAdapter!!.addFragment(fragment2,"title")
val fragment3 = ChildFragment("3")
fragmentAdapter!!.addFragment(fragment3,"title")
viewPager.adapter = fragmentAdapter
viewPager.offscreenPageLimit = 3
viewPager.addOnPageChangeListener(object :
ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
// do this instead, assuming your adapter reference
// is named mAdapter:
for (page in childFragmentManager.fragments) {
val frag: Fragment = fragmentAdapter!!.getItem(position)
if (frag != null && frag is ChildFragment && page is ChildFragment) {
page.refresh()
}
}
}
override fun onPageScrollStateChanged(state: Int) {}
})
}
}
class PagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()
val activeFragment: List<Fragment>
get() = mFragmentList
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]
}
}
class ChildFragment : Fragment{
var type:String = ""
constructor(type:String){
this.type = type
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return super.onCreateView(inflater, container, savedInstanceState)
if(type.equals("")){
//TODO LAYOUT LOGIC here according to type validation
}else{
//TODO LAYOUT LOGIC here according to type validation
}
refresh()
}
fun refresh() {
//TODO CALL API and update your layoyt here
}
}
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
}
i am using the same fragment for all the tabs.
But the problem is scroll of the recycler view of fragment working for last tab only.
Recycler view not scrolling for the previous tabs.
Below is my code of the FragmentPagerAdapter:
class DataViewPagerAdapter(fm: FragmentManager?) : FragmentStatePagerAdapter(fm) {
var fragmentList: MutableList<Fragment> = ArrayList()
var fragmentTitleList: MutableList<String> = ArrayList()
override fun getItem(position: Int): Fragment {
return DataFragment.newInstance(position)
}
override fun getCount(): Int {
return 3
}
override fun getPageTitle(position: Int): CharSequence? {
return "Deepak"
}
}
Here is my code for binding adapter:
var adapter = DataViewPagerAdapter( getChildFragmentManager())
dataViewpager.adapter = adapter
dataViewpager.offscreenPageLimit=1
tabLayout.setupWithViewPager(dataViewpager)
This is code of my fragment:
class DataFragment : Fragment() {
lateinit var recyclerView: RecyclerView
companion object {
fun newInstance(position: Int): DataFragment {
val fragment = DataFragment()
val args = Bundle()
args.putInt("KEY_POSITION", position)
args.putInt("KEY_ID",1)
fragment.setArguments(args)
return fragment
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
var view = LayoutInflater.from(activity).inflate(R.layout.fragment_data, null)
recyclerView = view.findViewById<RecyclerView>(R.id.recyclerView)
var adapter = DataFragmentAdapter()
recyclerView.layoutManager = GridLayoutManager(activity, 3)
recyclerView.adapter = adapter
return view;
}
}
I'm not sure if it's related to your problem but offscreenPageLimit should be the size of your adapter count.