PopupWindow Overlap With Virtual Keyboard - java

I have some problem with showing PopupWindow which is the popup overlaps with the keyboard. here my result
Here my popup code:
class NoteHintList(val view: View, val listener: MenuDetailFragment.OnItemListClickListener?){
var noteHintList: List<String> = ArrayList<String>()
val popupWindow = PopupWindow(view.context)
fun setNoteList(noteHint: List<String>){
this.noteHintList = noteHint
}
// ============ TODO: Pop up note hint ================================================================
fun popupWindow(): PopupWindow {
val popUpContents = arrayOfNulls<String>(noteHintList.size)
ArrayList(noteHintList).toArray(popUpContents)
val listView = ListView(view.context)
listView.adapter = noteHintAdapter(popUpContents)
// set the item click listener
listView.setOnItemClickListener { parent, view, position, id ->
listener!!.onItemListClickListener(listView.getItemAtPosition(position).toString())
}
// val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
// params.setMargins(16,0,16,0)
popupWindow.setFocusable(false)
popupWindow.contentView = view
// popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT - 32)
// popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT)
popupWindow.setContentView(listView)
return popupWindow
}
// ============ TODO: Popup list adapter for pop up ===================================================
private fun noteHintAdapter(array: Array<String?>): ArrayAdapter<Any> {
return object : ArrayAdapter<Any>(view.context, android.R.layout.simple_list_item_1, array) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
// setting the ID and text for every items in the list
val text = getItem(position)!!.toString()
// visual settings for the list item
val listItem = TextView(context)
listItem.setText(text)
listItem.tag = position
listItem.textSize = 22f
listItem.setPadding(10, 10, 10, 10)
listItem.setTextColor(Color.WHITE)
return listItem
}
}
}
}
The popup will show when text changed. here my code
txtHint.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
val text = txtHint.text.toString()
if (Pattern.compile("^\\s+$").matcher(text).find() || text == "") {
popupNoteHintList!!.setNoteList(noteHintAllList)
} else if (text.split(",").size > 0) {
val textSplit = text.split(",")
val myData = noteHintAllList.filter { s ->
s.toLowerCase().contains(textSplit[textSplit.size - 1].toLowerCase().replace(Regex("^ | \$"), "")) &&
s.toLowerCase() != textSplit[textSplit.size - 1].toLowerCase().replace(Regex("(^ +)|( +$)"), "")
}
popupNoteHintList!!.setNoteList(myData)
if (myData.size > 0) {
popupNoteHintList!!.popupWindow().showAsDropDown(txtHint, 0, 0)
}
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
popupNoteHintList!!.popupWindow().dismiss()
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
popupNoteHintList!!.popupWindow().dismiss()
}
})
How to make the popup not overlap with keyboard, maybe move to above EditText if overlap, but still under EditText if not overlap
So result seem like this:

You can hide the soft keyboard in the afterTextChanged method, so that the keyboard is always hidden when your popup appears.
Within the afterTextChanged method, add the following to hide the keyboard:
val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0) //Hide soft keyboard.

Related

How Add Animation On view when view width is changing [Android]

