Android ImageButton OnTouchListener not working - android

Okay, there's a weird thing happening in me.
I have an ImageButton named tab_btn from other layout which I imported and set onTouchListener which is working.
package com.xx
import kotlinx.android.synthetic.main.tab_btn_layout.*
import kotlinx.android.synthetic.main.btnNext_layout.*
class EventDetails : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_details)
tab_btn.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
if (event!!.action == MotionEvent.ACTION_DOWN) {
val icon: Drawable = ContextCompat.getDrawable(applicationContext, R.drawable.talk_bt_tab)
icon.setColorFilter(Color.GRAY,PorterDuff.Mode.MULTIPLY)
tab_btn.setImageDrawable(icon)
}else if (event!!.action == MotionEvent.ACTION_UP) {
tab_btn.clearColorFilter()
}
return true
}
})
btnNext.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(p0: View?, ev: MotionEvent?): Boolean {
if (ev!!.action == MotionEvent.ACTION_DOWN){
val icon: Drawable = ContextCompat.getDrawable(applicationContext, R.drawable.layer_bt_next)
icon.setColorFilter(Color.GRAY,PorterDuff.Mode.MULTIPLY)
btnNext.setImageDrawable(icon)
}else if(ev!!.action == MotionEvent.ACTION_UP){
btnNext.clearColorFilter()
}
return true
}
})
}
}
and below that I have another ImageButton from other layout named btnNext. I set the same OnTouchListener on it. But it gives me error .
And btnNext gives me error:
Attempt to invoke virtual method 'void android.widget.ImageButton.setOnTouchListener(android.view.View$OnTouchListener)' on a null object reference
note: I have imported both layout of the Image button. tab_btn is working but btnNext is not working.

Okay, I solved my own problem.
The real suspect is that my btnNext is in the fragment(viewpager) which I have to inflate first the rootview in oncreateview method.
note: you cannot access the element of the fragment without inflating it.
here's ma code:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {
val rootView = inflater!!.inflate(R.layout.btnNext_layout, container, false)
var next : ImageButton = rootView.findViewById(R.id.btnNext)
next.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(p0: View?, ev: MotionEvent?): Boolean {
if (ev!!.action == MotionEvent.ACTION_DOWN){
val icon: Drawable = ContextCompat.getDrawable(activity.applicationContext, R.drawable.layer_bt_next)
icon.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY)
btn_next.setImageDrawable(icon)
}else if (ev!!.action == MotionEvent.ACTION_UP){
val icon: Drawable = ContextCompat.getDrawable(activity.applicationContext, R.drawable.layer_bt_next)
icon.setColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)
btn_next.setImageDrawable(icon)
}
return true
}
})
}

Related

Pass touch event from dialog fragment to View right below (inside the parent activity)

As you can see in the image , red border rectangle is the parent activity . Blue one is dialog fragment . The circle is indicating a view and rectangle below is the description. I want the click on circle to be passed down to the button. So far i have tried
1. overriding onTouchEvent in Circle View and return false
2. setOntouchListener on circle view and call activity.dispatchTouchListener and return false
3. mark dialog frgament and circle view clickable/focusable false.
None of the above seems to be calling onCLickListener of the button underneath. I can see touch event being received in Activity's onIterceptTouch() though. Please help
Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val viewTreeObserver = button2.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {MainFragment.newInstance(button2).show(supportFragmentManager, "dialog")
button1.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
container.setOnClickListener {
Log.d("Test","Activity clicked")
}
button.setOnClickListener {
Log.d("Test","Button Clicked")
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.d("Test","Activity onTouchEvent")
return super.onTouchEvent(event)
}
}
Dialog Fragment
class MainFragment : DialogFragment() {
companion object {
fun newInstance(view: View?) : MainFragment {
val args = Bundle()
val point = getRippleLocation(getViewCenterLocation(view))
args.putInt("X", point.x)
args.putInt("Y", point.y)
val frag = MainFragment()
frag.arguments = args
return frag
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
dialog.window!!.setGravity(Gravity.START or Gravity.TOP)
point.x = arguments!!.getInt("X")
point.y = arguments!!.getInt("Y")
params.x = point.x
params.y = (point.y - 50)
dialog.window!!.attributes = params
return dialog
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addRippleView(main, 0, 0)
}
private fun addCircleView(rootView: ViewGroup, leftMargin: Int, topMargin: Int) {
val rippleView = BaseCircleView(context, null)
rippleView.isClickable = false
rippleView.isFocusable = false
rippleView.setOnTouchListener(object : View.OnTouchListener{
override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
activity!!.dispatchTouchEvent(p1)
return false
}
})
configureRipple(rippleView)
context?.resources?.let { resources ->
rippleView.id = R.id.gather_ripple_view_id
val params = RelativeLayout.LayoutParams(resources.getDimensionPixelSize(R.dimen.gather_on_boarding_ripple_container_width),
resources.getDimensionPixelSize(R.dimen.gather_on_boarding_ripple_container_height))
params.leftMargin = leftMargin
params.topMargin = topMargin
rootView.addView(rippleView, params)
}
}
}
If someone is still struggling with this one, this is how is solved it.
dialog?.window?.decorView?.setOnTouchListener { v, event ->
activity?.dispatchTouchEvent(event)
false
}
this will pass touch event through
setting the FLAG_NOT_TOUCH_MODAL worked for me.
Window window = dialog.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);

