I'm trying to use Tab Layout + Android Data Binding Library in a Kotlin project, but I'm not succeeding.
Please tell me what I'm doing wrong?
This is mine Activity:
class WoActivity : BaseActivity()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView(this, R.layout.activity_wo) as ActivityWoBinding
binding.wo = WorkOrder()
setSupportActionBar(binding.toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
binding.setHandler(this);
binding.setManager(getSupportFragmentManager());
}
companion object {
#BindingAdapter("handler") #JvmStatic
fun bindViewPagerAdapter(view: ViewPager, activity: WoActivity) {
val adapter = WOPagerAdapter(activity.supportFragmentManager)
adapter.addFragment(WoTabWoFragment.newInstance(), view.context.getString(R.string.work_order))
adapter.addFragment(WoTabScheFragment.newInstance(), view.context.getString(R.string.scheduling))
view.adapter = adapter
}
#BindingAdapter("pager") #JvmStatic
fun bindViewPagerTabs(view: TabLayout, pagerView: ViewPager) {
view.setupWithViewPager(pagerView, true)
}
}
}
And this is the final result: A empty tab without touch effects
Related
Hi guys how can I change this (FindviewById) to Data Binding because am having issues with
calling the views to the list so I need to change the (fvbi) to (binding) or
can I access my views here when im using the findviewbyid
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setListeners()
}// end of on create
private fun setListeners() {
val clickableViews: List<View> =
listOf()
for (item in clickableViews) {
item.setOnClickListener { makeColored(it) }
}
}
private fun makeColored(view: View) {
when (view.id) {
// Boxes using Color class colors for background
R.id.box_one_text -> view.setBackgroundColor(Color.DKGRAY)
R.id.box_two_text-> view.setBackgroundColor(Color.GRAY)
// Boxes using Android color resources for background
R.id.box_three_text -> view.setBackgroundResource(android.R.color.holo_green_light)
R.id.box_four_text -> view.setBackgroundResource(android.R.color.holo_green_dark)
R.id.box_five_text -> view.setBackgroundResource(android.R.color.holo_green_light)
else -> view.setBackgroundColor(Color.LTGRAY)
}
}
}
Yes you can access your views with findViewById
val clickableViews: List<View> =
listOf(findViewById(R.id.box_one_text), ...)
or with view binding you can do like this,
val clickableViews: List<View> =
listOf(binding.boxOneText, ...)
Using the binding structure makes more sense now and saves you a lot of code.
eg:if the activity was called HomeActivity it would be ActivityHomeBinding
build.gradle(module)
buildFeatures {
viewBinding true
dataBinding true
}
MainActivity
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.apply {
//eg:
button.setOnClickListener{
}
}
}
So my problem is that I created a OnPageChangeCallback (successfully) BUT i am not sure how to unregister it to avoid memory leak...
this is my mainActivity file everything is working properly otherwise :
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
lateinit var viewPager: ViewPager2
lateinit var navigationView: BottomNavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
viewPager = binding.viewPager
val adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
viewPager.adapter = adapter
navigationView = binding.bottomNavigation
navigationView.setOnItemSelectedListener { item ->
when(item.itemId){
R.id.nav_home -> viewPager.currentItem = 0
R.id.nav_info -> viewPager.currentItem = 1
}
true
}
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback(){
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
when(position){
0 -> navigationView.menu.findItem(R.id.nav_home).isChecked = true
1 -> navigationView.menu.findItem(R.id.nav_info).isChecked = true
}
}
})
}
// I am not sure I should do it this way
override fun onDestroy() {
super.onDestroy()
// I don't know how to put the callback in parameter (the ?? marks)
viewPager.unregisterOnPageChangeCallback(??)
}
}
If anyone can help me I want to learn how to do things properly I went to the android documentation but I didn't understand how to do it :/
As for Pager documentation
Remove a callback that was previously added via registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback).
Params:
callback – callback to remove
So to remove callback you'll need to save your ViewPager2.OnPageChangeCallback() in a variable and then pass it to unregisterOnPageChangeCallback in OnDestroy lifecycle method.
I'm updating my knowledge of Android, and I want to make an app with a Drawer menu, call an api and display the values inside a fragment. Starting from the template created by android studio itself, I have followed this tutorial:https://howtodoandroid.com/mvvm-retrofit-recyclerview-kotlin/ but I have a problem when programming the MainActivity.
Android studio template create this fragment (only changes the name of fragments):
class CheckListFragment : Fragment() {
private var _binding: FragmentCheckListBinding? = 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 {
val checklistViewModel =
ViewModelProvider(this).get(CheckListViewModel::class.java)
_binding = FragmentCheckListBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView: TextView = binding.textChecklist
checklistViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
The issue is that I don't know how I should call the viewmodel in that section, since I've tried in different ways, reading and reading examples, but none is exactly what I need. If you need me to show more parts of the code (viewmodel, viewmodelfactory etc. I can add it without any problem, although they are practically the same as those described in the tutorial)
In that example, in the MainActivity class we have this:
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
private lateinit var binding: ActivityMainBinding
lateinit var viewModel: MainViewModel
private val retrofitService = RetrofitService.getInstance()
val adapter = MainAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this, MyViewModelFactory(MainRepository(retrofitService))).get(MainViewModel::class.java)
binding.recyclerview.adapter = adapter
viewModel.movieList.observe(this, Observer {
Log.d(TAG, "onCreate: $it")
adapter.setMovieList(it)
})
viewModel.errorMessage.observe(this, Observer {
})
viewModel.getAllMovies()
}
But here not use fragments.
I would greatly appreciate help, or a link where I can see an example of this
How to share value between two RecyclerView? That are in one Activity? Below is my code with just one RecyclerView binding. That is OK and I understand how it works. What I want to add another RecyclerView in the same page. Both RecyclerViews are tables. When user clicked on item in first RecyclerView anything changed in second RecyclerView. Repository usually returns List of data from Room database.
What I appreciate would be some code example or maybe some tutorial with example.
This is MainActivity.kt
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val oneViewModel: OneViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMaindBinding.inflate(layoutInflater)
setContentView(binding.root)
val recyclerViewOneAdapter = RecyclerViewOneAdapter ()
binding.apply {
recyclerViewOneAdapter.apply {
adapter = Adapter
layoutManager = LinearLayoutManager(this#MainActivity)
}
oneViewModel.getItems.observe(this#MainActivity){
adapter.submitList(it)
}
}
}
}
This is ViewModel example
#HiltViewModel
class OneViewModel #Inject constructor(
repository: SybaseRepository
): ViewModel(){
val getItems = repository.getAllItemsFromDatabase().asLiveData()
}
What I suggest is something like this:
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val oneViewModel: OneViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMaindBinding.inflate(layoutInflater)
setContentView(binding.root)
val recyclerViewOneAdapter = RecyclerViewOneAdapter ()
val recyclerViewSecondAdapter = RecyclerViewSecondAdapter()
binding.apply {
recyclerViewOneAdapter.apply {
adapter = recyclerViewOneAdapter
layoutManager = LinearLayoutManager(this#MainActivity)
}
oneViewModel.getFirstItems.observe(this#MainActivity){
adapter.submitList(it)
}
recyclerViewTwoAdapter.apply {
adapter = recyclerViewSecondAdapter
layoutManager = LinearLayoutManager(this#MainActivity)
}
oneViewModel.getSecondItems.observe(this#MainActivity){
adapter.submitList(it)
}
}
}
}
This is ViewModel example
#HiltViewModel
class OneViewModel #Inject constructor(
repository: SybaseRepository
): ViewModel(){
val getFirstItems = repository.getFirsttemsFromDatabase().asLiveData()
val getSecondItems = repository.getSecondItemsFromDatabase().asLiveData()
}
Am I missing something? Or is it correct way?
I have an activity that has a button. On the button click I want to update text in text view.
I want to use ViewBinding instead of the normal findViewById
This is how I created the val binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater);
setContentView(binding.root)
binding.btnRoll.setOnClickListener {
rollDice()
}
}
Now in rollDice I want to update the text view but I'm not able to access binding which make sense because its scope is limited to onCreate() , so what is the best practice for this?
private fun rollDice() {
val random = Random().nextInt(6) + 1
binding.txt_random.setText("random")
}
You have two options.
1. Store in a property
Since the inflated content of Activity is fully bound to it's lifecycle, it's safe to keep the reference as a property
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater);
setContentView(binding.root)
binding.btnRoll.setOnClickListener {
rollDice()
}
}
private fun rollDice() {
val random = Random().nextInt(6) + 1
binding.txt_random.setText("random")
}
}
2. Pass the binding to the methods
That's what I usually do, it avoids creating a property where it's not really a necessity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater);
setContentView(binding.root)
binding.btnRoll.setOnClickListener {
rollDice(binding)
}
}
private fun rollDice(binding: ActivityMainBinding) {
val random = Random().nextInt(6) + 1
binding.txt_random.setText("random")
}
}
Both options are valid ways to make the binding visible to Activities methods.
Store the binding in an instance variable on the Activity. Then you have access to it from all the methods in the Activity.
As the question has accepted answer and it is already addressed but here is my approach to the viewBinding
class MainActivity : AppCompatActivity() {
private val binding by lazy{
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.btnRoll.setOnClickListener {
rollDice()
}
}
private fun rollDice() {
val random = Random().nextInt(6) + 1
binding.txt_random.setText("random")
}
}
I go with lazy initialization of binding so that way it is only intitialized if it is required.
More you can read about lazy initialization here
https://www.baeldung.com/kotlin/lazy-initialization