I am using viewpager2 in my application. I enabled auto slide using rxjava2 (observable interval). I want to stop auto slide when user touch the viewpager and after touch finishes start auto slide. But I can not find proper way to detect touch finish event. I tried action_up, but it triggers only when fast touch like click. It's not detect when user touch 2 second and finish touch.
Observable.interval(SLIDER_DELAY, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (!homeAdapter.touchStatus()) {
if (viewPagerSlider.currentItem < homeAdapter.itemCount - 1) {
viewPagerSlider.setCurrentItem(
viewPagerSlider.currentItem + 1,
true
)
} else {
viewPagerSlider.setCurrentItem(0, true)
}
}
}
HomeAdapter
itemView.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> isTouched = true
MotionEvent.ACTION_UP -> isTouched = false
else -> {}
}
true
}
fun touchStatus() = this.isTouched
How can I detect when user finished touch event after some time?
Related
In my app I want to display a splash screen and after 4 seconds go to HomeActivity. However, I would like to pause the execution by touching imageview imageSplash and resume when releasing. How to do this?
GlobalScope.launch(context = Dispatchers.Main) {
imageSplash.setOnTouchListener { view, motionEvent ->
when(motionEvent.action){
MotionEvent.ACTION_DOWN ->{
true
}
MotionEvent.ACTION_UP->{
true
}
else ->{
false
}
}
}
delay(4000)
val intent = Intent(this#SplashActivity, HomeActivity::class.java)
startActivity(intent)
finish()
}
Kotlin coroutine is of a type kotlinx.coroutines.Job.
That class does not have methods to pause or resume a job.
Alternatively:
You need to write a logic with 2 coroutines.
One that starts HomeActivity after 4 seconds.
Second that listens ACTION_DOWN and ACTION_UP motion events. On ACTION_DOWN you cancel the first coroutine Job, and after ACTION_UP (after N seconds) you show HomeActivity from a second coroutine.
A bit late, but hopefully it will help.
As mentioned by #I.Step, coroutines do not offer functionality for pausing and resuming so, in this case, a simple flag like var isPaused: Boolean could work. You'll just need to check it while the coroutine is running.
Example:
private var isPaused = false
GlobalScope.launch(...) {
while (isActive)
{
if (!isPaused)
{
// do your work here
}
}
}
fun pause()
{
isPaused = true
}
fun resume()
{
isPaused = false
}
I have an activity with a set of buttons on it, it resembles a NumPad keyboard.
My purpose: to do some terminal for input data with help of little (hard) “usb NumPad keyboard” – so interface looks as NumPad – just a set of Buttons.
I want to handle all the keyboard events to do with them what I need to do (my own function for each button). Overrided functions onKeyUp and onKeyDown – and they do all that I need, except handling the Enter key. In these two functions it’s not an event at all as I see.
On Enter activity opens menu, so Enter is some special function – not for me, but for system.
All the topics here that I saw (how to handle “Enter”) are about soft keyboard or EditView. I don’t have on my activity any editable, I just want to catch Enter event, or maybe possible link Enter with some of the Buttons on activity.
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
Toast.makeText(this,keyCode.toString(),Toast.LENGTH_SHORT).show()
val res: Int = when (keyCode) {
KeyEvent.KEYCODE_NUMPAD_0 -> 0
KeyEvent.KEYCODE_0 -> 0
KeyEvent.KEYCODE_NUMPAD_1 -> 1
KeyEvent.KEYCODE_1 -> 1
KeyEvent.KEYCODE_DPAD_DOWN_LEFT -> 1
KeyEvent.KEYCODE_NUMPAD_DOT -> 10
KeyEvent.KEYCODE_NUM_LOCK -> 11
KeyEvent.KEYCODE_NUMPAD_ENTER -> 16 //never happens
else -> -1
}
if (res>=0) doAction(res)
return if (res == -1) super.onKeyUp(keyCode, event)
else true
}
Use dispatchKeyEvent to handle enter:
override fun dispatchKeyEvent(event:KeyEvent):Boolean {
if (event.getAction() === KeyEvent.ACTION_UP)
{
Toast.makeText(this,event.getKeyCode().toString(),Toast.LENGTH_SHORT).show()
return true
}
}
I have a function that shrinks the size of a view on ACTION_DOWN, and returns it to original size on ACTION_UP. This function is strictly for aesthetics. I have setOnClickListeners on the buttons so they can (in theory...) execute code when the button is pressed.
private fun scaleButton(theButton:View, grow:Boolean){
theButton.setOnTouchListener(View.OnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
v.isPressed = true
if (grow) {
v.animate().scaleX(1.04F).scaleY(1.04F).setDuration(50)
} else {
v.animate().scaleX(0.97F).scaleY(0.97F).setDuration(50)
}
} else if (event.action == MotionEvent.ACTION_UP) {
v.isPressed = false
v.animate().scaleX(1.0F).scaleY(1.0F).setDuration(100)
}
false
})
}
The problem is setOnClickListener is never called.
onCreate:
scaleButton(button1,false)
scaleButton(button2,true)
scaleButton(button3,false)
button1.setOnClickListener {
println("Button 1 Pressed")
}
button2.setOnClickListener {
println("Button 2 Pressed")
}
button3.setOnClickListener {
println("Button 3 Pressed")
}
How can I both scale the button via animation (on touch) AND trigger setOnClickListener
Your click listener will never fire because the touch listener is supposed to tell the system when the elemnt was clicked. Yours isn't. Either add a performClick at the appropriate time, or put everything into the touch listener and perform the click on action_up
How to override performClick in Kotlin to avoid warning?
next.setOnTouchListener(View.OnTouchListener { view, motionEvent ->
when (motionEvent.action){
MotionEvent.ACTION_DOWN -> {
val icon: Drawable = ContextCompat.getDrawable(activity.applicationContext, R.drawable.layer_bt_next)
icon.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY)
next.setImageDrawable(icon)
}
MotionEvent.ACTION_UP -> {
//view.performClick()
next.setImageResource(R.drawable.layer_bt_next)
}
}
return#OnTouchListener true
})
view.performClick does not work.
Try this way :
next.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> //Do Something
}
return v?.onTouchEvent(event) ?: true
}
})
Okay, I solved my own problem by overriding the OnTouch listener.
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
when (view) {
next -> {
Log.d("next", "yeyy")
when (motionEvent.action){
MotionEvent.ACTION_DOWN -> {
val icon: Drawable = ContextCompat.getDrawable(activity.applicationContext, R.drawable.layer_bt_next)
icon.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY)
next.setImageDrawable(icon)
}
MotionEvent.ACTION_UP -> {
view.performClick()
next.setImageResource(R.drawable.layer_bt_next)
}
}
}
previous -> {
//ingredients here XD
}
}
return true
}
And in that way, I can call single onTouch and implement it to many button and also can use the onClick by :
view.performClick()
Don't forget to implement :
View.OnTouchListener
And set the listener :
next.setOnTouchListener(this)
previous.setOnTouchListener(this)
I don't think your solution will actually solve them problem presented by the warning. The warning states that certain accessibility functions use performClick() to activate buttons. If you look in the View class, the performClick() funtions calls the onClickListener directly, meaning the code in the onTouchListener will not be executed (next.setImageResource(R.drawable.layer_bt_next)) for these accessibility functions, since the view will never be physically touched, and thus your onTouch code won't run. You have to do one of either:
Subclass the view you are setting the onTouchListener on, and override performClick to execute the code, or
Set an onClickListener on the view that executes the code.
You could just implement onClickListener in your onTouchListener class and manually call onClick() from your onTouchListener (where you have view.performClick() now), and then move your executable code to the onClick override. You would also have to set BOTH onTouchListener and onClickListener on your views.
I'm not sure this is the same issue you saw, but since I found this page searching for my issue, I thought I'd add my experience to help others :)
In my case the warning was being generated because the nullable view could have been of type Void. Calling the following:
nullableView?.setOnTouchListener(this)
produced the error:
Custom view Void has setOnTouchListener called on it but does not override performClick
Performing a null check and casting to a View before setting the listener solved for me in this case, since View will override performClick:
if (nullableView != null) (nullableView as View).setOnTouchListener(this)
After a ton of digging, and not being able to fix my variation of this issue with anything in this thread, I finally found a fix. Maybe it will work for some of you. I had this widget listener setter in my MainActivity onCreate function:
findViewById<TextView>(R.id.tvAnimalList).setOnTouchListener { v, event ->
mGestureDetector.onTouchEvent(event)
}
Which results in the warnings:
'onTouch' lambda should call 'View#performClick' when a click is detected
Custom view "TextView" has 'setOnTouchListener' called on it but does not override 'performClick'
First, I added a call to v.performClick(), which got rid of the first warning. Like this:
findViewById<TextView>(R.id.tvAnimalList).setOnTouchListener { v, event ->
v.performClick()
mGestureDetector.onTouchEvent(event)
}
I got rid of the second warning by changing the findViewById cast from <TextView> to <View>. Here's my warning-free result:
findViewById<View>(R.id.tvAnimalList).setOnTouchListener { v, event ->
v.performClick()
mGestureDetector.onTouchEvent(event)
}
private fun closeKeyboard(binding: ContollerMeterBinding) {
binding.scrollView.apply {
setOnTouchListener(OnTouchListener { v, event ->
if (event != null && event.action == MotionEvent.ACTION_MOVE) {
val imm =
activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val isKeyboardUp = imm.isAcceptingText
if (isKeyboardUp) {
imm.hideSoftInputFromWindow(v.windowToken, 0)
}
}
performClick()
false
})
}
}
This works for me: (not directly related to onTouch event but yields the same warning, might be helpful to someone)
takePhotoButton.setOnTouchListener { _, motionEvent ->
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
//when user touch down
}
MotionEvent.ACTION_UP -> {
//when user touch release
}
}
true
}
I'm trying to write an observable that would generate repeated events while the user holds down a view. My code below works well, but only the first time (e.g. if the user presses the button again, nothing happens). Can you please advise what am I doing wrong and what is best practice for this?
val touches = RxView.touches(previousButton)
touches
.filter({ event -> event.action == MotionEvent.ACTION_DOWN })
.flatMap({
Observable.interval(500, 50, TimeUnit.MILLISECONDS)
.takeUntil(touches.filter({event -> event.action == MotionEvent.ACTION_UP}))
}).subscribe({ println("down") })
The problem is that the RxView.touches observable cannot exist for more than 1 source. This means when the subscription inside of the flatMap happens it breaks the original subscription used to trigger the flatMap, making it never occur again.
There are two possible ways around this:
Use .publish(...) to share the source of events instead of using touches.
Map the events into a Boolean on/off observable, then switchMap the appropriate actions based on the current value of the observable.
1.
touches.publish { src ->
src.filter(...)
.flatMap {
Observable.interval(...)
.takeUntil(src.filter(...))
}
}
2.
touches.filter {
it.action == MotionEvent.ACTION_DOWN
or it.action == MotionEvent.ACTION_UP
}
.map { it.action == MotionEvent.ACTION_DOWN }
.distinctUntilChanged() // Avoid repeating events
.switchMap { state ->
if (state) {
Observable.interval(...)
} else {
Observable.never()
}
}