Can't assign values outside onTouchListener

when a item is pressed I want to modify a value declared outside the onCreate method. But when the item is pressed, the onTouchListener does the other functions except modify that value. I've tried to write the same instruction to modify the value outside the onTouchListener method and it worked. This is my code:
class MainActivity : AppCompatActivity() {
var ciao: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var listenerUser = View.OnTouchListener(function = { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
this.ciao = "ciao"
}
true
})
}
override fun onResume() {
super.onResume()
if (ciao != null) {
println(ciao!!)
}
}
}
When my application resumes the var ciao is null, but if I put this.ciao = "ciao" outside the listener the var isn't null.
You need to set your OnTouchListener for the view that should react:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listener = View.OnTouchListener(function = {view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN) {
this.ciao = "ciao"
}
true
})
clickedView.setOnTouchListener(listener)
}
If you just want to detect a click you are probably better off using a OnClickListener like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
clickedView.setOnClickListener(_ ->
this.ciao = "ciao"
)
}

Kotlin edittxt.onKeyListener with emulator keyboard

I'm trying to reset a button to become clickable once a key on the keyboard is pressed. It works with the hardware keyboard but not with the emulator keyboard. How would one implement this?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.log_in_button)
val email = findViewById<EditText>(R.id.email_field)
button.isClickable = false
button.alpha = .5f
email.setOnKeyListener(object : View.OnKeyListener {
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
if(event.action == KeyEvent.ACTION_DOWN) {
button.isClickable = true
button.alpha = 1f
return true
}
return false
}
})
}

Android - View instance gets null on screen rotation

