I'm trying to convert my activity code to a fragment so I can make a tab layout.
Now when I copied the code from my activity to the fragment I got some errors. I tried to solve them but now my code doesn't work.
What should happen is that when I tap the textview it should flip and show a random text. In the Activity it works fine but now I tried converting it to a fragment it doesn't work. Nothing happens when I tap the textview.
Even when I remove the animation, and only use the textview the random texts won't appear when clicked in it. It seams like the setOnClickListener doesn't work.
Can someone tell me what I am doing wrong?
This is my fragment code:
import android.animation.AnimatorInflater
import android.animation.AnimatorSet
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
//import kotlinx.android.synthetic.main.TabStartKaartFragment.view.*
import kotlinx.android.synthetic.main.fragment_tab_start_kaart.*
class TabStartKaartFragment : Fragment() {
lateinit var front_anim: AnimatorSet
lateinit var back_anim: AnimatorSet
var isFront = false
#SuppressLint("ResourceType")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_tab_start_kaart, container, false)
val scale = getActivity()?.applicationContext?.resources?.displayMetrics?.density
tvstartkaart.cameraDistance = 8000 * scale!!
tvstartkaartachterkant.cameraDistance = 8000 * scale
front_anim = AnimatorInflater.loadAnimator(getActivity(), R.anim.font_animation) as AnimatorSet
back_anim = AnimatorInflater.loadAnimator(getActivity(), R.anim.back_animation) as AnimatorSet
val Carts = arrayOf("" +
"Bunch of random texts",
)
tvstartkaart.setOnClickListener() {
if (isFront) {
front_anim.setTarget(tvstartkaart)
back_anim.setTarget(tvstartkaartachterkant)
front_anim.start()
back_anim.start()
isFront = false
} else {
val random = Carts.random()
tvstartkaart.setText(random)
front_anim.setTarget(tvstartkaartachterkant)
back_anim.setTarget(tvstartkaart)
front_anim.start()
back_anim.start()
isFront = true
}
};
}
This is my original activity code:
import android.animation.AnimatorInflater
import android.animation.AnimatorSet
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import kotlin.random.Random
class StartKaartActivity : AppCompatActivity() {
lateinit var front_anim:AnimatorSet
lateinit var back_anim:AnimatorSet
var isFront = false
#SuppressLint("ResourceType")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start_kaart)
//Set Backbutton Bar & Logo
val display = supportActionBar
display?.title = ""
//display?.setDisplayHomeAsUpEnabled(true)
display?.setHomeAsUpIndicator(R.drawable.backarrow)
//display?.setLogo(R.drawable.logo)
display?.setIcon(R.drawable.logo);
display?.setDisplayShowHomeEnabled(true);
val scale = applicationContext.resources.displayMetrics.density
val tvstartkaart = findViewById<TextView>(com.fotf.klimaatambitiegame.R.id.tvstartkaart)
val tvstartkaartachterkant = findViewById<TextView>(com.fotf.klimaatambitiegame.R.id.tvstartkaartachterkant)
tvstartkaart.cameraDistance = 8000 * scale
tvstartkaartachterkant.cameraDistance = 8000 * scale
front_anim = AnimatorInflater.loadAnimator(applicationContext, R.anim.font_animation) as AnimatorSet
back_anim = AnimatorInflater.loadAnimator(applicationContext, R.anim.back_animation) as AnimatorSet
val Carts = arrayOf("" +
"Bunch of random texts",
)
tvstartkaart.setOnClickListener() {
if (isFront) {
front_anim.setTarget(tvstartkaart)
back_anim.setTarget(tvstartkaartachterkant)
front_anim.start()
back_anim.start()
isFront = false
} else {
val random = Carts.random()
tvstartkaart.setText(random)
front_anim.setTarget(tvstartkaartachterkant)
back_anim.setTarget(tvstartkaart)
front_anim.start()
back_anim.start()
isFront = true
}
};
}
}
This is my xml fragment file:
<?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"
android:background="#drawable/achtergrondnowhite"
tools:context=".TabStartKaartFragment">
<TextView
android:id="#+id/textView"
android:layout_width="372dp"
android:layout_height="62dp"
android:layout_marginTop="28dp"
android:paddingHorizontal="10dp"
android:text="Klik op de kaart om een nieuwe kaart te krijgen. \n\nGeef het goede antwoord op de vraag en verdien een houder."
android:textColor="#color/white"
android:textSize="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.487"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvstartkaart"
android:layout_width="350dp"
android:layout_height="450dp"
android:background="#drawable/kaartstartvoorkant"
android:gravity="left|center"
android:padding="15dp"
android:paddingHorizontal="10dp"
android:text=""
android:textColor="#color/black"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView"
app:layout_constraintVertical_bias="0.508" />
<TextView
android:id="#+id/tvstartkaartachterkant"
android:layout_width="353dp"
android:layout_height="453dp"
android:background="#drawable/kaartstartachterkant"
android:gravity="left|center"
android:padding="15dp"
android:paddingHorizontal="10dp"
android:textColor="#color/black"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
My front animation:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:duration="1000"
/>
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="500"
android:duration="1"
/>
</set>
My back animation:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0"
/>
<objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:repeatMode="reverse"
android:duration="1000"
/>
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="500"
android:duration="0"
/>
</set>
Related
I was making a custom view to learn android studio. In the meantime, to check if my code work correctly, I write the following code. I expected
4layout_width=wrap_content
layout_height=wrap_content
id=#+id/customView
is written in textView but still 4 is been writing. what went wrong? is this android studio's error? To make correct code, how can I do?
custom.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="match_parent"
android:orientation="vertical">
<com.example.myapplication.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/customView"/>
</LinearLayout>
activity_main.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=".MainActivity"
android:orientation="vertical"
android:id="#+id/root_layout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView"
android:text="4"/>
</LinearLayout>
MainActivity.kt
package com.example.myapplication
import android.content.Context
import android.content.Context.LAYOUT_INFLATER_SERVICE
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
val layout = findViewById<LinearLayout>(R.id.root_layout)
inflater.inflate(R.layout.custom, layout, true)
}
}
class MyView : View {
constructor(context : Context) : super(context)
constructor(context : Context, attr : AttributeSet?) : super(context, attr){
val inflater = context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
val layout = inflater!!.inflate(R.layout.activity_main, null)
val textView = layout!!.findViewById<TextView>(R.id.textView)
if(attr != null){
for(i in 0 until attr.attributeCount){
val string = "${attr.getAttributeName(i)}=${attr.getAttributeValue(i)}\n"
textView.append(string)
}
}
}
constructor(context : Context, attr : AttributeSet?, defStyleAttr : Int) :
super(context, attr, defStyleAttr)
}
The list doesn't display on screen and it doesn't throw any error.
Am I doing something wrong connecting the adapter and layoutmanager?
Whenever I open the fragment it writes the following line: "W/RecyclerView: No adapter attached; skipping layout" and it doesn't change anything to the fragment.
This is the MainActivity.kt:
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.ListView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomnavigation.BottomNavigationView
class MainActivity : AppCompatActivity() {
val addnewmoviefragment = AddNewMovieFragment()
val movielistfragment = MovieListFragment()
val moviedetailsfragment = MovieDetailsFragment()
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<MovieListViewAdapter.ViewHolder>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
replaceFragment(movielistfragment)
val movies: MutableList<Movies> = initList()
layoutManager = LinearLayoutManager(this)
adapter = MovieListViewAdapter(this, movies)
val button = findViewById<BottomNavigationView>(R.id.bottom_navigation)
button.setOnNavigationItemSelectedListener {
when(it.itemId){
R.id.ic_movieadd -> replaceFragment(addnewmoviefragment)
R.id.ic_moviedetails -> replaceFragment(movielistfragment)
}
true
}
//val submitButton = findViewById<Button>(R.id.submitButton)
//val cancelButton = findViewById<Button>(R.id.cancelButton)
}
private fun replaceFragment(fragment: Fragment){
if(fragment!=null){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.commit()
}
}
private fun initList(): MutableList<Movies>{
return mutableListOf(
Movies(1, "Inception", "Very good movie!", "James Gun", Actors("Simeon")),
Movies(2, "Extracton", "Extracting!", "Christopher Nolan", Actors("Ivan"))
)
}
MovieListViewAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.EditText
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class MovieListViewAdapter(val context: Context, val allMovies: MutableList<Movies>):
RecyclerView.Adapter<MovieListViewAdapter.ViewHolder>(){
class ViewHolder(view: View): RecyclerView.ViewHolder(view){
val id: TextView
val name: TextView
val description: TextView
val producer: TextView
val actors: TextView
init{
id = view.findViewById(R.id.movieId)
name = view.findViewById(R.id.movieNameId)
description = view.findViewById(R.id.movieDescriptionId)
producer = view.findViewById(R.id.movieProducerId)
actors = view.findViewById(R.id.movieActorsId)
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recycler_view_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentMovie: Movies = allMovies[position]
holder.id.text = currentMovie.id.toString()
holder.name.text = currentMovie.name
holder.description.text = currentMovie.description
holder.producer.text = currentMovie.producer
holder.actors.text = currentMovie.actors.toString()
}
override fun getItemCount(): Int {
return allMovies.size
}
}
MovieListFragment.kt
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MovieListFragment : Fragment() {
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<MovieListViewAdapter.ViewHolder>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view: View = inflater.inflate(R.layout.fragment_movie_list, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Toast.makeText(context, "You are viewing the movie list!", Toast.LENGTH_SHORT).show()
}
}
Movies.kt
data class Movies(
val id: Int,
val name: String,
val description: String,
val producer: String,
val actors: Actors
) {
}
Actors.kt
data class Actors(
val name: String
){}
fragment_movie_list.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=".MovieListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/movieListId"
android:layout_width="389dp"
android:layout_height="589dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.246" />
</androidx.constraintlayout.widget.ConstraintLayout>
recycler_view_row.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">
<TextView
android:id="#+id/indexId"
android:layout_width="68dp"
android:layout_height="23dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.045"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.079" />
<TextView
android:id="#+id/nameId"
android:layout_width="68dp"
android:layout_height="21dp"
android:layout_marginBottom="620dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.046"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/descriptionId"
android:layout_width="68dp"
android:layout_height="19dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/nameId" />
<TextView
android:id="#+id/producerId"
android:layout_width="68dp"
android:layout_height="23dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/descriptionId" />
<TextView
android:id="#+id/actorsId"
android:layout_width="65dp"
android:layout_height="21dp"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/producerId" />
</androidx.constraintlayout.widget.ConstraintLayout>
From the code, I see that the adapter gets instantiated, but never attached to the recyclerView.
You are just missing the following line in the onViewCreated override in the fragment.
findViewById<RecyclerView>(R.id.movieListId).adapter = adapter
https://developer.android.com/guide/topics/ui/layout/recyclerview
I got stuck in the situation ..
The idea is ,make the display total price with the first item selected multiply the first value of popup menu which is 1 ,I was trying to make the first item as the initial price ,but the total price became the first item price * selected value of popup menu ....
I have no idea what happend ..
Could you please help me check my code ,thank you so much in advance :
Product.kt
package com.gearsrun.recyclerviewfragmentapplication
data class Product(var price : String)
ProductAdapter.kt
package com.gearsrun.recyclerviewfragmentapplication
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_layout.view.*
class ProductAdapter(private val productList:List<Product>): RecyclerView.Adapter<ProductAdapter.ProductViewHolder>() {
private var selectedItemPosition :Int = 0
private var mlistener : onItemClickListener ?=null
fun interface onItemClickListener{
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: onItemClickListener){
mlistener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout,parent,false)
return ProductViewHolder(itemView,mlistener)
}
override fun onBindViewHolder(holder: ProductAdapter.ProductViewHolder, position: Int) {
val currentItem = productList[position]
holder.price.text = currentItem.price
holder.itemView.setOnClickListener {
selectedItemPosition = position
notifyDataSetChanged()
}
if(selectedItemPosition == position){
holder.itemView.cardView.setBackgroundColor(Color.parseColor("#FAFAD2"))
}else{
holder.itemView.cardView.setBackgroundColor(Color.parseColor("#FFFFFF"))
}
}
override fun getItemCount() = productList.size
class ProductViewHolder(itemView: View,listener: onItemClickListener):RecyclerView.ViewHolder(itemView) {
val price : TextView = itemView.price
init {
itemView.setOnClickListener {
listener?.onItemClick(absoluteAdapterPosition)
}
}
}
}
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.HomeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<!--Recycler View-->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/giftRecycleView"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_marginTop="30dp"
/>
<!--Selected option will display here-->
<LinearLayout
android:layout_width="match_parent"
android:gravity="center"
android:padding="16dp"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Select amount : "
android:textColor="#color/black"
android:textSize="18sp"/>
<TextView
android:id="#+id/selectedTv"
android:layout_width="100dp"
android:layout_height="60dp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginLeft="10dp"
android:background="#color/black"
android:textColor="#color/white"
android:textSize="20sp"
android:text="1" />
</LinearLayout>
<!--Total price-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Total : "
android:textColor="#color/black"
android:textSize="18sp"/>
<TextView
android:id="#+id/price_t"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:textSize="18sp"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
HomeFragment.kt
package com.gearsrun.recyclerviewfragmentapplication.fragment
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.gearsrun.recyclerviewfragmentapplication.Product
import com.gearsrun.recyclerviewfragmentapplication.ProductAdapter
import com.gearsrun.recyclerviewfragmentapplication.R
import kotlinx.android.synthetic.main.fragment_home.*
import kotlin.properties.Delegates
class HomeFragment : Fragment(R.layout.fragment_home) {
//recyclerview
private val productList = generateProduct()
private var select_price = 0 // gift price
private var select_num = 1 //popup menu value
private fun refreshOutput(){
price_t.text = (select_num*select_price).toString()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = ProductAdapter(productList)
giftRecycleView.adapter = adapter
adapter.setOnItemClickListener{position : Int->
select_price = productList[position].price.toInt()
refreshOutput()
}
giftRecycleView.layoutManager = LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false)
//popup menu
val popupMenu = PopupMenu(
context,
selectedTv
)
for(i in 0..5){
popupMenu.menu.add(Menu.NONE,i,i,i.toString())
}
//handle menu clicks
popupMenu.setOnMenuItemClickListener { menuItem ->
val i = menuItem.itemId+1
selectedTv.text = i.toString()
select_num = i
refreshOutput()
true
}
//handle menu click to show menu
selectedTv.setOnClickListener {
popupMenu.show()
}
//calculate the total price
refreshOutput()
}
private fun generateProduct(): List<Product>{
val list = ArrayList<Product>()
list.add(Product("5"))
list.add(Product("6"))
list.add(Product("7"))
return list
}
}
If I understood correctly the popup has a value other than textView and this value is used for the calculation. And the problem comes before selecting amout.
You can set a default popup value like here
How to set a default selected option in Android popup menu?
First I have to say something about this piece of code. If you find yourself copy-pasting code like this, you need to step back and simplify it. Your setup of the popup menu could be cut down to this:
//add menu items to popup menu
for (i in 0..4) {
popupMenu.menu.add(Menu.NONE, i, i, (i + 1).toString())
}
//handle menu clicks
popupMenu.setOnMenuItemClickListener { menuItem ->
val i = menuItem.itemId + 1
selectedTv.text = i.toString()
select_num = i
total_price = select_num * select_price
price_t.text = total_price.toString()
true
}
And here you can see the problem. You are only updating the calculated value when the popup menu is clicked, but not when a different view is selected in the adapter. You should create a function that updates the calculation and puts it in the text view, and call them from both listeners (adapter's item click listener and the popup menu listener).
It's kind of weird to do this with local variables and more unusual to define a function inside onViewCreated(). You should promote them to private properties so the function goes outside onViewCreated(). Eliminate the total_price variable because you never use it in a useful way. It will always be out of date once something else changes, so it is not helping you at all. So your code will end up looking like:
private var select_price = 0
private var select_num = 1
private fun refreshOutput() {
price_t.text = (select_num * select_price).toString()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = ProductAdapter(productList)
giftRecycleView.adapter = adapter
adapter.setOnItemClickListener(object :ProductAdapter.onItemClickListener{
override fun onItemClick(position: Int) {
select_price = productList[position].price.toInt()
refreshOutput()
}
})
giftRecycleView.layoutManager = LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false)
//popup menu
val popupMenu = PopupMenu(
context,
selectedTv
)
for (i in 0..5) {
popupMenu.menu.add(Menu.NONE, i, i, i.toString())
}
//handle menu clicks
popupMenu.setOnMenuItemClickListener { menuItem ->
val i = menuItem.itemId + 1
selectedTv.text = i.toString()
select_num = i
refreshOutput()
true
}
//handle menu click to show menu
selectedTv.setOnClickListener {
popupMenu.show()
}
//calculate the total price
refreshOutput() // show initial value
}
And a couple of tips about Kotlin. You are misusing lateinit for your listener. lateinit is for properties that are guaranteed to be initialized before they are accessed anywhere else in your code. This is mostly only applicable for classes that are instantiated by reflection, and the subclass's code's first entry point is somewhere other than the constructor, like in an Activity's onCreate() or Fragment's onCreateView()/onViewCreated(). This is not true for your Adapter, so by marking the property lateinit, you are only using the keyword to subvert null-safety. The property should simply be nullable, and a null-safe call should be used with it in the one place where you actually use it.
Also, if you define your interface as a fun interface, you can take advantage of lambda syntax.
private var mlistener : onItemClickListener? = null
fun interface onItemClickListener{
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: onItemClickListener){
mlistener = listener
}
//...
// In product view holder:
init {
itemView.setOnClickListener {
listener?.onItemClick(absoluteAdapterPosition)
}
}
// In Fragment:
adapter.setOnItemClickListener { position: Int ->
select_price = productList[position].price.toInt()
refreshOutput()
}
And finally, my answer is just explaining how to get your current code working. Really, you should convert the select_price and select_num into LiveDatas or StateFlows in a ViewModel. Then you would use these values to set up the state of your UI elements, and they will persist correctly if the screen is rotated. The way it is now, when the screen rotates, your currently selected price and number will be lost.
I've made an app with 4 fragments, each of which represents the app page. Now, inside one of the fragments I've got a ToggleButton. I am trying to get OnClickListener so that it changes the button's background colour once clicked. I'm coding this in my MainActivity.kt and not in the Fragments. However, it crashes the app without logcat.
package com.example.myassignment
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.example.myassignment.Fragments.DestinationsFragment
import com.example.myassignment.Fragments.HelpFragment
import com.example.myassignment.Fragments.HomeFragment
import com.example.myassignment.Fragments.SettingsFragment
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_settings.*
class MainActivity : AppCompatActivity() {
private val helpFragment = HelpFragment()
private val settingsFragment = SettingsFragment()
private val homeFragment = HomeFragment()
private val destinationsFragment = DestinationsFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
replaceFragment(homeFragment)
bottom_navigation.setOnNavigationItemSelectedListener{
when(it.itemId){
R.id.ic_home -> replaceFragment(homeFragment)
R.id.ic_destinations -> replaceFragment((destinationsFragment))
R.id.ic_help -> replaceFragment(helpFragment)
R.id.ic_settings -> replaceFragment(settingsFragment)
}
true
}
// This is the onclick for the button which is inside a fragment (SettingsFragment)
btnReset.setOnClickListener { resetColour() }
}
// Function for the onclick function
private fun resetColour() {
btnReset.setBackgroundColor(Color.parseColor("#3E3E3E"))
}
private fun replaceFragment(fragment: Fragment){
if (fragment !=null){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.commit()
}
}
}
And here's my SettingsFragment code (I haven't changed anything)
package com.example.myassignment.Fragments
import android.graphics.Color
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.myassignment.R
import kotlinx.android.synthetic.main.fragment_settings.*
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [SettingsFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class SettingsFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
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_settings, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment SettingsFragment.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
SettingsFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Here's the XML for the fragment, the button I'm trying to change is btnReset
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".Fragments.SettingsFragment"
android:background="#color/black">
<TextView
android:id="#+id/txtSettings"
android:layout_width="323dp"
android:layout_height="102dp"
android:layout_marginTop="16dp"
android:background="#color/orange"
android:gravity="center"
android:text="Settings"
android:textAlignment="center"
android:textColor="#color/black"
android:textSize="60sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/txtNotifications"
android:layout_width="223dp"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:background="#color/grey"
android:gravity="center"
android:text="Notifications"
android:textAlignment="center"
android:textColor="#color/black"
android:textSize="20sp"
app:layout_constraintEnd_toStartOf="#+id/btnNotifications"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtSettings" />
<TextView
android:id="#+id/txtNightMode"
android:layout_width="223dp"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:background="#color/grey"
android:gravity="center"
android:text="Night Mode"
android:textAlignment="center"
android:textColor="#color/black"
android:textSize="20sp"
app:layout_constraintEnd_toStartOf="#+id/btnNightMode"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtNotifications" />
<TextView
android:id="#+id/txtWIFI"
android:layout_width="223dp"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:background="#color/grey"
android:gravity="center"
android:text="WI-FI Only"
android:textAlignment="center"
android:textColor="#color/black"
android:textSize="20sp"
app:layout_constraintEnd_toStartOf="#+id/btnWIFI"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtNightMode" />
<ToggleButton
android:id="#+id/btnNotifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="44dp"
android:layout_marginRight="44dp"
android:text="ToggleButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtSettings"
android:background="#color/cyan"
/>
<ToggleButton
android:id="#+id/btnNightMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="44dp"
android:layout_marginRight="44dp"
android:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnNotifications"
android:background="#color/cyan"
/>
<ToggleButton
android:id="#+id/btnWIFI"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="44dp"
android:layout_marginRight="44dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnNightMode"
android:background="#color/cyan"/>
<Button
android:id="#+id/btnReset"
android:layout_width="337dp"
android:layout_height="154dp"
android:layout_marginTop="24dp"
android:text="Reset"
android:background="#color/cyan"
android:backgroundTint="#color/orange"
android:textColor="#color/black"
android:textSize="50sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtWIFI" />
</androidx.constraintlayout.widget.ConstraintLayout>
----- Edit -----
New SettingsFragment code:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
btnReset.setOnClickListener { resetColour() }
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_settings, container, false)
}
private fun resetColour() {
btnReset.setBackgroundColor(Color.parseColor("3E3E3E"))
}
Here's the logcat
2021-02-17 08:20:32.594 11717-11717/com.example.myassignment E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myassignment, PID: 11717
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.example.myassignment.Fragments.SettingsFragment.onCreateView(SettingsFragment.kt:40)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
This line is your problem.
btnReset.setOnClickListener { resetColour() }
btnReset lives in your fragment, but you are trying to reference it from your activity. Try moving that code to the fragment which inflates the view that btnReset lives in.
See the line import kotlinx.android.synthetic.main.fragment_settings.* in your activity class? This is what made it seem like btnReset was a valid property to reference in your activity. You should delete that line.
Lastly, it's very unlikely that this 'crashed without logcat'. You probably have some sort of filter on your logcat that's preventing you from seeing the crash.
I am trying to implement TableLayout on ViewPager but i can not use method setupWithViewPager because android studio says that it is unresolved reference.
This TableLayout and ViewPager should be implemented in fragment (AboutFragment.kt)
My ViewPager works perfectly fine without TabLayout, I mean i can swipe between fragments.
Does somebody have solution?
Here is my AboutFragment:
package hr.ferit.brunozoric.taskie.ui.fragments
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import hr.ferit.brunozoric.taskie.R
import hr.ferit.brunozoric.taskie.ui.adapters.ViewPagerAdapter
import hr.ferit.brunozoric.taskie.ui.fragments.base.BaseFragment
import kotlinx.android.synthetic.main.fragment_about.*
class AboutFragment: BaseFragment() {
override fun getLayoutResourceId(): Int {
return R.layout.fragment_about
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupPager()
}
private fun setupPager() {
viewPager.adapter = ViewPagerAdapter(fragmentManager!!)
tabLayout.setupWithViewPager(viewPager) // I have error in this line :(
}
companion object{
fun newIstance(): Fragment {
return AboutFragment()
}
}
}
Here is my ViewPagerAdapter:
package hr.ferit.brunozoric.taskie.ui.adapters
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import hr.ferit.brunozoric.taskie.ui.fragments.AboutAplicationFragment
class ViewPagerAdapter(manager :FragmentManager) : FragmentStatePagerAdapter(manager) {
companion object{
const val NUM_OF_PAGES =2
const val PAGE_ONE_TITLE = "About Application"
const val PAGE_TWO_TITLE = "About Author"
}
private val frags = mutableListOf<Fragment>(AboutAplicationFragment(),AboutAplicationFragment())
private val titles = mutableListOf(PAGE_ONE_TITLE, PAGE_TWO_TITLE)
override fun getItem(position: Int): Fragment {
return frags[position]
}
override fun getPageTitle(position :Int) : String{
return titles[position]
}
override fun getCount(): Int {
return NUM_OF_PAGES
}
}
Here is XML file with ViewPager and TabLayout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/tabLayout"
/>
<TableLayout
android:id="#+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
/>
</RelativeLayout>
You have used <TableLayout> instead of <TabLayout>.