I am trying to find the position of widgets but somehow it always returns 0. I have followed most suggestions but all of them still returns 0. I finally ended up with this, but still it returns a 0. I have read somewhere that the reason it returns a 0 is because I am calling for the position before onCreate() has finished parsing the layout specified in the accompanying .xml file.
Where then should I put the call to the positions then?
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fuelCalculationConstraintLayout"
tools:context=".FuelCalculation">
<ImageView
android:id="#+id/imageViewIconHome"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/icon_home" />
<TextView
android:id="#+id/textViewSettingValues"
android:text="Settings:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/imageViewIconHome"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
android:height="30dp"
android:width="100dp"/>
<TextView
android:id="#+id/textViewScratchPad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scratch Pad"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.fuelcalculation3
import android.content.Context
import android.content.Intent
import android.graphics.Point
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class FuelCalculation : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fuel_calculation)
val updateTextViewSettingValues = findViewById<TextView>(R.id.textViewSettingValues)
val updateTextViewScratchPad = findViewById<TextView>(R.id.textViewScratchPad)
val imageIconHomeClick = findViewById<ImageView>(R.id.imageViewIconHome)
imageIconHomeClick.setOnClickListener(){
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
fun View.getLocationOnScreen(): Point {
val location = IntArray(2)
this.getLocationOnScreen(location)
return Point(location[0], location[1])
}
val location = updateTextViewSettingValues.getLocationOnScreen()
val absX = location.x
val absY = location.y
val getTextViewSettingsRootView = updateTextViewSettingValues.rootView
val getTextViewSettingsValueWidth = updateTextViewSettingValues.width
val getTextViewSettingsValueTop = updateTextViewSettingValues.top
val getTextViewSettingsValueLeft = updateTextViewSettingValues.left
val getTextViewSettingsValueRight = updateTextViewSettingValues.right
val getTextViewSettingsValueMeasuredWidth = updateTextViewSettingValues.measuredWidth
val getTextViewSettingsValueA = updateTextViewSettingValues.measuredWidth
updateTextViewScratchPad.text = "Top=$getTextViewSettingsValueTop" // returns 0
updateTextViewScratchPad.text = "$absX" // returns 0
}
}
See Why getLocationOnScreen(location) always returns 0?. You are to use postDelayed or getViewTreeObserver to get view position after an activity has rendered. Maybe you should use requestLayout.
Related
I'm trying to create an application and considering my level it's not easy! I hope you could help me since I didn't succeed with the many links I found on the internet.
I can't add the onClick function of View.OnClickListener, each time the Intent function is not recognized. I tried to implement it in the UserViewHolder and FirestoreRecyclerAdapter class but it doesn't work.
Here is my current code:
---------- kotlin part ---------
package edu.stanford.rkpandey.emojistatus
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.firebase.ui.firestore.FirestoreRecyclerAdapter
import com.firebase.ui.firestore.FirestoreRecyclerOptions
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import kotlinx.android.synthetic.main.activity_main.*
data class User(
val displayName: String? = "",
val emojis: String? = ""
)
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainActivity : AppCompatActivity() {
private val db = Firebase.firestore
private lateinit var auth: FirebaseAuth
// Query the users collection
private val query = db.collection("users")
val options = FirestoreRecyclerOptions.Builder<User>()
.setQuery(query, User::class.java)
.setLifecycleOwner(this).build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
auth = Firebase.auth
val adapter = object: FirestoreRecyclerAdapter<User, UserViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(this#MainActivity).inflate(R.layout.item_pack, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(
holder: UserViewHolder,
position: Int,
model: User
) {
val tvName: TextView = holder.itemView.findViewById(R.id.title)
val tvEmojis: TextView = holder.itemView.findViewById(R.id.excerpt)
tvName.text = model.displayName
tvEmojis.text = model.emojis
}
}
rvUsers.adapter = adapter
rvUsers.layoutManager = LinearLayoutManager(this)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.miLogout) {
Log.i("MainActivity", "Logout")
auth.signOut()
val logoutIntent = Intent(this, LoginActivity::class.java)
logoutIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(logoutIntent)
}
return super.onOptionsItemSelected(item)
}
}
------- xml part -------
=> activity_main
<?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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvUsers"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
=> item_pack
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="100sp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="12sp"
android:layout_marginTop="12sp"
android:layout_marginEnd="12sp"
android:focusable="true"
android:clickable="true"
app:cardCornerRadius="10dp"
android:foreground="?android:attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="#color/colorPack">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5sp"
style="#style/NoteTitleFont"
android:textColor="#color/colorTitle"
tools:text="Note 1" />
<TextView
android:id="#+id/excerpt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12sp"
android:layout_below="#id/title"
android:maxLines="1"
android:ellipsize="end"
android:textStyle="italic"
android:textColor="#color/colorDescribe"
tools:text="test text va se trouver ici, ça va contenir le début de la description du package." />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
This code gives this result :
I would like that when I click on one of the carviews it can go to the activity_pack_detail.
Do you know how to do Intent to PackDetailActivity?
I get this error no matter what I do =>
You're getting that error because you are calling Intent's class constructor with a wrong argument. The first argument should be a Context and not a View. The keyword this is referring in your code to a View and not to a context, hence the error.
To solve this, you have to pass a Context object like this:
val i = Intent(view.getContext(), PackDetailActivity::class.java)
And your error will go away.
I have two activities. I want to receive a value from MainActivity, change the value from SecondActivity, and send it back to editText of MainActivity.
However, the editText of MainActivity does not change even if I move back from SecondActivity.
Could you tell me which part I misunderstood?
MainActivity.kt code
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import com.example.homework07.databinding.ActivityMainBinding
import com.google.android.material.snackbar.Snackbar
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val result = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
it.resultCode
val ret_num = it.data?.getIntExtra("result", 0) ?:0
Snackbar.make(binding.root, "result code: ${it.resultCode}, result number: ${ret_num}", Snackbar.LENGTH_SHORT).show()
}
binding.button.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("num", Integer.parseInt(binding.editText.text.toString()))
result.launch(intent)
}
}
}
activity_main.xml code
<?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=".MainActivity">
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="Enter number only"
android:inputType="number"
android:minHeight="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="START SECOND ACTIVITY"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/editText" />
</androidx.constraintlayout.widget.ConstraintLayout>
SecondActivity.kt code
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.example.homework07.databinding.ActivitySecondBinding
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivitySecondBinding.inflate(layoutInflater)
setContentView(binding.root)
val viewModel = ViewModelProvider(this)[MyViewModel::class.java]
viewModel.myLiveData.observe(this) {
binding.textView.text = "$it"
}
var num2 = intent?.getIntExtra("num", 0) ?:0
binding.textView.text = "$num2"
binding.buttonInc.setOnClickListener {
num2 = viewModel.myLiveData.value ?:0
viewModel.myLiveData.value = Integer.parseInt(binding.textView.text.toString()) + 1
}
binding.buttonDec.setOnClickListener {
num2 = viewModel.myLiveData.value ?:0
viewModel.myLiveData.value = Integer.parseInt(binding.textView.text.toString()) - 1
}
setResult(0, Intent().putExtra("result", num2))
}
}
activity_second.xml code
<?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=".SecondActivity">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/buttonInc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="INCREASE"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/buttonDec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="DECREASE"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toBottomOf="#+id/buttonInc" />
</androidx.constraintlayout.widget.ConstraintLayout>
MyViewModel.kt code
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
val myLiveData : MutableLiveData<Int> = MutableLiveData()
}
Thank you.
You made a mistake:
setResult(0, Intent().putExtra("result", num2))
replace 0 (it cancel result) to Activity.RESULT_OK.
This line has a mistake (resultCode) parameter:
setResult(0, Intent().putExtra("result", num2))
It's better if you make a constant for resultCode or do something like this in your SecondActivity.kt.
setResult(200, Intent().putExtra("result", num2))
And in MainActivity.kt add this is "registerActivityForResult":
if(it.resultCode == 200){
//Rest of the code here
}
Or you can also follow Below answer
You should set the result before finishing the SecondActivity with:
intent.putExtra("result", num2)
setResult(Activity.RESULT_OK, intent)
finish()
The, after checking the result, retrieve the value in the FirstActivity:
val result = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val ret_num = result.data?.getIntExtra("result", 0) ?:0
Snackbar.make(binding.root, "result code: ${it.resultCode}, result number: ${ret_num}", Snackbar.LENGTH_SHORT).show()
}
}
I am trying to show a list of transactions in a recycler view.
Each transaction is represented by a CardView inside a constraint layout (see item_transaction.xml).
Somehow, the functions of my RecyclerView Adapter (onCreateViewHolder, onBindViewHolder and getItemCount) are never called (logs are never displayed - I removed most of them so the code is easier to read).
Therefore, the RecyclerView content doesn't display on my app.
It is also worth pointing out that I have a list of transactions called data in my adapter. Whenever I set the data in the list, it does update. No issues there.
💡 I know notifyDataSetChanged() isn't clean, I'll change it later on when I manage to get everything working.
Here is a preview of what I want so you can grasp the idea better :
EDIT : My fragment displays normally and has no problem executing code.
Hang on tight because there is a lot of code :
package com.example.manage.manageit.database
class Transaction(
var value: Long = 0,
var note: String = "",
var budget: String = "",
var creationTime: Long = System.currentTimeMillis()
)
Transaction.kt
package com.example.manage.manageit.home
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.manage.manageit.databinding.ActivityHomeBinding
import androidx.fragment.app.Fragment
import com.example.manage.manageit.R
import com.example.manage.manageit.about.AboutFragment
import com.example.manage.manageit.budget.TransactionFragment
class HomeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHomeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_home)
binding.bottomNavigation.menu.getItem(0).isCheckable = true
setFragment(HomeFragment())
binding.bottomNavigation.setOnItemSelectedListener {menu ->
when(menu.itemId){
R.id.homeFragmentButton -> {
setFragment(HomeFragment())
true
}
R.id.transactionFragmentButton -> {
setFragment(TransactionFragment())
true
}
R.id.aboutFragmentButton -> {
setFragment(AboutFragment())
true
}
else -> false
}
}
}
private fun setFragment(fr : Fragment){
val frag = supportFragmentManager.beginTransaction()
frag.replace(R.id.myNavHostFragment,fr)
frag.commit()
}
}
HomeActivity.kt : It contains a BottomNavigationView and my fragment.
package com.example.manage.manageit.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.manage.manageit.R
import com.example.manage.manageit.adapters.TransactionAdapter
import com.example.manage.manageit.database.Transaction
import com.example.manage.manageit.databinding.FragmentHomeBinding
import java.util.logging.Logger
class HomeFragment : Fragment() {
private var logger = Logger.getLogger(HomeFragment::class.java.name)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentHomeBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_home,
container,
false
)
val recyclerView: RecyclerView = binding.transactionList
val transactionAdapter = TransactionAdapter()
recyclerView.adapter = transactionAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
transactionAdapter.data = listOf(
Transaction(10, "Transaction note", "Groceries"),
Transaction(20, "Transaction note", "Groceries"),
Transaction(30, "Transaction note", "Groceries")
)
return inflater.inflate(R.layout.fragment_home, container, false)
}
}
HomeFragment.kt
package com.example.manage.manageit.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.manage.manageit.R
import com.example.manage.manageit.database.Transaction
import java.util.logging.Logger
class TransactionAdapter : RecyclerView.Adapter<TransactionViewHolder>() {
private var logger = Logger.getLogger(this::class.java.name)
var data = listOf<Transaction>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
logger.info("adapter : onCreateViewHolder()")
return TransactionViewHolder.from(parent)
}
override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) {
val item = this.data[position]
holder.bind(item)
}
override fun getItemCount(): Int {
logger.info("adapter : getItemCount()")
return this.data.size
}
}
class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val icon: ImageView = itemView.findViewById(R.id.transaction_icon)
private val note: TextView = itemView.findViewById(R.id.transaction_note)
private val value: TextView = itemView.findViewById(R.id.transaction_value)
fun bind(item: Transaction) {
icon.setImageResource(R.drawable.ic_dollar_sign)
note.text = item.note
value.text = item.value.toString()
}
companion object {
fun from(parent: ViewGroup): TransactionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.item_transaction, parent, false)
return TransactionViewHolder(view)
}
}
}
TransactionAdapter.kt
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.manage.manageit.home.HomeActivity">
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/myNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"/>
</LinearLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/black"
android:paddingVertical="6dp"
app:itemIconSize="28dp"
app:itemIconTint="#color/nav_bar_item"
app:itemRippleColor="#color/ripple_color"
app:itemTextColor="#color/nav_bar_item"
app:labelVisibilityMode="unlabeled"
app:menu="#menu/bottom_navigation_menu" />
</RelativeLayout>
</layout>
activity_home.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black"
android:fillViewport="true"
tools:context="com.example.manage.manageit.home.HomeFragment">
<TextView
android:id="#+id/homeActivityTitle"
style="#style/activityTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/medium_margin"
android:layout_marginTop="#dimen/medium_margin"
android:layout_marginEnd="#dimen/medium_margin"
android:text="#string/home_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/transaction_list"
android:layout_width="match_parent"
android:layout_height="400dp"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="#+id/floatingActionButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/homeActivityTitle" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/medium_margin"
android:layout_marginBottom="#dimen/very_big_margin"
android:backgroundTint="#color/secondaryColor"
android:clickable="true"
android:contentDescription="#string/add_button_description"
android:src="#drawable/ic_plus"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:maxImageSize="32dp"
tools:ignore="RedundantDescriptionCheck" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
fragment_home.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="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
app:cardBackgroundColor="#android:color/transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#drawable/transaction">
<ImageView
android:id="#+id/transaction_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:contentDescription="Icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_dollar_sign"
app:tint="#android:color/white" />
<TextView
android:id="#+id/transaction_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:text="Transaction note"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/transaction_icon"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/transaction_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:text="55€"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
item_transaction.xml : Basically a card view with an image and two text views (the transaction "note" and value).
In onCreateView, you have created your view layout twice. The first one you did using DataBindingUtil and that's the one whose RecyclerView you set up. But then you let that whole layout go back to the garbage collector because you create a brand new layout using layoutInflater and return that layout on the last line of onCreateView.
Technically, you should not be setting up views in onCreateView anyway. It should be done in onViewCreated(), although I don't think it makes much difference. However, since Fragment provides a constructor that can automatically inflate a provided layout ID, I think it's cleaner anyway to eliminate onCreateView() entirely, like this:
class HomeFragment : Fragment(R.layout.fragment_home) {
private var logger = Logger.getLogger(HomeFragment::class.java.name)
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
val binding: FragmentHomeBinding = DataBindingUtil.bind(view)
val recyclerView: RecyclerView = binding.transactionList
val transactionAdapter = TransactionAdapter()
recyclerView.adapter = transactionAdapter
recyclerView.layoutManager = LinearLayoutManager(context)
transactionAdapter.data = listOf(
Transaction(10, "Transaction note", "Groceries"),
Transaction(20, "Transaction note", "Groceries"),
Transaction(30, "Transaction note", "Groceries")
)
}
}
I am trying to run this example of face detection with the firebase MLkit on Android. Instead of the built in CameraView I use the library CameraView.
Running the code the logs says: Faces: [], so no faces were found from the camera.
You can checkout the project here on github.
the code:
import android.os.Bundle
import android.util.Log
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import android.widget.TextView
import androidx.annotation.WorkerThread
import com.otaliastudios.cameraview.CameraView
import com.otaliastudios.cameraview.Frame
import com.otaliastudios.cameraview.FrameProcessor
import com.otaliastudios.cameraview.Size
import androidx.core.view.ViewCompat.getRotation
import com.google.android.gms.tasks.OnFailureListener
import com.google.firebase.FirebaseApp
import com.google.firebase.ml.vision.FirebaseVision
import com.google.firebase.ml.vision.common.FirebaseVisionImage
import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata
import com.google.firebase.ml.vision.face.FirebaseVisionFace
import com.google.firebase.ml.vision.face.FirebaseVisionFaceContour
import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions
import com.google.firebase.ml.vision.face.FirebaseVisionFaceLandmark
class MainActivity : AppCompatActivity() {
private lateinit var textMessage: TextView
private lateinit var camera:CameraView
private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
textMessage.setText(R.string.title_home)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
textMessage.setText(R.string.title_dashboard)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
textMessage.setText(R.string.title_notifications)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
textMessage = findViewById(R.id.message)
navView.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)
camera = findViewById<CameraView>(R.id.camera)
camera.setLifecycleOwner(this)
val realTimeOpts1 = FirebaseVisionFaceDetectorOptions.Builder()
.setContourMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
.build()
val realTimeOpts = FirebaseVisionFaceDetectorOptions.Builder()
.setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
.setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
.build()
FirebaseApp.initializeApp(this);
camera.addFrameProcessor { frame ->
val data = frame.data
val rotation = frame.rotation
val time = frame.time
val size = frame.size
val format = frame.format
val metadata = FirebaseVisionImageMetadata.Builder()
.setWidth(480) // 480x360 is typically sufficient for
.setHeight(360) // image recognition
.setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
.setRotation(3)
.build()
val image = FirebaseVisionImage.fromByteArray(data, metadata)
val detector = FirebaseVision.getInstance()
.getVisionFaceDetector(realTimeOpts)
val result = detector.detectInImage(image)
.addOnSuccessListener { faces ->
// Task completed successfully
// ...
Log.e("FACE", faces.toString())
for (face in faces) {
val bounds = face.boundingBox
val rotY = face.headEulerAngleY // Head is rotated to the right rotY degrees
val rotZ = face.headEulerAngleZ // Head is tilted sideways rotZ degrees
// If landmark detection was enabled (mouth, ears, eyes, cheeks, and
// nose available):
val leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR)
leftEar?.let {
val leftEarPos = leftEar.position
}
// If contour detection was enabled:
val leftEyeContour = face.getContour(FirebaseVisionFaceContour.LEFT_EYE).points
val upperLipBottomContour = face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).points
// If classification was enabled:
if (face.smilingProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
val smileProb = face.smilingProbability
}
if (face.rightEyeOpenProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
val rightEyeOpenProb = face.rightEyeOpenProbability
}
// If face tracking was enabled:
if (face.trackingId != FirebaseVisionFace.INVALID_ID) {
val id = face.trackingId
}
}
}.addOnFailureListener(
object : OnFailureListener {
override fun onFailure(e: Exception) {
// Task failed with an exception
// ...
Log.e("M", e.toString())
}
})
}}
}
in activity_main.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:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:text="#string/title_home"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center"
android:layout_width="280dp"
card_view:cardCornerRadius="280dp"
android:layout_height="280dp">
<com.otaliastudios.cameraview.CameraView
android:id="#+id/camera"
app:cameraFacing="front"
android:keepScreenOn="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tried the same also with Fotoapparat, same result: No faces found. What am I missing here?
Try getting the height and width from the frame programmatically instead of hard coding them.
I think the problem is frame.rotation value. Maybe you can use FirebaseVisionImageMetadata.ROTATION_90 for FirebaseVisionImageMetadata
I am trying to implement a horizontal scroll views that adds custom written cardviews based on information in a room database. I also add a text view to space out the card views as configuring the padding in XML and programmatically both did not work. The problem is that whenever I create a new "artist" in the database, which should then add a card to the scroll view, there is a chance that the text view for spacing gets added to the incorrect side of the card view. Also there is a chance that card views get repeated and in this case the text view for spacing doesn't get added at all.
If you need the github repository to see the effects yourself, you can clone it here https://github.com/jeremyohandley/PosTattoo
I've tried changing adding log statements in various places and checking the database to see if there is any repeated data or missing data. I'm not sure what the source of the error is.
Artist Page Fragment.kt
package com.jeremy.ohandley.postattoo
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AlertDialog
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.jeremy.ohandley.postattoo.db.Artist
import com.jeremy.ohandley.postattoo.db.ArtistDao
import com.jeremy.ohandley.postattoo.db.Customer
import com.jeremy.ohandley.postattoo.db.database
import org.w3c.dom.Text
class ArtistPageFragment : Fragment() {
private var db: database? = null
private var artistDao: ArtistDao? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
db = database.getInstance(activity!!.applicationContext)
artistDao = db!!.artistDAO()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_artist_page, container,
false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val addArtist = view!!.findViewById<ImageButton>
(R.id.add_artist_button)
addArtist.setOnClickListener { launchNewArtistDialog() }
loadArtists()
}
private fun launchNewArtistDialog() {
val inflater = LayoutInflater.from(context)
val builder = AlertDialog.Builder(context!!)
val builderView =
inflater.inflate(R.layout.fragment_new_artist_dialog, null)
initAddArtistDialogButtons(builder, builderView)
builder.setView(builderView)
builder.show()
}
private fun initAddArtistDialogButtons(builder: AlertDialog.Builder,
builderView: View) {
builder.setNegativeButton("Cancel") { _, _ ->
Toast.makeText(context, "Artist not created",
Toast.LENGTH_SHORT).show()
}
builder.setPositiveButton("Create Artist") { _, _ ->
val firstNameInput = builderView.findViewById<EditText>
(R.id.first_name_input)
val lastNameInput = builderView.findViewById<EditText>
(R.id.last_name_input)
val emailInput = builderView.findViewById<EditText>
(R.id.email_input)
val phoneNumberInput = builderView.findViewById<EditText>
(R.id.phone_number_input)
val firstName = firstNameInput.text.toString()
val lastName = lastNameInput.text.toString()
val email = emailInput.text.toString()
val phoneNumber = phoneNumberInput.text.toString()
val newArtist = Artist(null, firstName, lastName, email,
phoneNumber)
artistDao!!.insertArtist(newArtist)
loadArtists()
}
}
private fun loadArtists() {
val artists = artistDao!!.getArtists()
clearArtists()
artists.forEach {
val card = ArtistCard(context!!)
val name = "${it.firstName} ${it.lastName}"
card.setName(name)
card.setNumber(it.phoneNumber)
card.setEmail(it.email)
card.setPadding(4, 4, 4, 4)
addToLayout(card)
}
}
private fun clearArtists() {
val cardLL = view!!.findViewById<LinearLayout>(R.id.cardLL)
for (i in 1 until cardLL.childCount) {
cardLL.removeView(cardLL.getChildAt(i))
}
}
private fun addToLayout(card: ArtistCard) {
val spacerView = TextView(context)
val cardLL = view!!.findViewById<LinearLayout>(R.id.cardLL)
cardLL.addView(card)
cardLL.addView(spacerView)
spacerView.layoutParams =
LinearLayout.LayoutParams(convertDpToPx(context!!, 8f).toInt(),
LinearLayout.LayoutParams.MATCH_PARENT)
}
private fun convertDpToPx(context: Context, dp: Float): Float {
return dp * context.resources.displayMetrics.density
}
}
Artist Page Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ArtistPageFragment"
android:orientation="horizontal"
android:gravity="center"
android:id="#+id/artists_linear_layout"
>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scroll_view"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:id="#+id/cardLL"
android:layoutDirection="rtl"
android:gravity="center"
>
<android.support.v7.widget.CardView
android:layout_width="200dp"
android:layout_height="300dp"
android:layout_margin="8dp"
>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="#+id/add_artist_button"
android:src="#drawable/ic_add_24dp"
/>
</android.support.v7.widget.CardView>-
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
ArtistCard.kt (custom card)
package com.jeremy.ohandley.postattoo
import android.content.Context
import android.graphics.Paint
import android.support.v7.widget.CardView
import android.util.AttributeSet
import android.widget.TextView
class ArtistCard(context: Context) : CardView(context) {
var attributes:AttributeSet? = null
constructor(context: Context, attrs: AttributeSet) : this(context) {
attributes = attrs
}
init{
inflate(context, R.layout.artist_card, this)
val name = findViewById<TextView>(R.id.artist_name)
val number = findViewById<TextView>(R.id.artist_phone_number)
val email = findViewById<TextView>(R.id.artist_email)
val attributes = context.obtainStyledAttributes(attributes,
R.styleable.ArtistCard)
name.text = attributes.getString(R.styleable.ArtistCard_name)
number.text = attributes.getString(R.styleable.ArtistCard_number)
email.text = attributes.getString(R.styleable.ArtistCard_email)
attributes.recycle()
}
fun setName (mName:String){
val name = findViewById<TextView>(R.id.artist_name)
name.text = mName
}
fun setNumber (mNumber:String){
val number = findViewById<TextView>(R.id.artist_phone_number)
number.text = mNumber
}
fun setEmail (mEmail:String){
val email = findViewById<TextView>(R.id.artist_email)
email.text = mEmail
}
}
Artist Card Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="300dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="sample name"
android:id="#+id/artist_name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Phone Number"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="(1)-555-555-5555"
android:id="#+id/artist_phone_number"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Email"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="sample#gmail.com"
android:id="#+id/artist_email"
/>
</LinearLayout>
</android.support.v7.widget.CardView>