I'm using collapsing toolbar and I am changing view width OnOffsetChangedListener. what I want is that put little animation when searchView width will change , now its changing very straight and its little weird for user. I tried to set android:animateLayoutChanges="true" on parent layout but it not worked
var mListener =
OnOffsetChangedListener { appBarLayout, verticalOffset ->
if (binding.collapsingToolbar.getHeight() + verticalOffset < 2 * ViewCompat.getMinimumHeight(
binding.collapsingToolbar
)
) {
val view: View = binding.searchView
val layoutParams: CollapsingToolbarLayout.LayoutParams = view.layoutParams as CollapsingToolbarLayout.LayoutParams
layoutParams.width = binding.appBar.width / 2
view.layoutParams = layoutParams
} else {
val view: View = binding.searchView
val layoutParams: CollapsingToolbarLayout.LayoutParams = view.layoutParams as CollapsingToolbarLayout.LayoutParams
layoutParams.width = CollapsingToolbarLayout.LayoutParams.MATCH_PARENT
view.layoutParams = layoutParams
}
}
binding.appBar.addOnOffsetChangedListener(mListener)
How you tried built-in interpolators?
Please check below link for some insights:
https://jebware.com/interp/android-animation-interpolators.html
a very basic example is as follows:
val fastOutSlowInInterpolator = FastOutSlowInInterpolator()
val interpolatedValue = fastOutSlowInInterpolator.getInterpolation(0.5f)
In above example 0.5f would vary from 0 to 1 and it will return animated value which you should use in view animation.
You can simply define an animation class.
class ResizeWidthAnimation(private val mView: View, private val mWidth: Int) : Animation() {
private val mStartWidth: Int = mView.width
override fun applyTransformation(interpolatedTime: Float, t: Transformation{
mView.layoutParams.width = mStartWidth + ((mWidth - mStartWidth) * interpolatedTime).toInt()
mView.requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}

How to add animation to the viewPager in Kotlin?

**this is my Activity pager **
private const val TAG = "CarpagerActivity"
var carList: ArrayList<Car>? = null
var mSerializer : JsonSerializer? = null
class CarPagerActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_car_pager)
mSerializer = JsonSerializer("CarTrinkApp.json",
applicationContext)
try {
carList = mSerializer!!.load()
} catch (e: Exception) {
carList = ArrayList()
Log.e("Error loading cars: ", "", e)
}
// create list of fragments, one fragment for each car
var carFragmentList = java.util.ArrayList<Fragment>()
for (car in carList!!) {
carFragmentList.add(ShowCarFragment.newInstance(car))
}
val pageAdapter = CarPagerAdapter(supportFragmentManager, carFragmentList)
findViewById<ViewPager>(R.id.pager_cars).adapter = pageAdapter
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val myPost = data?.getIntExtra("adapterPosition", 123)
println(myPost)
println("adadadada")
}
class CarPagerAdapter(fm: FragmentManager, private val carFragmentList: ArrayList<Fragment>) : FragmentPagerAdapter(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount() = carFragmentList.size
override fun getItem(position: Int) = carFragmentList[position]
}
}
[![I want add animation to this transition ][1]][1]
**and also this page in Fragment Layout **
[1]: https://i.stack.imgur.com/qFB3b.png
Fading animation, see reference here
viewPager.setPageTransformer(false) { page, position ->
// do transformation here
page.alpha = 0f
page.visibility = View.VISIBLE
// Start Animation for a short period of time
page.animate()
.alpha(1f).duration =
page.resources.getInteger(android.R.integer.config_longAnimTime)
.toLong()
}
or
viewPager.setPageTransformer(false) { page, position ->
// do transformation here
page.rotationY = position * -70
}

Can You smooth scroll to new position within RecyclerView (before it is shown) and then show insert animation?

