Prevent Click-through of Android ImageVIew? - java

I have an ImageView overlay inside of a RelativeLayout and want to prevent any clicks from going through the ImageView to the Buttons etc that are behind it (thus in effect disabling the entire RelativeLayout).
Is the a simpler way of doing this then iterating the RelativeLayout views and setting them to disabled as I currently am doing using this code:
RelativeLayout rlTest = (RelativeLayout ) findViewById(R.id.rlTest);
for (int i = 0; i < rlTest.getChildCount(); i++) {
View view = rlTest.getChildAt(i);
view.setEnabled(true);
}

you can set the image to be
android:clickable="true"

Simply call rlTest.setClickable(false). This will prevent the click to be propagate to the children

There is a much cleaner way
You can use:
android:onClick="preventClicks"
in XML and in your Activity
public void preventClicks(View view) {}
This works with fragments.
Example inside this Activity has multiple fragments overlapping one another, just by adding the XML attribute in the background of your fragment it will still call the Activity.preventClicks and will prevent touches on fragments behind it

The following solution works in the general case:
_container.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// NOTE: This prevents the touches from propagating through the view and incorrectly invoking the button behind it
return true;
}
});
It basically blocks any touches from propagating through the view by marking the touch event as handled.
This works on both UI controls and layout containers (ie: LinearLayout, FrameLayout etc.).
The solution to set "clickable" as false did not work for me for layout containers either in code or in the view XML.

I assume that you are using onClickListeners.
How about using onTouchListener instead of onClickListeners. By doing this you will have a control over how deep down in your hierarchy the touch even can be visible. For example, if you have toch listeners on a relative-layout(RL) and a image-view(IV)(contained in RL), and you assign touchListeners to both. Now if you return true from IV's touch event, the lower down member RL will not receive the touch event. However if you return false from from IV's touch event, the lower down member RL will receive the touch event.
Hope this helps!

Just add these two listeners:
// Set an OnTouchListener to always return true for onTouch events so that a touch
// sequence cannot pass through the item to the item below.
view.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return true;
}
});
// Set an OnHoverListener to always return true for onHover events so that focus cannot
// pass through the item to the item below.
view.setOnHoverListener(new OnHoverListener() {
#Override
public boolean onHover(View v, MotionEvent event) {
v.onHoverEvent(event);
return true;
}
});

You could use databindings and consume the clicks like this:
android:onClick="#{() -> true}"

In C#, I use an empty delegate:
objectName.Click += delegate {};
I haven't encountered any problems with it but it does prevent clicks from filtering through to underlying controls.

you can also se the root click listener to null:
// Do not process clicks on other areas of this fragment
binding.root.setOnClickListener(null)
This works 100%.
It doesnt affect other listeners that are already set on the fragment's views.

Related

Reorder items in recyclerView without long press

I implemented the reorder of items in recyclerView using itemTouchHelper.callback as it seems to be the most popular solution now. However this allows to reorder only after long press on any item and I need to be able to freely move items around any time (I attach item itemTouchHelper for 'edit mode' and detach right after.
What is the easiest way to achieve that?
first of all disable LongPressDragEnabled in itemTouchHelper and then just call startDrag(RecyclerView.ViewHolder) from on touch of your custom handle view ie Imageview or anything like this
viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
mItemTouchHelper.startDrag(viewHolder);
}
return false;
}
});

Custom gesture detection inside CollapsingToolbarLayout

I'm trying to use a custom view that I've written inside a CollapsingToolbarLayout, but it seems that the touch events are not propagating properly to my custom view with gesture detection. The result is that scrolling and interactions with the view are not working as expected or smoothly. My custom view makes heavy use of GestureDetector.SimpleOnGestureListener
Is it possible to embed a custom view which has it's own touch events inside CollapsingToolbarLayout?
After some investigation, I found that the problem was with onTouchEvent, specifically, you need to requestDisallowInterceptTouchEvent for the parent view so that the paraent view does not process the touch event:
public boolean onTouchEvent(MotionEvent event) {
// Do stuff on touch
// prevent parent container from processing ACTION_MOVE events
if(event.getAction() == MotionEvent.ACTION_MOVE) {
getParent().requestDisallowInterceptTouchEvent(true);
} else if(event.getAction() == MotionEvent.ACTION_CANCEL) {
getParent().requestDisallowInterceptTouchEvent(false);
}
// Do some more stuff
return true;
}

onFling and LinearLayouts

