Handling onTouch event in a custom container, with TextView as content - android

I have the following problem.
In an Android application, there is a custom layout class, which extends the FrameLayout. It overrides the onTouchEvent method for a custom implementation. We have a TextView which resides as the content of this layout class. We want that the URL links in this TextView to be clickable. For that purpose, we add 'android:autoLink="web"' to the TextView's property. The following Xml ensues:
<PinchZoomScrollView
android:layout_weight="350"
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="#+id/newsDetailScrollView">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:id="#+id/newsDetailText"/>
</PinchZoomScrollView>
After adding autoLink="web", it seems that the onTouchEvent of the custom layout class "PinchZoomEvent" is not being called. It seems that the TextView with the autoLink property blocks touch events somehow. This is weird since the PinchZoomScrollView is TextView's parent and it should receive the event first. What should be done in this case?

Parents don't automatically receive touches first in Android. You're thinking of other frameworks. Touch events start at the children and go up the chain in Android. If you have a parent class such as a scroller that wants to intercept touch events and possible override the child behavior, it needs to implement onInterceptTouchEvent, and return true from it when it detects an action it wants to take from the children.

Related

Android: OnClick margin space in RecyclerView.Adapter

I want to execute some action if user click the space(created by margin) between two items in RecyclerView, however I did not found a way to do that. OnTouchListener might do the trick but it is posible to get view by given coordinate (x, y)?
Setting onTouchListener on root does not do the trick, it seems like the event is handled by view on top of it. I want to register that can handle all ontouch event and pass the ontouch event to the right view after pre-handle.
You can also achieve this by creating margin using View tag-
Something like this-
<View
android:id="#+id/left_margin_view"
android:layout_width="10dp"
android:layout_height="match_parent"/>
Remove margin applied to recycle item root tag and create view and now you can use any event listener with this view id left_margin_view.

Android - distinguish ViewGroup click and child View long click [duplicate]

I have a TextView in a layout whos background is a Selector. And the TextView's text is set to Spanned from HTML.
Then I set the TextView with the LinkMovementMethod.
Now when I tap on the TextView, the click event is not sent to its parent layout to trigger the selector.
How should this be solved?
Declare your TextView not clickable / focusable by using android:clickable="false" and android:focusable="false" or v.setClickable(false) and v.setFocusable(false). The click events should be dispatched to the TextView's parent now.
Note:
In order to achieve this, you have to add click to its direct parent. or set
android:clickable="false" and android:focusable="false" to its direct parent to pass listener to further parent.
I think you need to use one of those methods in order to be able to intercept the event before it gets sent to the appropriate components:
Activity.dispatchTouchEvent(MotionEvent) - This allows your Activity to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent) - This allows a ViewGroup to watch events as they are dispatched to child Views.
ViewParent.requestDisallowInterceptTouchEvent(boolean) - Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).
More information here.
Hope that helps.
Sometime only this helps:
View child = parent.findViewById(R.id.btnMoreText);
child.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View parent = (View) v.getParent();
parent.performClick();
}
});
Another variant, works not always:
child.setOnClickListener(null);
Put
android:duplicateParentState="true"
in child then the views get its drawable state (focused, pressed, etc.) from its direct parent rather than from itself.
you can set onclick for parent and it call on child clicked
If your TextView create click issues, than remove android:inputType="" from your xml file.
This answer is similar to Alexander Ukhov's answer, except that it uses touch events rather than click events. Those event allow the parent to display the proper pressed states (e.g., ripple effect). This answer is also in Kotlin instead of Java.
view.setOnTouchListener { view, motionEvent ->
(view.parent as View).onTouchEvent(motionEvent)
}
If you want to both OnTouch and OnClick listener to parent and child view both, please use below trick:
User ScrollView as a Parent view and inside that placed your child view inside Relative/LinearLayout.
Make Parent ScrollView android:fillViewport="true" so View not be scrolled.
Then set OnTouch listener to parent and OnClick listener to Child views.
And enjoy both listener callbacks.
Then I set the TextView with the LinkMovementMethod.
TextView.setMovementMethod() internally calls a private method fixFocusableAndClickableSettings. It is the root of the problem: calls setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true);. So no matter what clickability you think you've set, it'll be all true.
source
Those 3 flags have to be reset back to false in order for the view to become non-clickable.

Android Textview Selection not working