I am using Kotlin Android Extension to access view directly by their id.
I have a progress bar which I access directly in fragment using id i.e progress_bar
<ProgressBar
android:id="#+id/progress_bar"
style="#style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="15dp"
android:indeterminate="true"/>
In fragment, I am showing and hiding it with this code
progress_bar.visibility = if (visible) View.VISIBLE else View.GONE
It is working perfectly until I rotate the screen. After that, it throws the exception
java.lang.IllegalStateException: progress_bar must not be null.
The variable gets null on screen rotation. How to solve this problem?
Fragment code
class SingleAppFragment : Fragment() {
private lateinit var appName: String
companion object {
fun newInstance(appName: String = ""): SingleAppFragment {
val fragment = SingleAppFragment()
val args = Bundle()
args.putString(Constants.EXTRA_APP_NAME, appName)
fragment.arguments = args
return fragment
}
}
private var mListener: OnFragmentInteractionListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appName = if (arguments != null && !arguments.getString(Constants.EXTRA_APP_NAME).isEmpty()) {
arguments.getString(Constants.EXTRA_APP_NAME)
} else {
Constants.APP_NAME_FACEBOOK
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_single_app, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
setEventListeners()
}
private fun initView() {
var canShowSnackBar = true
web_single_app.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
showHideProgressBar(true)
canShowSnackBar = true
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
showHideProgressBar(false)
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
web_single_app.stopLoading()
if (canShowSnackBar) {
mListener?.onErrorWebView()
canShowSnackBar = false
}
}
}
web_single_app.settings.javaScriptEnabled = true
web_single_app.loadUrl(Constants.APP_NAME_URL_MAP[appName])
}
private fun setEventListeners() {
back_web_control.setOnClickListener({
web_single_app.goBack()
})
}
fun showHideProgressBar(visible: Boolean) {
progress_bar_web_control.visibility = if (visible) View.VISIBLE else View.GONE
}
fun loadUrl(appName: String) {
web_single_app.loadUrl(Constants.APP_NAME_URL_MAP[appName])
}
override fun onAttach(context: Context?) {
super.onAttach(context)
if (context is OnFragmentInteractionListener) {
mListener = context
}
}
override fun onDetach() {
super.onDetach()
mListener = null
}
interface OnFragmentInteractionListener {
fun onErrorWebView()
}
}
Steps to reproduce:
Start Activity
Fragment get loaded
At Fragment load, I load an URL and show a progress bar
At loading the URL I rotate the phone and the progress bar variable gets null
In my case this bug happens from time to time. Of course, onViewCreated() is a good method to place your code in. But sometimes it's strangely not enough. And setRetainInstance(true) may help, may not. So sometimes this helps: access your Views with a view variable. You can even access them inside onCreateView(). You can use ?. for a guarantee that an application won't crash (of course, some views won't update in this case). If you wish to get context, use view.context.
In my case this bug reproduced only in Kotlin coroutines.
private fun showProgress(view: View) {
view.progressBar?.visibility = View.VISIBLE
}
private fun hideProgress(view: View) {
view.progressBar?.visibility = View.GONE
}
Then in code:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showData(view)
}
private fun showData(view: View) {
showProgress(view)
adapter = SomeAdapter()
adapter.setItems(items)
val divider = SomeItemDecoration(view.context)
view.recycler_view?.run {
addItemDecoration(divider)
adapter = this#SomeFragment.adapter
layoutManager = LinearLayoutManager(view.context)
setHasFixedSize(true)
}
hideProgress(view)
}
In which method do you get the progress_bar by Id?
Please consider the fragment state lifecycle. Maybe you try to load it when the view is not ready yet.
Ensure your progress_bar variable is assigned only after the view is ready. For example in the
onViewCreated method.
See here the official Android lifecycle:
Update
As #CoolMind pointed out the diagram doesn't show the method onViewCreated.
The complete Android Activity/Fragment lifecycle can be found here:
Add retain intance true to the fragment so that it will not be destroyed when an orientation changes occurs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance=true
}
Also do a null check using safe call operator before accessing views
fun showHideProgressBar(visible: Boolean) {
progress_bar_web_control?.visibility = if (visible) View.VISIBLE else View.GONE
}

Intercept motion event from RecyclerView in opened PopupWindow

In my case I want to open PopupWindow by long press on ViewHolder item and process motion event in this window without removing finger. How can I achieve this?
I trying to open CustomPopupWindow by follow:
override fun onBindViewHolder(holder: Item, position: Int) {
val item = items[position]
holder.bindView(testItem)
holder.itemView.view.setOnLongClickListener {
val inflater = LayoutInflater.from(parent?.context)
val view = inflater.inflate(R.layout.popup_window, null)
val popupMenu = CustomPopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
popupMenu.elevation = 5f
popupMenu.showAsDropDown(holder.itemView.view)
true
}
}
and after that disable scrolling in RecyclerView:
class CustomLayoutManager(context: Context) : LinearLayoutManager(context) {
var scrollEnabled: Boolean = true
override fun canScrollVertically(): Boolean {
return scrollEnabled
}
}
Here my CustomPopupWindow:
class CustomPopupWindow(contentView: View?, width: Int, height: Int) : PopupWindow(contentView, width, height), View.OnTouchListener {
init {
contentView?.setOnTouchListener(this)
setTouchInterceptor(this)
}
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
Log.i("Touch", "Touch")
}
MotionEvent.ACTION_MOVE -> {
Log.i("Touch", "Event {${event.x}; ${event.y}}")
}
MotionEvent.ACTION_UP-> {
Log.i("Touch", "Up")
}
}
return true
}
}
In this case onTouch() event never called in CustomPopupWindow only if I remove finger and tap again.
Thanks advance!
SOLVED
I solved this by adding a touch listener to the anchor view:
holder.itemView.view.setOnLongClickListener {
val inflater = LayoutInflater.from(parent?.context)
val view = inflater.inflate(R.layout.popup_window, null)
val popupMenu = CustomPopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
popupMenu.elevation = 5f
it.setOnTouchListener(popupMenu) // solution
popupMenu.showAsDropDown(it)
true
}
Thanks #Brucelet
If you can refactor to using a PopupMenu, then I think PopupMenu.getDragToOpenListener() will do what you want. Similar for ListPopupWindow.createDragToOpenListener().
You could also look at the implementation of those methods for inspiration in creating your own.

Categories

Resources