I have a listview with a padding top. I need to detect when the empty space on top is clicked.
I tried OnClickLister but I cannot use it on ListView. OnItemClickListener works only when I click on a row.
you can add header with nothing in it to listView and set onClickListener for that header
Use a Space with a listener attached on it instead of padding. Or add a listener to the parent view and look for that event.
You can use OnTouchListener and check if the Y coordinate of the MotionEvent is less than your padding :
listView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_UP :
final int padding = getResources().getDimensionPixelSize(R.dimen.listView_padding);
if (motionEvent.getY() < padding) {
// do stuff here
return true;
}
break;
}
return false;
}
});
Related
I have two view pager, one is nested in the other. To get the correct behaviour (swiping the inner view pager without changing the outer) I had to override the inner view pagers onTouchListener and put all my onTouch/onClick logic into it (got the idea from here).
Works all fine, but since I don't have a onClickListener anymore I lost my selector effect. When I put android:clickable="true" on the layout element I get my selector effect, but the view pagers behaviour is wrong again.
Is there any way to achieve the selector effect out of the onTouchListener?
innerPager.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
Log.d(DEBUG_LOG, "Single tap.");
return true;
} else if(event.getAction() == MotionEvent.ACTION_DOWN && v instanceof ViewGroup) {
((ViewGroup) v).requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
This solved it for me. Just added the following code to my OnTouchListener and replaced the card view with my inner view pagers current item:
// Since the host view actually supports clicks, you can return false
// from your touch listener and continue to receive events.
myTextView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
// Convert to card view coordinates. Assumes the host view is
// a direct child and the card view is not scrollable.
float x = e.getX() + v.getLeft();
float y = e.getY() + v.getTop();
// Simulate motion on the card view.
myCardView.drawableHotspotChanged(x, y);
// Simulate pressed state on the card view.
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
myCardView.setPressed(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
myCardView.setPressed(false);
break;
}
// Pass all events through to the host view.
return false;
}
});
My Recyclerview item occupies the whole screen height and width.
I wish to implement a custom scroll inside the Recyclerview such that it scrolls only one item at a time ( similar to News In Shorts app ).
Thanks in advance!!
You need to add just two below Lines
RecylerView mRecylerView = (RecylerView).findViewById(R.id.mRecylerView)
SnapHelper mSnapHelper = new PagerSnapHelper();
mSnapHelper.attachToRecyclerView(mRecylerView);
SnapHelper can be used to snap the child to any imaginary point or any pixel on the screen
Try this code
parentScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
findViewById(R.id.childScrollView).getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
});
childScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// Disallow the touch request for parent scroll on touch of
// child view
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
I have a simple Button, that has a touch listener. Once it's triggered on ACTION_DOWN action, a ListView appears on top of that button (so that the ListView is under user's finger).
What I want is to "pass" that touch event from Button to that ListView, so that when moved up/down, the list view would also scroll up/down.
Simply put:
User touches a Button
A list view appears on covering that button
WITHOUT RELEASING A FINGER, user starts moving finger to the top/bottom screen edge,
The list view scrolls.
UPDATE
I tried making a custom ListView component with onInterceptTouchEvent overridden, but I do not clearly get what should go into that method?
You should create a custom class extending ListView, then override the method onInterceptTouchEvent(MotionEvent e). That method gets called whenever a view inside the list is touched.
You don't need any custom component.
Set your button onTouchListener as:
button.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
switch (motionEvent.getAction())
{
case MotionEvent.ACTION_DOWN:
// popup the listView
break;
case MotionEvent.ACTION_MOVE:
// lstPopup is your ListView
lstPopup.dispatchTouchEvent(motionEvent);
break;
case MotionEvent.ACTION_UP:
// do other stuff
}
return false;
}
});
And set your listView onTouchListener as:
lstPopup.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_MOVE)
{
lstPopup.setSelectionFromTop(0, (int) motionEvent.getY());
}
return false;
}
});
I have a ListView inside ScrollView. I can enable scroll of ListView by
listView.getParent().requestDisallowInterCeptTouchEvent(true);
But the problem is when i scroll up in listView and it reaches top it should scroll to parent view i.e. parent scroll has to work . How can i do this ? any suggestion please.
listView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return true; // Indicates that this has been handled by you and will not be forwarded further.
}
return false;
}
});
OR
To make the View unselectable just get the view and .setClickable(false)
OR
listView.setScrollContainer(false);
You can override ScrollView class and insert these methods inside:
private boolean isScrollEnabled = true;
public void enableScroll(boolean isScrollEnabled ) {
this.isScrollEnabled = isScrollEnabled ;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isScrollEnabled) {
return super.onInterceptTouchEvent(ev);
} else {
return false;
}
}
This is the cleanest solution to achieve this. You only call scrollView.enableScroll(true) to enable scrolling or scrollView.enableScroll(false) to disable it.
I would suggest to embed your upper view i.e any viewgroup above list view into listview header. ListView has a method, listview.addHeaderView(). That way you would be able to scroll your list (Whole View) even on small size display and you don't need scrollview.
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.