Disable / Enable Scroll for ListView in android - android

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.

Related

Android ListView how to get click not on item

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;
}
});

Android touch event stucked in ListView

I have a container View that should process touches. It has Listview with overrided onTouch(), which returns false (it will return true, when container is expanded by swipe). When Listview touch occurs it passes to container View and everything is ok. But I need to add click handling for items of Listview.
When I set onClickListener for item, I see in logs it is no MotionEvent passed to container, it is stucked in ListView onTouch() (which return false and does nothing). I'm trying to set onTouchListener for item like this:
viewHolder.itemLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_UP:
Log.d(TAG, "something like click detected");
return true;
}
return false;
}
});
But result is the same. Can someone explain why my container does not receive touch event even ListView returns false for its overrided onTouch()?
If you wish to handle the clicks of ListView items : use OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//parent.getItemAtPosition(position) returns the value of item clicked.. use it to do whatever you wish to do
}
});
I had the same problem about 2 weeks ago. Returning false from listview's onTouch method did not pass event to parent container. I ended up doing this:
1. You need custom layout for your container View. Then in that view, override onInterceptTouchEvent, for example like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
previousX_actionDOWN = ev.getRawX();
isOnClick = true;
timePressed = System.currentTimeMillis();
return false;
case MotionEvent.ACTION_MOVE:
if (isOnClick && (Math.abs(previousX_actionDOWN - ev.getRawX()) > SCROLL_THRESHOLD)) {
return true;
} else {
return false;
}
case MotionEvent.ACTION_UP:
if (!wasDragged) {
return false;
}
else {
return true;
}
}
return false;
}
I also have complicated onTouch method in that view but it's not worth pasting. Point is that your parent view should only intercept touch event if some threshold was passed while ACTION_MOVE - well at least that's what I wanted to achieve. Then in your listview you just implement standard onItemClickListener instead of onTouch.

Touch to Stop scrolling on listview does not work

Suppose that I have 100 item in my listView. When I scroll up and down, and I try to touch to stop the scrolling. But it's not work. It scroll until it reach the item of the first scroll touch.
Is it the phone problem or my app missing some implementing ?
public class OnTouchListner implements OnTouchListener{
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
{
businessResultListView.smoothScrollBy(0, 0);
return true;
}
return false;
}
}
and call it:
businessResultListView.setOnTouchListener(new OnTouchListner());

Let parent View assume MotionEvents if child returns false

I have a application that need event handling on a unusual way.
For my question, let me first explain a simple case that the current event handling system of Android don't fits for me.
Supposing that I have a FrameLayout (that I'll call ViewSwiper since now) that all Views added on it are MATCH_PARENT X MATCH_PARENT (that I'll call PageView), it's handles events by translating the actual View and replace it based on the direction of moving.
This component I already have done and work properly ( Using Animation to swipe views ).
But the problem is on that PageView I add on top of it, in case of ImageViews that return false on it's onTouchEvent, the ViewSwiper will handle the events and let another PageView enter the screen, but if I add a ScrollView on that, all the events will be consumed by the Scroll and the ViewSwiper will not have chance to replace the PageView.
So I figured out that returning false onTouchEvent of the ScrollView the parent can assume it's events, I wrote this sub-class of ScrollView to test it:
public class ScrollViewVertical extends ScrollView {
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
}
public boolean onTouchEvent(MotionEvent evt) {
super.onTouchEvent(evt);
return false;
}
}
But returning false make any further events to get dispatched to the parent, but I need these events for VERTICAL scrolling, so I have the idea to return falses only if the user are moving HORIZONTAL, that's what my code looks like:
public class ScrollViewVertical extends ScrollView {
private MovementTracker moveTracker;
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
moveTracker = new MovementTracker();
}
public boolean onTouchEvent(MotionEvent evt) {
if (moveTracker.track(evt))
if (moveTracker.getDirection() == Direction.HORIZONTAL)
return false;
return super.onTouchEvent(evt);
}
}
PS: MovementTracker will returns true on track() after some events and tell on which direction the user is moving.
But in that case, the ScrollView keep receiving events since it's returns true on the first events.
Any ideas on how can I handle the events on ViewSwiper when it's child returns false (even if some trues are returned).
PS: I can give more info about this if needed, and accept different solutions also.
Based on answers I tried the following:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return intercept;
}
public boolean onTouchEvent(MotionEvent evt) {
boolean x = super.onTouchEvent(evt);
if (moveTracker.track(evt)) {
intercept = moveTracker.getDirection() != Direction.VERTICAL;
if (!intercept)
getParent().requestDisallowInterceptTouchEvent(false);
}
return x;
}
Still nothing.
try this in onTouchEvent() of the scrollview
//if (evt.getAction() == MotionEvent.ACTION_MOVE) {
if (moveTracker.track(evt)){
if (moveTracker.getDirection() == Direction.VERTICAL){
//Or the direction you want the scrollview keep moving
getParent().requestDisallowInterceptTouchEvent(true);
}
}
return true;
Update
Please try the following to the custom Scrollview
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return false;
}
And nothing else
This way i assume the MotionEvent will perform on both views. And since they don't conflict (One is vertical the other one is Horizontal) this could work
Based on the answer from weakwire, I came to the following solution:
On ViewSwiper
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!super.dispatchTouchEvent(ev))
onTouchEvent(ev);
return true;
}
And on ScrollHorizontal I return false on dispatchTouchEvent when I don't need then anymore.

