ClickListener in ScrollView intercepts scrolling - android

I have two rows of imageViews in a ScrollView. The imageViews all have onClickListener and when i want to scroll in this area it doesn't work. So i guess the Click listeners intercept the scrolling of the ScrollView. What's the best way to change this behaviour ?
My View hierarchy is like this:
<ScrollView>
<RelativeLayout>
<FrameLayout>
</FrameLayout>
<RelativeLayout>
</ScrollView>
in the FrameLayout i put a Fragment which has a LinearLayout in which i inflate other LinearLayouts like this:
productHolder.productLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
}
});

Maybe you could use an OnTouchListener instead of an OnClickListener.
In the OnTouchListener you can check if the action of the motion event was a click and consume the event (return true). Otherwise you can leave the event for other listeners (return false) to consume.
Note, that you might need to add some additional implementation for figuring out which image view was clicked, however without seeing your layout, it's hard to give you a hint how to do it.

Related

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.

Making a view "solid" in Android

I have a RelativeLayout positioned over the rest of my view and I want this RelativeLayout to stop anything underneath it from being clicked. Here is my document outline:
FrameLayout
LinearLayout
RelativeLayout
The RelativeLayout only has a background colour, and has nothing inside it. The LinearLayout has buttons and textboxes in it. I want to stop people from being able to click on the buttons and textboxes if the RelativeLayout is visible.
You will need to consume the click. Find yourRelativeLayout and set an OnClickListener on it:
yourRelativeLayout.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) { }
});
In the xml file
android:clickable="false"
To do it in code:
LinearLayoutItem.setClickable(false);

onClick not triggered on LinearLayout with child

I've got a custom LinearLayout with a smaller TextView child. I'd like to be able to click the area not covered by the TextView, so I set clickable=true and an onclicklistener to the LinearLayout, but onClick is not triggered. If I set the onclick listener on the TextView it works as expected...
Anybody can help?
ar_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ar_item" android:layout_width="202dp"
android:layout_height="62dp" android:background="#drawable/bg_item_ar"
android:clickable="true">
<TextView android:id="#+id/ar_item_txt"
android:layout_width="164dp" android:layout_height="fill_parent"
android:paddingBottom="8dp" android:paddingLeft="8dp"
android:paddingTop="8dp" android:paddingRight="6dp" android:gravity="center"
android:background="#50000000" />
</LinearLayout>
My custom LinearLayout
public class ARView extends LinearLayout
{
public ARView(final Context context, String name, String id)
{
super(context);
getLayoutInflater().inflate(R.layout.ar_item, this ,true);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.ar_item, null);
TextView textView = (TextView) findViewById(R.id.ar_item_txt);
textView.setText(name);
setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Toast t = Toast.makeText(context, "hey!", Toast.LENGTH_SHORT);
t.show();
}
});
}
}
android:duplicateParentState="true" did not help me.
To make your layout clickable with its children you need add this option for every child:
android:clickable="false"
Then click handling will go up to parent.
for every child
android:duplicateParentState="true"
This isn't your case, but I had similar problem with clickable ViewGroup. After a hour of looking for solution a found out that I set android:inputType to TextView inside my ViewGroup which was blocking onClick() listener (no idea why)
Don't use android:inputType with TextView
Make Your parent LinearLayout's android:clickable="true"
Make all of the the childview's android:clickable="false"
Under Linearlayout - Remove android:inputType="" from TextView
The android:duplicateParentState="true" made my TextView looks like it's disabled, and cannot receive click event.
All you need is set the TextView clickable="false". So the click event will dispatch to parent layout, and the TextView still can react to touch event (with ripple effect).
Your TextView height covers the whole parent (whole layout) so you might clicking on empty space but not on the layout. Try using wrap_content for android:layout_height for your TextView. Set click listener for the layout as well.
You aren't using your custom View; you're using a standard LinearLayout. Your XML tag should be:
<com.yourcode.ARView ...> ... </com.yourcode.ARView>
One thing to make sure of is that another view is not on top of the view you are trying to click. This is especially common in FrameLayouts (where your sub LinearLayout may be covered) or with Relative Layouts you might have forgot to update this line:
android:layout_below="#id/shouldBeTheIdOfTheViewCurrentlyBeingCovered"
so that views don't fill the same space.
If the views in question are TextViews, you may need to set them as focusable="false" so that the first click isn't used focusing on the text view.
The problem may be from the textview that has android:layout_height="fill_parent" in its layout. If that doesn't fix the issue, the problem may be the onClick() event. The linear layout may not actually ever call onClick() since its a layout. Try overriding the onTouch() event listener instead.
Add the following attributes to the linearlayout
Any Click events not handled by the child views will be automatically passed over to the LinearLayout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:focusable="true"
android:clickable="true">
<child/>
<child/>
</LinearLayout>
I faced the same problem, and all the XML Attributes didn't work. I am not sure if this happens because i programmatically inflate and add the views, but this is how i worked around the problem.
I have a Class which extends LinearLayout, with a TextView and an ImageView.
After inflating the layout and getting the views, I assigned the child views a OnClickListener, when pressed, executes the LineaLayout's onClickListner.
public class MyView extends LinearLayout {
private OnClickListener clickListener;
ImageView imageView;
TextView textView;
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
super.setOnClickListener(l);
clickListener = l;
}
void afterViews() {
imageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
clickListener.onClick(MyView.this);
return false;
}
});
textView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
clickListener.onClick(MyView.this);
return false;
}
});
}
I also tried overriding OnTouchListener, but then my child views didn't have the ripple effect, which I needed.
None of the solutions above forked for me. Than i noticed that a LinkMovementMethod set to TextView inside my LinearLayout. This class intercepts touch events, click events and properties of TextView such as clickable, focusable vs..
I faced the same problem, and all the XML attributes didn't work. I think this happens because I programmatically inflate and add the views. The fix for me was to - also that programatically - set the inflated root view not clickable:
View view = layoutInflater.inflate(R.layout.some_layout, someLinearLayout, false);
view.setClickable(false);
(Yes, I tried to have the some_layout not clickable in XML.)
android:duplicateParentState="true" >= not worked
focusable="false" >= not worked
clickable="false>= not worked
So i did a quick fix by adding Transparent Button on top with match_parent for height and width
Problem solved without any extra code,

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.