Hello fellow programmers!
I am currently facing problem with RecyclerView's add animation with smooth scroll simulataneously.
Problem
I have implemented RecyclerView with ListAdapter and DiffUtil for cool animations (insert, remove). The problem is that I can't get it to smooth scroll to added position without cancelling insert animation.
User is supposed to type in 'exercise name' and then push 'add' button to add it to current 'exercise list'. Then this exercise should be added to RecyclerView list. Everything works fine here, but I can't achieve smooth scroll to new item position without cancelling (or accelerating) insert animation.
viewModel.exerciseListLiveData.observe(viewLifecycleOwner, {
adapter.submitList(it) {
// Cancels insert animation (or accelerates it rapidly) - bad UX...
layoutManager.smoothScrollToPosition(recyclerView, RecyclerView.State(), 0)
}
})
On the other hand, substituting smoothScrollToPosition with scrollToPosition works fine but when there's more items (recycler's view is filled from top to bottom) it blinks on scroll. I know it's rather jump than smooth scroll but then, why is it working with add animation so it doesn't cancel or accelerate?
viewModel.exerciseListLiveData.observe(viewLifecycleOwner, {
adapter.submitList(it) {
// Works fine (recyclerView is scrolling to added position)
// but when there's no room to display more items - it scrolls to it with blink...
// which I simply can't stand!
layoutManager.smoothScrollToPosition(recyclerView, RecyclerView.State(), 0)
}
})
Possible solution?
I'm supposed to first smoothScrollToPosition(0) to top-most position, then call submitList(it) and finally call scrollToPosition(0) but I can't achieve that (it is doing so fast, simultaneously, that it's layering each other). Maybe I should use coroutines? Don't know...
How am I supposed to add proper delay? Maybe I am doing something wrong because i read that some programmers had this autoscroll when added item.
Code sample
Adapter
class ExercisesListAdapter :
ListAdapter<Exercise, ExercisesListAdapter.ExerciseItemViewHolder>(ExercisesListDiffUtil()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExerciseItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ItemRecyclerviewExerciseBinding =
DataBindingUtil.inflate(inflater, R.layout.item_recyclerview_exercise, parent, false)
return ExerciseItemViewHolder(binding)
}
override fun onBindViewHolder(holder: ExerciseItemViewHolder, position: Int) {
val currentExercise = getItem(position)
holder.bind(currentExercise)
}
override fun getItemId(position: Int): Long {
return getItem(position).id
}
// Item ViewHolder class
class ExerciseItemViewHolder(private val binding: ItemRecyclerviewExerciseBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(itemExercise: Exercise) {
binding.textCardExerciseName.text = itemExercise.name
}
}
}
DiffUtil
class ExercisesListDiffUtil: DiffUtil.ItemCallback<Exercise>() {
override fun areItemsTheSame(oldItem: Exercise, newItem: Exercise): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Exercise, newItem: Exercise): Boolean {
return oldItem == newItem
}
}
ViewModel
class CreateWorkoutViewModel(private val repository: WorkoutRepository) : ViewModel() {
private var exerciseId = 0L
private var exerciseList = mutableListOf<Exercise>()
val exerciseName = MutableLiveData<String?>()
private val _exerciseListLiveData = MutableLiveData<List<Exercise>>()
val exerciseListLiveData: LiveData<List<Exercise>>
get() = _exerciseListLiveData
fun onAddExercise() {
if (canValidateExerciseName())
addExerciseToList()
}
fun onAddWorkout() {
exerciseList.removeAt(2)
_exerciseListLiveData.value = exerciseList.toList()
}
private fun canValidateExerciseName(): Boolean {
return !exerciseName.value.isNullOrBlank()
}
private fun addExerciseToList() {
exerciseList.add(0, Exercise(exerciseId++, exerciseName.value!!))
_exerciseListLiveData.value = exerciseList.toList()
}
}
Fragment
class CreateWorkoutFragment : Fragment() {
lateinit var binding: FragmentCreateWorkoutBinding
lateinit var viewModel: CreateWorkoutViewModel
lateinit var recyclerView: RecyclerView
lateinit var adapter: ExercisesListAdapter
lateinit var layoutManager: LinearLayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =
DataBindingUtil.inflate(inflater, R.layout.fragment_create_workout, container, false)
setViewModel()
setRecyclerView()
// Observers:
// Update RecyclerView list on change
viewModel.exerciseListLiveData.observe(viewLifecycleOwner, {
adapter.submitList(it) {
layoutManager.smoothScrollToPosition(recyclerView, RecyclerView.State(), 0)
}
})
return binding.root
}
private fun setRecyclerView() {
recyclerView = binding.recyclerviewExercises
setLayoutManager()
setAdapter()
}
private fun setLayoutManager() {
layoutManager = LinearLayoutManager(context)
recyclerView.layoutManager = layoutManager
}
private fun setAdapter() {
adapter = ExercisesListAdapter()
adapter.setHasStableIds(true)
recyclerView.adapter = adapter
}
private fun setViewModel() {
val dao = WorkoutDatabase.getInstance(requireContext()).workoutDAO
val repository = WorkoutRepository(dao)
val factory = CreateWorkoutViewModelFactory(repository)
viewModel = ViewModelProvider(this, factory).get(CreateWorkoutViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = this
}
}
EDIT
I got it working but it's not 'elegant' way and insert animation is canceled through process :/
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if(layoutManager.findFirstCompletelyVisibleItemPosition() == 0)
layoutManager.scrollToPosition(0)
else {
recyclerView.clearAnimation()
layoutManager.smoothScrollToPosition(recyclerView, RecyclerView.State(), 0)
}
}
})

How would I get the context of a RecyclerView List Item?