Disable the touch events for all the views

What's the best way to disable the touch events for all the views?
Here is a function for disabling all child views of some view group:
/**
* Enables/Disables all child views in a view group.
*
* #param viewGroup the view group
* #param enabled <code>true</code> to enable, <code>false</code> to disable
* the views.
*/
public static void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
}
}
}
Override the dispatchTouchEvent method of the activity and like this:
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
return true;//consume
}
If you return true all touch events are disabled.
Return false to let them work normally
You could try:
your_view.setEnabled(false);
Which should disable the touch events.
alternatively you can try (thanks to Ercan):
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
return true;//consume
}
or
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
This piece of code will basically propagate this event to the parent view, allowing the touch event, if and only if the inProgress variable is set to false.
private boolean inProgress = false;
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!inProgress)
return super.dispatchTouchEvent(ev);
return true;
}
Use this. returning true will indicate that the listener has consumed the event and android doesn't need to do anything.
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
The easiest way to do this is
private fun setInteractionDisabled(disabled : Boolean) {
if (disabled) {
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
}
What about covering a transparent view over all of your views and capturing all touch event?
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
In Kotlin:
fun View.setEnabledRecursively(enabled: Boolean) {
isEnabled = enabled
if (this is ViewGroup)
(0 until childCount).map(::getChildAt).forEach { it.setEnabledRecursively(enabled) }
}
// usage
import setEnabledRecursively
myView.setEnabledRecursively(false)
I made this method, which works perfect for me. It disables all touch events for selected view.
public static void disableView(View v) {
v.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
disableView(child);
}
}
}
It may not be possible for the whole application. You will have to override onTouchEvent() for each view and ignore the user inputs.
Per your comment:
i just want to be able to disable the views of the current activity at some point
you seem to want to disable all touch for the current activity regardless of the view touched.
Returning true from an override of Activity.dispatchTouchEvent(MotionEvent) at the appropriate times will consume the touch and effectively accomplish this. This method is the very first in a chain of touch method calls.
In case you want to disable all the views in a specific layout, one solution is adding a cover ( a front view that fills up the whole layout ) to consume all the touch events, so that no events would be dispatched to other views in that layout.
Specifically, you first need to add a view to the layout in xml file ( note that it should be placed after all the other views ), like
<FrameLayout>
... // other views
<View
android:id="#+id/vCover"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
then, remember to set click listener to that view in your code so that it will consume touch events, like
override fun onCreate(savedInstanceState: Bundle?) {
viewBinding.vCover.setOnClickListener {}
}
That's all you need.
At the point you want to enable all the view, just gone the cover.
This worked for me, I created an empty method and called it doNothing.
public void doNothing(View view)
{
}
Then called this method from onClick event on all the objects I wanted to disable touch event. android:onClick="doNothing"
When the click or touch event is fired nothing is processed.
One more easier way could be disabling it through layout (i.e. .xml) file:
Just add
android:shouldDisableView="True"
for the view you want disable touch events.

Categories

Resources