I am using a TextView in my application. Here is the XML for that component.
<TextView
android:id="#+id/txt_MyText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:textSize="90px"
android:textIsSelectable="true"
android:fadeScrollbars="false"
android:text="#string/dummy_text"/>
Now in my code, I am setting movement method for my TextView using this code,
txt_MyText.setMovementMethod(new ScrollingMovementMethod());
After setting the movement method, text view fails to make a selection of Text when user long presses the TextView. In Logs, I can see this message,
TextView does not support text selection. Selection cancelled.
But if I remove the movement method code the TextView continues to allow selection.
What's going wrong here?
Thanks
The issue here is the focus. The Android Doc has the explanation here:
void setMovementMethod (MovementMethod movement)
Sets the MovementMethod for handling arrow key movement for this TextView. This can be null to disallow using the arrow keys to move the cursor or scroll the view.
Be warned that if you want a TextView with a key listener or movement method not to be focusable, or if you want a TextView without a key listener or movement method to be focusable, you must call setFocusable(boolean) again after calling this to get the focusability back the way you want it.
EDIT
I believe it could be how you are setting your TextView's movementMethod.
Try it like so:
myTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
try
txt_MyText.setTextIsSelectable(true);
txt_MyText.setFocusable(true);
txt_MyText.setFocusableInTouchMode(true);

Is it possible to set event listener in XML?

When I using XML to design layout, I am using findViewById() in java code to load views and set listeners to them.
Is this correct what I am doing? May be it is possible to set listeners in XML or something?
Most people set their listeners in code. It's sometimes easier to do it in code because you often times will need to add or remove listeners based on some action or state.
However, Android also gives you the option of setting a OnClickListener for any View in XML. Here's an example:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onActionClick"
android:text="Action" />
Using the onClick attribute, you assign the name of the method that will handle the click. This method must exist in the same Context as the View, so in the same Activity. So for example, I would have to implement this method:
public void onActionClick(View v) {
// Do stuff for my "Action" button...
}
I believe it has to have a View parameter, just like implementing OnClickListener would. I also believe it must be public.
So as far as which way is "best"? That's up to you. Both routes are viable. It's worth noting though that this is only useful for click listeners and not other types of listeners.
There is an onClick attribute that you can use inside your xml.
You may specify android:onClick attribute when describe your View in XML. The value of this attribute is the name of the method which will be called at your View's onClick event. Then you should define this method as public in your code and you could pass there View object which will determine what View from those that you described at xml with the same android:onClick attributes has gotten touch event from user.
you can set an onClick but I think thats about it

How to pass a view's onClick event to its parent on Android?

I have a TextView in a layout whos background is a Selector. And the TextView's text is set to Spanned from HTML.
Then I set the TextView with the LinkMovementMethod.
Now when I tap on the TextView, the click event is not sent to its parent layout to trigger the selector.
How should this be solved?
Declare your TextView not clickable / focusable by using android:clickable="false" and android:focusable="false" or v.setClickable(false) and v.setFocusable(false). The click events should be dispatched to the TextView's parent now.
Note:
In order to achieve this, you have to add click to its direct parent. or set
android:clickable="false" and android:focusable="false" to its direct parent to pass listener to further parent.
I think you need to use one of those methods in order to be able to intercept the event before it gets sent to the appropriate components:
Activity.dispatchTouchEvent(MotionEvent) - This allows your Activity to intercept all touch events before they are dispatched to the window.
ViewGroup.onInterceptTouchEvent(MotionEvent) - This allows a ViewGroup to watch events as they are dispatched to child Views.
ViewParent.requestDisallowInterceptTouchEvent(boolean) - Call this upon a parent View to indicate that it should not intercept touch events with onInterceptTouchEvent(MotionEvent).
More information here.
Hope that helps.
Sometime only this helps:
View child = parent.findViewById(R.id.btnMoreText);
child.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
View parent = (View) v.getParent();
parent.performClick();
}
});
Another variant, works not always:
child.setOnClickListener(null);
Put
android:duplicateParentState="true"
in child then the views get its drawable state (focused, pressed, etc.) from its direct parent rather than from itself.
you can set onclick for parent and it call on child clicked
If your TextView create click issues, than remove android:inputType="" from your xml file.
This answer is similar to Alexander Ukhov's answer, except that it uses touch events rather than click events. Those event allow the parent to display the proper pressed states (e.g., ripple effect). This answer is also in Kotlin instead of Java.
view.setOnTouchListener { view, motionEvent ->
(view.parent as View).onTouchEvent(motionEvent)
}
If you want to both OnTouch and OnClick listener to parent and child view both, please use below trick:
User ScrollView as a Parent view and inside that placed your child view inside Relative/LinearLayout.
Make Parent ScrollView android:fillViewport="true" so View not be scrolled.
Then set OnTouch listener to parent and OnClick listener to Child views.
And enjoy both listener callbacks.
Then I set the TextView with the LinkMovementMethod.
TextView.setMovementMethod() internally calls a private method fixFocusableAndClickableSettings. It is the root of the problem: calls setFocusable(FOCUSABLE); setClickable(true); setLongClickable(true);. So no matter what clickability you think you've set, it'll be all true.
source
Those 3 flags have to be reset back to false in order for the view to become non-clickable.

Categories

Resources