I am new to Firebase and RecyclerViews and I am trying to get a list of ingredients from my Firebase Database and I want to create checkboxes for every item I retrieve from the database. I got as far as creating a RecyclerView and adapter for my Firebase Query but binding lists from the query doesn't seem to be covered anywhere I could find.
Please find below the code for putting the data into the recycler view as well as the layout files being used for the recycler view.
Data being retrieved
that I want to display the original field of my object as checkboxes
ShoppingListFragment.kt
class ShoppingListFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Enable Firestore logging
FirebaseFirestore.setLoggingEnabled(true)
//Firestore
firestore = Firebase.firestore
// Get recipes that the user has liked up to ${LIMIT}
query = firestore.collection("recipes").whereArrayContains(
"plannedBy",
Firebase.auth.uid.toString()
)
.orderBy("name", Query.Direction.ASCENDING)
.limit(LIMIT.toLong())
// Init the adapter to hold the recipe objects.
adapter = object : ShoppingListAdapter(query) {
override fun onDataChanged() {
if (itemCount == 0) {
shoppingListRecycler.visibility = View.GONE
viewEmpty.visibility = View.VISIBLE
} else {
shoppingListRecycler.visibility = View.VISIBLE
viewEmpty.visibility = View.GONE
}
}
override fun onError(e: FirebaseFirestoreException) {
// Show a snackbar on errors
view?.let {
Snackbar.make(
it,
"Error: check logs for info.", Snackbar.LENGTH_LONG
).show()
}
}
}
...
}
...
}
ShoppingListAdapter.kt
open class ShoppingListAdapter(query: Query) :
FirestoreAdapter<ShoppingListViewHolder>(query) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShoppingListViewHolder {
return ShoppingListViewHolder(
ItemShoppingListBinding.inflate(
LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ShoppingListViewHolder, position: Int) {
holder.bind(getSnapshot(position))
}
}
ShoppingListViewHolder.kt
class ShoppingListViewHolder(val binding: ItemShoppingListBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(snapshot: DocumentSnapshot) {
val recipe = snapshot.toObject<Recipe>() ?: return
binding.listTitle.text = recipe.name
if(recipe.ingredients.size > 1) {
binding.shoppingListItem.text = recipe.ingredients[0].original
for(i in 1 until recipe.ingredients.size) {
val checkBox = CheckBox() //Not sure what to put here as the context for my recyclerview List Item
checkBox.text = recipe.ingredients[i].original
checkBox.layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
binding.shoppingListContainer.addView(checkBox)
}
} else {
binding.shoppingListItem.text = recipe.ingredients[0].original
}
}
}
I have tried passing a context parameter through ShoppingListViewHolder.kt and in turn adding that parameter to the adapter and so forth like so.
class ShoppingListViewHolder(val binding: ItemShoppingListBinding, val context: Context) : RecyclerView.ViewHolder(binding.root) {
fun bind(snapshot: DocumentSnapshot) {
val recipe = snapshot.toObject<Recipe>() ?: return
binding.listTitle.text = recipe.name
if(recipe.ingredients.size > 1) {
binding.shoppingListItem.text = recipe.ingredients[0].original
for(i in 1 until recipe.ingredients.size) {
val checkBox = CheckBox(context)
...
}
ShoppingListFragment.kt
// Init the adapter to hold the recipe objects.
adapter = object : ShoppingListAdapter(query, requireContext()) {
override fun onDataChanged() {
if (itemCount == 0) {
shoppingListRecycler.visibility = View.GONE
viewEmpty.visibility = View.VISIBLE
} else {
shoppingListRecycler.visibility = View.VISIBLE
viewEmpty.visibility = View.GONE
}
}
override fun onError(e: FirebaseFirestoreException) {
// Show a snackbar on errors
view?.let {
Snackbar.make(
it,
"Error: check logs for info.", Snackbar.LENGTH_LONG
).show()
}
}
}
But all that did was create a huge amount of white space between entries. After some debugging, it looks like passing the context from the fragment gave it the MainActivity context. I'm not too sure where to go from here and any help would be appreciated. Thank you!
you can use binding.root.context except passing context

How make function to progress bar, for different items in BaseAdapter

I used listview. I have 5 items. When I start program I see only 3. Handler what is for progress bar start working for 3/5 items. I go to see 4,5 item, so i lost from view 1,2 item. 4 and 5 item handler starts working.
//Adapter class
var progress = 0
var gain = myData
handler = Handler(Handler.Callback {
progress = progress + speed
if (progress >= 100) {
progress = 0
functionWhatChangeInFirebase(gain)
}
iData.progressBar?.progress = progress
handler?.sendEmptyMessageDelayed(0, 100)
true
})
handler.sendEmptyMessage(0)
Problem is when I go back to look at first and second item, handler start "new thread" and progress bar have more that one function, what changed data.
Edit
Added Adapter class
package com.example.adventurepwr
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Handler
import android.support.design.widget.FloatingActionButton
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import java.util.ArrayList
class AdapterItem(context: Context, private val itemList: ArrayList<Item>) : BaseAdapter() {
private val mInflater: LayoutInflater = LayoutInflater.from(context)
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val current = itemList[position]
val item: String = current.item!!
val level: Int = current.level!!
val price: Int = current.price!!
val gain: Int = current.gain!!
val speed: Int = current.speed!!
val count: Int = current.count!!
val view: View
val iData: ItemsData
if (convertView == null) {
view = mInflater.inflate(R.layout.content_item, parent, false)
iData = ItemsData(view)
view.tag = iData
} else {
view = convertView
iData = view.tag as ItemsData
}
iData.name?.text = item
iData.level?.text = level.toString()
iData.price?.text = price.toString()
iData.gain?.text = gain.toString()
iData.speed?.text = speed.toString()
iData.count?.text = count.toString()
var progress: Int = 0
var handler: Handler? = null
iData.lvlButton?.setOnClickListener {
canUpgrade(price, item)
}
handler = Handler(Handler.Callback {
progress = progress + speed
if (progress >= 100) {
progress = 0
addMoneyNormal(gain)
}
iData.progressBar?.progress = progress
handler?.sendEmptyMessageDelayed(0, 100)
true
})
handler.sendEmptyMessage(0)
return view
}
override fun getItem(index: Int): Any {
return itemList.get(index)
}
override fun getItemId(index: Int): Long {
return index.toLong()
}
override fun getCount(): Int {
return itemList.size
}
private class ItemsData(row: View?) {
val name: TextView? = row!!.findViewById(R.id.name_item) as TextView?
val level: TextView? = row!!.findViewById(R.id.lvl_Number) as TextView?
val price: TextView? = row!!.findViewById(R.id.price_number) as TextView?
val speed: TextView? = row!!.findViewById(R.id.speed) as TextView?
val gain: TextView? = row!!.findViewById(R.id.gain) as TextView?
val count: TextView? = row!!.findViewById(R.id.count_number) as TextView?
val lvlButton: FloatingActionButton? = row!!.findViewById(R.id.lvl_up_button) as FloatingActionButton?
val progressBar: ProgressBar? = row!!.findViewById(R.id.progressBar) as ProgressBar?
}
}
I go to the main
and do something like this, but i get stackOverflow 8mb
fun progress(){
for (oneRecord in itemList) {
val item: Item = oneRecord
item.count=item.count!! + item.speed!!
if (item.count!!>100){
addMoneyNormal(item.gain!!)
item.count=0
}
Thread.sleep(1000)
}
adapterItem.notifyDataSetChanged()
progress()
}
progress()
Maybe we can do something with this
Now progressBar.progress = count
val myRun = object : Runnable {
override fun run() {
for (oneRecord in mUploads) {
val item: Item = oneRecord
item.count = item.count!! + item.speed!!
if (item.count!! >= 100) {
item.count = 0
addMoneyNormal(item.gain!!)
}
}
adapterItem.notifyDataSetChanged()
this#MainActivity.mHandler.postDelayed(this, 100)
}
}
myRun.run()
So i use this in my main class
in adapter i used, that count is progression
For now I dont see any bugs
Im not sure, but you need to move handler from getView() and you should check for each item.
Can you try this:
fun testMethod(current) {
if(current.count < 4) {
return
}
handler = Handler(Handler.Callback {
progress = progress + speed
if (progress >= 100) {
progress = 0
addMoneyNormal(gain)
}
iData.progressBar?.progress = progress
handler?.sendEmptyMessageDelayed(0, 100)
true
})
handler.sendEmptyMessage(0)
}

Categories

Resources