My Linear Layout: main_linear_layout contains four Linear Layouts: ll1 ll2 ll3 ll4. Each LinearLayout contains Buttons. I am trying to implement the onFling method on main_linear_layout. I want to be able to swipe anywhere on the Layout of Buttons and call an action.
Currently I am able to swipe anywhere on the screen except for the Layout of Buttons. I tried using the following code to fix the problem:
swipeLayout = (LinearLayout) findViewById(R.id.main_linear_layout);
//swipeLayout is the main Layout that contains 4 other linear layouts
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
gestureScanner.onTouchEvent(event);
return true;
}
});
But I am still not able to swipe on the Layout of Buttons. Does anybody know why this is? Is there something else I need to implement? Am I supposed to implement an OnTouchListener to every Linear Layout inside the main Linear Layout? Or to every Button in each Layout?
And in the xml I added a bunch of code to the main Linear Layout:
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:longClickable="true"
But this did not work either. Can someone please help?
If you have a LinearLayout, and then more layouts inside of that, and then buttons on those as you say you do, then this is functioning as intended.
You're only adding the listener to the outside layout...so of course, it'll only trigger when you slide on it. By sliding on the other layouts, or even the buttons, the slide event doesn't even get to that listener, because it is consumed.
You'd need to add the listener to each element that you want to be checking for.
One way to do this is to create an array of your buttons and do it all at once:
private Button button1;
private Button button2;
....
private Button button10;
...
protected void onCreate(Bundle b) {
super.onCreate(b);
button1 = findViewById(R.id.button1);
...
button10 = findViewById(R.id.button10);
OnClickListener onCL = new OnClickListener . . . //do all of your creation here
Button[] buttons = {button1, button2, . . . , button10};
for(Button b : buttons) {
b.setOnClickListener(onCL);
}
}
Well the reason the ll1,ll2,ll3,ll4 don't pick up on touch events is because the parent linear layout receives the motion event and it doesn't propagate further.
Why do you need a touch event listener on the main linear layout? Do you want the sub layouts to all fling together?
If you want the other layouts to pick up on the touch event return false on the touch listener
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
gestureScanner.onTouchEvent(event);
return false;
}
});
Or you could create 4 GestureDetectors and pass the event to the right one depending on which view was pressed
swipeLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(v.getId()){
case R.id.ll1:
gesture1.ontouchEvent(e);
break;
case R.id.ll2:
gesture1.ontouchEvent(e);
break;
}
//etc
return false;
}
});

2 Simultaneous Android Touches / not triggered

So hi there.
I have a simple Layout with 2 Views in it. Both have an onTouchListener attached.
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
System.out.println("Touching");
return false;
}
});
But when I open the application on my phone and touch the first view and do NOT relase my finger and touch the second view with another finger, the second view wont trigger the touch event. why is this so?
I think in this case both touches are passed to the first view as a multi-touch event. So this is one event but contains (I forgot the details) both touch positions.

Android: HorizontalScrollView inside ScrollView

I have multiple HorizontalScrollViews inside a ScrollView. Horizontal scroll isn't smooth at all. I have to scroll almost perfectly horizontally for scrolling to work. Is there a simple fix to tweak this ??? Thanks!
You can use Recycler view with Staggered layout manager
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL);
RecyclerViewAdapter recyclerViewAdapter = newRecyclerViewAdapter(this);
recyclerView.setAdapter(recyclerViewAdapter); //Don't miss to initialize your adapter
This class creates a ScrollView containing a HorizontalScrollView combined into one class. You can put stuff inside it using the AddChild() method. The dispatchTouchEvent overide keeps the scrolling smooth so you can pan around with a single slide of the finger.
(I recently used this to wrap a programmatically created TextView)
class MultiScrollView extends ScrollView
{
public HorizontalScrollView hscroll;
public MultiScrollView ( Context context )
{
super( context );
}
public void AddChild( View child )
{
hscroll.addView( child );
}
#Override
public boolean dispatchTouchEvent( MotionEvent event )
{
hscroll.dispatchTouchEvent(event);
onTouchEvent(event);
return true;
}
}
If you are using the horizontal scroll view solution from (http://www.dev-smart.com/archives/34) the solution for the cross focus problem between the scroll view and the list view is blocking the focus to the scroll view once you have focus on the list view.
From a technical point of view you should add the following line to the onScroll function inside the HorizontalListView class.
getParent().requestDisallowInterceptTouchEvent(true);
Hope this helps.
I've found the solution and still can't believe that this is what you have to do to make this work normal! Just added blank onClickListener to the each item in the HorizontalScrollView:
item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
After this slide is really smooth, both upwards and downwards.
In general, you shouldn't be using nested ScrollViews in Android at all, the behaviour of scrolling in this way is unnatural too.
You may want to rethink your layout design, is it anything that couldn't be achieved with an expandable list?
While David's answer works, it has a downside. It passes ScrollView's MotionEvent object directly to HorizontalScrollView.onTouchEvent(), so if HorizontalScrollView or its children try to get the event coordinates, they will get the wrong coordinates which based on ScrollView.
My solution:
public class CustomScrollView extends ScrollView{
/*************skip initialization*************/
#Override
public boolean onInterceptTouchEvent(MotionEvent e){
//returning false means ScrollView is not interested at any events,
//so ScrollView's onTouchEvent() won't be called,
//and all of the events will be passed to ScrollView's child
return false;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//manually call ScrollView's onTouchEvent(),
//the vertical scrolling happens there.
onTouchEvent(ev);
//dispatch the event,
//ScrollView's child will have every event.
return super.dispatchTouchEvent(ev);
}
}
Just wrap this CustomScrollView around the HorizontalScrollView in your layout file.

Categories