ListView items not clickable with HorizontalScrollView inside

I have quite a complicated ListView. Each item looks something like this:
> LinearLayout (vertical)
> LinearLayout (horizontal)
> include (horizontal LinearLayout with two TextViews)
> include (ditto)
> include (ditto)
> TextView
> HorizontalScrollView (this guy is my problem)
> LinearLayout (horizontal)
In my activity, when an item is created (getView() is called) I add dynamic TextViews to the LinearLayout inside the HorizontalScrollView (besides filling the other, simpler stuff out). Amazingly, performance is pretty good.
My problem is that when I added the HorizontalScrollView, my list items became unclickable. They don't get the orange background when clicked and they don't fire the OnItemClickedListener I have set up (to do a simple Log.d call).
How can I make my list items clickable again?
Edit: setting android:descendantFocusability="blocksDescendants" on the topmost LinearLayout seems to work. I'd like to know if there are other ways, though: what if I want focusable items in my list items?
Using android:descendantFocusability="blocksDescendants" on the topmost LinearLayout did the trick. Elements inside can still be made "clickable", they're just not focusable (i.e. you can't click them on a non-touchscreen device). Good enough for me.
when i applied HorizontalScrollView to my TableLayout scrolling is working fine but unable to click on list item
My layout is as follwos
Linearlayout
HorizontalScrollView
TableLayout
.........
/TableLayout
/HorizontalScrollView
/LinearLayout
i applied android:descendantFocusability="blocksDescendants" to my top most linearlayout Any Help
android:descendantFocusability="blocksDescendants"
didn't help for me.
So
So, i realized listener patern.
public interface EventListItemOnClickListener {
public void itemClicked();
}
And in adapter notify all listeners
private List<EventListItemOnClickListener> listeners;
protected void notifyOnClick(int position){
for(int i=0; i<listeners.size();++i)
listeners.get(i).itemClicked((Event)events.get(position));
}
...
#Override
public void onClick(View v) {
notifyOnClick(position);
}
...

Categories

Resources