I'm creating a custom widget by expanding LinearLayout. One of the elements in my custom widget is a linear layout, inflated from another layout. When I set the OnClickListener it is not responding. Can you please advise?
Thanks!
Instead of using setOnClickListener use setOnTouchListener
This code will work as a onclick event
YourLinearLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean returnValue = true;
if(event.getAction()==MotionEvent.ACTION_UP){ //on touch release
returnValue = false; //prevent default action on release
//do something here
}
return returnValue;
}
});
And then add this to your LinearLayout class to intercept child touch events
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true; //will prevent child touch events
}
Have you declared the LinearLayout clickable?
You can do this either in the XML:
android:clickable="true"
or in Java code:
myLinearLayout.setClickable( true );
Also see the other StackOverflow thread on this question:
onClickListener on a LinearLayout
Make sure you put all childs inside LinearLayout to android:clickable="false". Than myLinearLayout.setClickable( true ) works for me.
Related
In my layout I have a structure like that:
--RelativeLayout
|
--FrameLayout
|
--Button, EditText...
I want to handle touch events in the RelativeLayout and in the FrameLayout, so I set the onTouchListener in these two view groups. But only the touch in the RelativeLayout is captured.
To try solve this, I wrote my own CustomRelativeLayout, and override the onInterceptTouchEvent, now the click in the child ViewGroup (FrameLayout) is captured, but the click in the buttons and other views doesn't make any effect.
In my own custom layout, I have this:
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
You need to override the onInterceptTouchEvent() for each child, otherwise it will remain an onTouchEvent for the parent.
Intercept Touch Events in a ViewGroup
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
...
// In general, we don't want to intercept touch events. They should be
// handled by the child view.
return false;
}
You need to return false to have the child handle it, otherwise you are returning it to the parent.
Your custom solution will capture touch events from anywhere in your relative layout since the overridden method is set to always throw true.
For your requirement I guess its better to use
the onClick method rather than using onTouch.
OnTouch method invokes different threads on every TouchEvent and I guess that is the cause of your problem
Rather than handling these events its better to try onClick method.
I was able to solve this problem with the following code:
Step 1: declare the EditText above the onCreate () method
public EditText etMyEdit;
Step 2: in the onResume () method the configuration ends:
etMyEdit = (EditText) findViewById (R.id.editText);
etMyEdit.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_UP:
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
return false;
}
});
Hope it helps someone!
I have a list view whose item is a linearlayout which has a button as it's child view. I want the ontouchLIstener of the linearlayout to work. I don't want to use onInterceptTouchEvent. Is there a way a I can pass on the touch form the button to the parent listview. I tried this
- returning true from the button's onTouchListener
private View.OnTouchListener buttonListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.i(TAG, "Button On Touch");
return true;
}
};
But this does not work. It does not pass on the touch event to the linearlayout's onTouchListener.
There must be someway it should work.
The chain of events for a View group works like this - The dispatchEvent is called. Then dispatchEvent calls onInterceptTouchEvent. if it returns false, then the touch events are passed on to the children of the ViewGroup. If any of the children consume the event (in this case the button consumes the event) i.e if they return true then the motionevent is not passed on to other methods. Since the button is clickable it returns true in this case. If the onInterceptTouchEvent method returns true then the child views are not given the motion event and instead that ViewGroup's onTouchListener or onTouch method are called. Hence to pass on the touch event to the parent's (View Group) onTouchListener make sure to return true in the onInterceptTouchEvent method of the Parent (ViewGroup). You don't have to override onDispatchTouchEvent()
#Override
public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
Log.i(TAG,"PARENT.onInterceptTouchEvent");
return true;
}
For more details about how the touch navigation is done, please see this stack post
Set your button clickable property to false, using:
button.setClickable(false);
Then in onTouch of button:
return false;
Note: This behavior is specific to button (and any other view that has clickable property set to true) that even if you return false from onTouch it will not propagate event to the parent and onClick method of the button will be called anyway.
EDIT: Another way is extending ListView class and overriding onInterceptTouch:
public class CustomListView extends ListView {
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// here you can listen for button touch events
}
}
The question explains everything I want.
HorizontalScrollView seems to be the only widget for which setOnClickListener() does'nt trigger.
NOTE: I can't use onTouch event because on every touch it is triggered 4-5 times.
I also can't use onTouch on its parent view because parent view has many buttons with different functionality.
The following description is not important:
But still, these are the existing links I've searched(none helped):
horizontalscrollview onclicklistener doesn't get called
How to implement on click listener for horizontalscrollview
Click event of HorizontalScrollView
Android click event of items in HorizontalScrollView not respond after scroll
These are the links which I posted(none got answered completely):
Insert views in HorizontalScrollView programatically
One of the ImageButton not clicking & make HorizontalScrollView clickable
I asked multiple questions in these links, 1 of which was "onClickListener for HorizontalScrollView". That part of my question was never answered.
Hence an all exclusive question.
I came up with two ways of doing it.
The simpler (but less ideal) one:
HorizontalScrollView scrollView = (HorizontalScrollView) findViewById(R.id.scrollView);
scrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// Do stuff
}
return false;
}
});
This will get called only once when you click, drag or whatever you do with your finger. However, it will also react to all kinds of gestures, so if you want to detect only tap/click events, you need to further investigate the "event" object and filter out events you don't need. This may be more work than you would like, so you should better use a GestureDetector to do it for you.
This leads to method 2:
HorizontalScrollView scrollView = (HorizontalScrollView) findViewById(R.id.scrollView);
final GestureDetector detector = new GestureDetector(this, new OnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
// Do stuff.
return false;
}
// Note that there are more methods which will appear here
// (which you probably don't need).
});
scrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
return false;
}
});
Well it happened to me now and I came here for solution, but in my case it was just easier to listen to onClick to its child view instead...
I have a scrollView with lot of elements
ScrollView scroller = (ScrollView)findViewById(R.id.scrollView);
I need to attach an onClickListener to the scrollview so I do
scroller.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// This is all you need to do to 3D flip
AnimationFactory.flipTransition(viewAnimator, FlipDirection.LEFT_RIGHT);
}
});
But this is not getting triggered when I touch. Any Ideas?
The best solution seem to put LinearLayout into ScrollView and set the setOnClickListener on it.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/myLayout"
android:clickable="true"
android:orientation="vertical">
<!-- content here -->
</LinearLayout>
</ScrollView>
in the Activity :
LinearLayout lin = (LinearLayout) fragment.rootView.findViewById(R.id.myLayout);
lin.setOnTouchListener(new setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Whatever
}
});
You need to set the setOnClickListener directly on the ScrollView's child.
Since a ScrollView can have only one child, you can simply use this approach:
ScrollView scrollView = //...
View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
// ...
}
//Set the onClickListener directly on the ScrollView's child
scrollView.getChildAt(0).setOnClickListener(mOnClickListener);
It is because the child of the ScrollView is getting the touch event of the user and not the ScrollView. You must set android:clickable="false" attribute to each and every child of the ScrollView for the onClickListener to work on ScrollView.
Or else the alternate could be to set the onClickListener on each of the ScrollView's children and handle it.
UPDATE 22/12/2020
sadly this also triggers after each scroll event.
This the actually the answer to the question without any odd cases by using View.OnTouchListener instead of View.OnClickListener on the ScrollView and detecting the MotionEvent.ACTION_UP where the finger is left off the screen.
To make sure that it's not a scroll, then save previous touched screen x, y values of the MotionEvent.ACTION_DOWN and compare it to those of MotionEvent.ACTION_UP. If they are not equal then certainly the user is moving their finger (i.e. scrolling) before they left it off the screen.
int mXOld, mYOld; // field values to save the tap down on the ScrollView
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mXOld = x;
mYOld = y;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (x == mXOld || y == mYOld) { // detecting it's not a horizontal/vertical scrolling in ScrollView
// HERE add the code you need when the ScrollView is clicked
Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
return false;
}
}
return false;
}
});
Original Answer: Odd case that is different than the question
My problem was somehow different, so I wanted to share it..
I have a ScrollView that I have to use match_parent in its width & height; and I have an internal TextView that is centered in the ScrollView.
The text of the TextView can be long so it occupies the full height of the ScrollView, and sometimes it can be short, so there will be blank areas on the top and bottom., So setting the OnClickListener on the TextView didn't help me whenever the text is short as I want the blank areas detects the click event as well; and also the OnClickListener on the ScrollView doesn't work..
So, I solved this by setting OnTouchListener on the ScrollView and put code into MotionEvent.ACTION_UP So it can kind of simulating complete tap by lefting off the finger off the screen.
private View.OnTouchListener mScrollViewTouchListener = new View.OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// DO Something HERE...
}
return false;
}
};
As #Zain pointed out, sometimes it is necessary to capture OnClicks for the whole area of the Scrollview, while the childs may be smaller or invisible.
To circumvent scrolling detected as an onClick, we used a GestureDetector:
final protected GestureDetector gestureDetector = new GestureDetector(getActivity(),
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
in onCreateView
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(gestureDetector.onTouchEvent(event)){
[do stuff]
}
return false;
}
});
I think you can custom a ScrollView, override the dispatchTouchEvent method, add add the custom onClick callback.
I have one linearlayout and have also a few button inside it.I want make it visible when touch and invisible when touch it again.
How can i make it??
LinearLayout one = (LinearLayout) findViewById(R.id.one);
one.setVisibility(View.GONE);
I suggest that you use GONE insteady of INVISIBLE in the onclick event because with
View.GONE the place for the layout will not be visible and the application will not appear to have unused space in it unlike the View.INVISIBLE that will leave the gap that is intended for the the layout
Add a boolean on your code
boolean flag = false;
then add android:clickable = true on your linear layout on xml
then use this code for reference
your_linear_layout = new OnClickListener(){
#Override
public void onClick(View v) {
if (flag){
// means true
your_linear_layout.setVisibility(View.INVISIBLE);
flag = false;
}
else{
your_linear_layout.setVisibility(View.VISIBLE)
flag = true;
}
}
};
Havent tried this yet but this should work..
Cheers
add setOnTouchListener to linearLayout get touch events as :
linearLayout.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// show-hide view here
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
// show-hide view here
return true;
}
return false;
}
});
for making View visible use yourview.setVisibility(View.VISIBLE) and for Invisible use yourview.setVisibility(View.INVISIBLE)
You should user
Invisible -: mButton.setVisibility(View.INVISIBLE);
Vsible -: mButton.setVisibility(View.VISIBLE);
Put this code in onclick listner of button With checking if condition.