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
}
}
Related
I have an onScrollListener animation in my Kotlin project and I will like to use that code in my java as well, I have tired to convert the Kotlin code to Java but i am see some error and my app crashes when it detects the onScrollListener the animation hides a TextView
This is the Kotlin Code
class MainActivity : AppCompatActivity()
{
private lateinit var category_recycler: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var btnExtended = true
//animated scroll
var animator : ValueAnimator? = null
//the note onscroll listener
note_recycler.addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (animator==null){
animator=createanimator()
}
if (dy>0 && btnExtended)
{
animator!!.start()
btnExtended = !btnExtended
}else if (dy<0 && !btnExtended){
animator!!.reverse()
btnExtended = !btnExtended
}
}
})
}
private fun createanimator(): ValueAnimator{
var textview = findViewById<TextView>(R.id.textview) //textview i want to hide
val initSize=textview.measuredWidth
val animator=ValueAnimator.ofInt(initSize,0)
animator.duration=250
animator.addUpdateListener { animation ->
val value = animation.animatedValue as Int
val layoutParams = textview.layoutParams //line 50
layoutParams.width = value
textview.requestLayout()
}
return animator
}
}
//So far this is the code i has converted to java
public class MainActivity extends AppCompatActivity implements NoteListener {
private RecyclerView note_recycler;
private ValueAnimator animator = null;
private Boolean btnExtended;
private TextView textview;
private LinearLayout.LayoutParams layoutParams;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnExtended = true;
note_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(animator==null){
animator=createanimator();
}
if (dy>0 && btnExtended)
{
if(animator!=null){
animator.start();
}
btnExtended = !btnExtended;
}
else if (dy<0 && !btnExtended){
if(animator!=null){
animator.reverse();
}
btnExtended = !btnExtended;
}
}
});
}
private ValueAnimator createanimator() {
textview = findViewById(R.id.textview);
int initSize = textview.getMeasuredWidth();
animator=ValueAnimator.ofInt(initSize,0);
animator.setDuration(250);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(#NonNull ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
//there is a missing line here named line 50 from the kotlin code, Please can you help me convert it and also cross check the other code for the java convertion
layoutParams.width =value;
textview.requestLayout();
}
});
return animator;
}
}
//Please I am new to kotlin and i am not sure if this java convertion is right please can you check through it and convert the line 50 i was unable to convert
After a few looking around i wont say i found the answer my self but it sure works :)
ViewGroup.LayoutParams layoutParams = textview.getLayoutParams();
that was the missing line 50 that works, shockingly there are no error with the rest of the conversion.
+4hours haha
Either way this is a OnScrollAnimation like in gmail with their compose button, i set the button to wrap content and when the animation hides the text in the button, it only shows the icon.
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)
}
}
})
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)
}
I'm new to Kotlin and I'm trying to implement a custom pin entry text edit field as done in this tutorial.
I re-wrote the PinEntryEditText in Kotlin, and it throws no errors. I also added to my XML layout file, and used it in my MainActivity page. It runs and throws no errors. However, it does not display as it is supposed to. I started trying to debug it and setup 3 breakpoints on each of the class constructors. The debugger does not stop at any of them, which makes me think it never even goes from there.
Now, here is my class :
class PinEntryEditText : AppCompatEditText {
private var mSpace = 24f //24 dp by default, space between the lines
private var mCharSize: Float = 0.toFloat()
private var mNumChars = 4f
private var mLineSpacing = 8f //8dp by default, height of the text from our lines
private var mMaxLength = 4f
val XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android"
var mClickListener: View.OnClickListener? = null
private var mLineStroke = 1f //1dp by default
private var mLineStrokeSelected = 2f //2dp by default
private var mLinesPaint: Paint? = null
var mStates = arrayOf(intArrayOf(android.R.attr.state_selected), // selected
intArrayOf(android.R.attr.state_focused), // focused
intArrayOf(-android.R.attr.state_focused))// unfocused
var mColors = intArrayOf(Color.GREEN, Color.BLACK, Color.GRAY)
var mColorStates = ColorStateList(mStates, mColors)
constructor(context: Context) : super(context) {
this.setWillNotDraw(false)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
this.setWillNotDraw(false)
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
this.setWillNotDraw(false)
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet) {
val multi = context.resources.displayMetrics.density
mLineStroke = (multi * mLineStroke)
mLineStrokeSelected = (multi * mLineStrokeSelected)
mLinesPaint = Paint(paint)
mLinesPaint?.strokeWidth = mLineStroke.toFloat()
if (!isInEditMode) {
val outValue = TypedValue()
context.theme.resolveAttribute(colorControlActivated,
outValue, true)
val colorActivated = outValue.data
mColors[0] = colorActivated
context.theme.resolveAttribute(colorPrimaryDark,
outValue, true)
val colorDark = outValue.data
mColors[1] = colorDark
context.theme.resolveAttribute(colorControlHighlight,
outValue, true)
val colorHighlight = outValue.data
mColors[2] = colorHighlight
}
setBackgroundResource(0)
mSpace = (multi * mSpace) //convert to pixels for our density
mLineSpacing = (multi * mLineSpacing) //convert to pixels for our density
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 4).toFloat()
mNumChars = mMaxLength
//Disable copy paste
super.setCustomSelectionActionModeCallback(object : ActionMode.Callback {
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode) {}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return false
}
})
// When tapped, move cursor to end of text.
super.setOnClickListener { v ->
setSelection(text!!.length)
if (mClickListener != null) {
mClickListener?.onClick(v)
}
}
}
override fun setOnClickListener(l: View.OnClickListener?) {
mClickListener = l
}
override fun setCustomSelectionActionModeCallback(actionModeCallback: ActionMode.Callback) {
throw RuntimeException("setCustomSelectionActionModeCallback() not supported.")
}
override fun onDraw(canvas: Canvas) {
//super.onDraw(canvas)
val availableWidth = width - paddingRight - paddingLeft
if (mSpace < 0) {
mCharSize = availableWidth / (mNumChars * 2 - 1)
} else {
mCharSize = (availableWidth - mSpace * (mNumChars - 1)) / mNumChars
}
var startX = paddingLeft.toFloat()
val bottom = height - paddingBottom
//Text width
val text = text
val textLength = text!!.length
val textWidths = FloatArray(textLength)
paint.getTextWidths(getText(), 0, textLength, textWidths)
for (i in 0..mNumChars.toInt()) {
updateColorForLines(i == textLength)
canvas.drawLine(startX.toFloat(), bottom.toFloat(), startX.toFloat() + mCharSize, bottom.toFloat(), paint)
if (text.length > i) {
val middle = startX + mCharSize / 2
canvas.drawText(text, i,i + 1, middle - textWidths[0] / 2, (bottom - mLineSpacing).toFloat(), paint)
}
if (mSpace < 0) {
startX += mCharSize * 2
} else {
startX += mCharSize + mSpace
}
}
}
private fun getColorForState(vararg states: Int): Int {
return mColorStates.getColorForState(states, Color.GRAY)
}
private fun updateColorForLines(next: Boolean) {
if (isFocused) {
mLinesPaint?.strokeWidth = mLineStrokeSelected.toFloat()
mLinesPaint?.color = getColorForState(android.R.attr.state_focused)
if (next) {
mLinesPaint?.color = getColorForState(android.R.attr.state_selected)
}
} else {
mLinesPaint?.strokeWidth = mLineStroke.toFloat()
mLinesPaint?.color = getColorForState(-android.R.attr.state_focused)
}
}
}
And here's my XML for the component :
<com.myapp.app.myapp.PinEntryEditText
android:id="#+id/pin_entry_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:digits="1234567890"
android:inputType="number"
android:maxLength="4"
android:textIsSelectable="false"
android:textSize="20sp" />
Do I have to programmatically assign the class? Unfortunately, I don't really know how to approach this since the debugger won't even step into the class. Any form of input on what could be wrong with this would be greatly appreciated.
# Giulio Colleluori Double check your package name, Your access to the class through package name does not look fine.
According to View.setWillNotDraw, you should remove setWillNotDraw(true) from your constructors:
If this view doesn't do any drawing on its own, set this flag to allow
further optimizations. By default, this flag is not set on View, but
could be set on some View subclasses such as ViewGroup. Typically, if
you override onDraw(android.graphics.Canvas) you should clear this
flag.
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.