Related
I am trying to achieve a scroll effect, I think it can be done because I see some apps implemented this.
I have a FrameLayout, in this layout I have:
- A recycler view
- A float view
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout // float layout here
android:layout_width="match_parent"
android:layout_height="100dp">
</LinearLayout>
</FrameLayout>
When I scroll the recycler view, I can see the float view scroll also, but when it reaches the top of the screen, I want it to stop there. I have successfully implemented this but after that I face to a new issue. Because the float view is above the recycler view, I can not scroll when touch and scroll the float view. In this case the float view seems consumes the touch event so that the recycler does nothing.
What I want to achieve is when user want to scroll the recycler view should consume it.
Im thinking of sending the float view's touch event to recycler view.
Thanks.
I have found same problem some time ago. Here is my solution (it is a little bit hacky, but didn't find better solution). Put in in your custom FrameLayout class:
public class CustomFrameLayout extends FrameLayout {
...
#InjectView(R.id.rv_details)
RecyclerView recyclerView;
#InjectView(R.id.ll_details_action_bar_wrapper)
ViewGroup actionBarWrapperViewGroup;
private List<MotionEvent> cachedEventList = new ArrayList<>();
private boolean touchIsFromActionBar;
private boolean yTranslationThresholdPassed;
// Pawel Janeczek
// Those two overrides is for forwarding touch events, that started on action bar, to recyclerview.
// But you may ask, why there are so many lines? it should by only recyclerView.dispatchTouchEvent(ev) and it should be fine
// It is because RecyclerView when it is starting scrolling it sends parent.requestDisallowInterceptTouchEvent which disables sending onInterceptTouchEvent to parent
// In such case we must set a flag touchIsFromActionBar when motion event starts and is in action bar, and then when this flag is set we remove calling super on requestDisallowInterceptTouchEvent
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN && viewUtils.isWithinViewBounds(actionBarWrapperViewGroup, ev.getRawX(), ev.getRawY())) {
touchIsFromActionBar = true;
}
if (touchIsFromActionBar && shouldDispatchEventToRecyclerView(ev)) {
if (!listUtils.isEmpty(cachedEventList)) {
for (MotionEvent motionEvent : cachedEventList) {
recyclerView.dispatchTouchEvent(motionEvent);
}
cachedEventList.clear();
}
recyclerView.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
cachedEventList.clear();
yTranslationThresholdPassed = false;
touchIsFromActionBar = false;
}
return false;
}
private boolean shouldDispatchEventToRecyclerView(MotionEvent event) {
if (yTranslationThresholdPassed) {
return true;
} else if (listUtils.isEmpty(cachedEventList)) {
cachedEventList.add(MotionEvent.obtain(event));
return false;
}
int yTranslationThreshold = 2;
MotionEvent lastEvent = listUtils.getLast(cachedEventList);
if (Math.abs(lastEvent.getY() - event.getY()) > yTranslationThreshold) {
yTranslationThresholdPassed = true;
return true;
} else {
cachedEventList.add(MotionEvent.obtain(event));
return false;
}
}
#Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (!touchIsFromActionBar) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
...
}
ViewGroup named actionBarWrapperViewGroup is a flow layout in your sample.
And xml for CustomFrameLayout:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RecyclerView
android:id="#+id/rv_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout
android:id="#+id/ll_details_action_bar_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
...
<FrameLayout
android:id="#+id/ll_details_action_bar_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="#dimen/default_bar_height"
android:background="?colorPrimary"/>
...
</LinearLayout>
</merge>
It is live copied from my project, so names can be misleading but I think it is understandable. If you have any questions go on.
I have a one RelativeLayout and this layout is having nearly 10 views in it.
I have set OnTouchListener to this Layout and doing some work in it and returning true.
this listener is working fine when I touch the layout where there are no View (mean on Empty area). If I touch on child views of this layout, this listener is not firing...
and from the documentation, I understood that we can override onInterceptTouchEvent() by extending ViewGroup (so here RelativeLayout) and handle touch events before child views consume this event...
this will do a trick, but I need to modify many xml files where I need this functionality by replacing RelativeLayout with my CustomRelativeLayout.
so my question is:
is there any way to handle touch event for RelativeLayout (ofcourse ViewGroup) before child views in RelativeLayout consumes event? I don't want to extend RelativeLayout...
Try to override
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
of Activity.
This method is the first that process touch events.
But in this case you need to work with current coordinates of views
While not necessarily being ideal, this works well for smaller layouts that won't have dynamic content.
In xml, set android:clickable="false" to all descendants of the ViewGroup in the layout (including nested ViewGroups and their children). Each child that gets clicked on will then propagate the click to its parent, eventually getting to the ViewGroup's default touch handlers. Make sure to set root ViewGroup, where you want to get the click events as android:clickable="true" or viewgroup.setClickable(true)
If you add views dynamically, make sure to call view.setClickable(false); before adding it to the view hierarchy.
Try add onTouchListener to RelativeLayout
RelativeLayout rl = (RelativeLayout) findViewById( R.id.relativeLauout );
rl.setOnTouchListener( new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// do something
return false;
}
});
or use method onTouchEvent from Activity
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return super.onTouchEvent(event);
}
Try to return false in the onTouchEvent() method of child views when processing ACTION_DOWN event. So the forwarding touch events will not be sent to child views, aka event not consumed.
You can simply set in your xml:
android:clickable="true"
MediaController always shown on top of all views in the screen and do not pass clicks to below views until it is hidden, my issue is:
How to add button view on top of MediaController so it handle click events ?
OR
How to pass click events to below view?
Try overriding MediaController.dispatchTouchEvent()
It suits your task better, see my answer here for a detailed explanation why.
The code will go something like this:
public class MyMediaController extends MediaController {
...
private WeakReference<Button> mButton;
// TODO You'll have to call this whenever the layout might have been rebuilt
// i.e. in onCreate() of your activity (or in onConfigurationChanged() if you
// handle configuration changes yourself)
public void setButton(Button button) {
mButton = new WeakReference<Button>(button);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
int iX = (int) event.getX();
int iY = (int) event.getY();
Button button = mButton.get();
if (button != null) {
Rect buttonHitRect = new Rect();
button.getHitRect(buttonHitRect);
if (buttonHitRect != null && buttonHitRect.contains(iX, iY)) {
// user hit the button, dispatch event to the button
button.dispatchTouchEvent(event);
return true;
}
}
// button did not get hit, pass the touch through
return super.dispatchTouchEvent(event)
}
}
Try this, let me know how it goes.
UPD Theres's a very similar question on this, it may have a better solution: Android MediaController intercepts all other touch events
When a view is overlapping another view then the hidden view will not get any touch event as the view on top consumes the touch event. If you want to the hidden view to receive the touch event then you have to manually pass the touch event from the top view to the hidden view.
Here there are two possibilities:
You want the touch event to be shared by both the view: In this case after passing the touch event to the hidden view indicate android that touch event has not been consumed by returning false from the onTouch() method of the top view.
You want the touch event to be handled by only the hidden view: In this case after passing the touch event to the hidden view indicate android that the touch event been consumed by returning true from the onTouch() method of the top view.
Here is a sample code for this:
btn.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch (View v, MotionEvent event)
{
list.onTouchEvent(event);
return true; // return true if touch event is consumed else return false
}
});
XML for this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#drawable/shape" >
<ListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" >
</ListView>
<Button
android:id="#+id/view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#88cc0000"
android:layout_alignParentTop="true"
android:onClick="showMe" />
</RelativeLayout>
Here Button is hiding the list view still the list will scroll as the touch event is passed to the below layout.
I hope this will help. :)
I have some views that I make visible upon a button press. I want them to disappear if I click outside of those views.
How would this be done on Android?
Also, I realize that the "back button" can also assist Android users with this - I might use that as a secondary way to close the views - but some of the tablets aren't even using a 'physical' back button anymore, it has been very de-emphasized.
An easy/stupid way:
Create a dummy empty view (let's say ImageView with no source), make it fill parent
If it is clicked, then do what you want to do.
You need to have the root tag in your XML file to be a RelativeLayout. It will contain two element: your dummy view (set its position to align the Parent Top). The other one is your original view containing the views and the button (this view might be a LinearLayout or whatever you make it. don't forget to set its position to align the Parent Top)
Hope this will help you, Good Luck !
Find the view rectangle, and then detect whether the click event is outside the view.
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect viewRect = new Rect();
mTooltip.getGlobalVisibleRect(viewRect);
if (!viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
setVisibility(View.GONE);
}
return true;
}
If you want to use the touch event other place, try
return super.dispatchTouchEvent(ev);
This is an old question but I thought I'd give an answer that isn't based on onTouch events. As was suggested by RedLeader it's also possible to achieve this using focus events. I had a case where I needed to show and hide a bunch of buttons arranged in a custom popup, ie the buttons were all placed in the same ViewGroup. Some things you need to do to make this work:
The view group that you wish to hide needs to have View.setFocusableInTouchMode(true) set. This can also be set in XML using android:focusableintouchmode.
Your view root, i.e. the root of your entire layout, probably some kind of Linear or Relative Layout, also needs to be able to be focusable as per #1 above
When the view group is shown you call View.requestFocus() to give it focus.
Your view group need to either override View.onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) or implement your own OnFocusChangeListener and use View.setOnFocusChangeListener()
When the user taps outside your view focus is transferred to either the view root (since you set it as focusable in #2) or to another view that inherently is focusable (EditText or similar)
When you detect focus loss using one of the methods in #4 you know that focus has be transferred to something outside your view group and you can hide it.
I guess this solution doesn't work in all scenarios, but it worked in my specific case and it sounds as if it could work for the OP as well.
I've been looking for a way to close my view when touching outside and none of these methods fit my needs really well. I did find a solution and will just post it here in case anyone is interested.
I have a base activity which pretty much all my activities extend. In it I have:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (myViewIsVisible()){
closeMyView();
return true;
}
return super.dispatchTouchEvent(ev);
}
So if my view is visible it will just close, and if not it will behave like a normal touch event. Not sure if it's the best way to do it, but it seems to work for me.
base on Kai Wang answer : i suggest first check visibility of Your view , base on my scenario when user clicked on fab myView become visible and then when user click outside myView disappears
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect viewRect = new Rect();
myView.getGlobalVisibleRect(viewRect);
if (myView.getVisibility() == View.VISIBLE && !viewRect.contains((int) ev.getRawX(), (int) ev.getRawY())) {
goneAnim(myView);
return true;
}
return super.dispatchTouchEvent(ev);
}
I needed the specific ability to not only remove a view when clicking outside it, but also allow the click to pass through to the activity normally. For example, I have a separate layout, notification_bar.xml, that I need to dynamically inflate and add to whatever the current activity is when needed.
If I create an overlay view the size of the screen to receive any clicks outside of the notification_bar view and remove both these views on a click, the parent view (the main view of the activity) has still not received any clicks, which means, when the notification_bar is visible, it takes two clicks to click a button (one to dismiss the notification_bar view, and one to click the button).
To solve this, you can just create your own DismissViewGroup that extends ViewGroup and overrides the following method:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ViewParent parent = getParent();
if(parent != null && parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(this);
}
return super.onInterceptTouchEvent(ev);
}
And then your dynamically added view will look a little like:
<com.example.DismissViewGroup android:id="#+id/touch_interceptor_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent" ...
<LinearLayout android:id="#+id/notification_bar_view" ...
This will allow you to interact with the view, and the moment you click outside the view, you both dismiss the view and interact normally with the activity.
Implement onTouchListener(). Check that the coordinates of the touch are outside of the coordinates of your view.
There is probably some kind of way to do it with onFocus(), etc. - But I don't know it.
Step 1: Make a wrapper view by Fragmelayout which will cover your main layout.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is your main layout-->
</RelativeLayout>
<View
android:id="#+id/v_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is the wrapper layout-->
</View>
</FrameLayout>
Step 2: Now add logic in your java code like that -
View viewOverlay = findViewById(R.id.v_overlay);
View childView = findViewByID(R.id.childView);
Button button = findViewByID(R.id.button);
viewOverlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
childView.setVisibility(View.GONE);
view.setVisibility(View.GONE);
}
});
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
childView.setVisibility(View.VISIBLE);
// Make the wrapper view visible now after making the child view visible for handling the
// main visibility task.
viewOverlay.setVisibility(View.VISIBLE);
}
});
To hide the view when click performs outside the view:
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (isMenuVisible) {
if (!isWithinViewBounds(ev.rawX.toInt(), ev.rawY.toInt())) {
hideYourView()
return true
}
}
return super.dispatchTouchEvent(ev)
}
create a method to get the bounds(height & width) of your view, so when you click outside of your view it will hide the view and when click on the view will not hide:
private fun isWithinViewBounds(xPoint: Int, yPoint: Int): Boolean {
val l = IntArray(2)
llYourView.getLocationOnScreen(l)
val x = l[0]
val y = l[1]
val w: Int = llYourView.width
val h: Int = llYourView.height
return !(xPoint < x || xPoint > x + w || yPoint < y || yPoint > y + h)
}
I've created custom ViewGroup to display info box anchored to another view (popup balloon).
Child view is actual info box, BalloonView is fullscreen for absolute positioning of child, and intercepting touch.
public BalloonView(View anchor, View child) {
super(anchor.getContext());
//calculate popup position relative to anchor and do stuff
init(...);
//receive child via constructor, or inflate/create default one
this.child = child;
//this.child = inflate(...);
//this.child = new SomeView(anchor.getContext());
addView(child);
//this way I don't need to create intermediate ViewGroup to hold my View
//but it is fullscreen (good for dialogs and absolute positioning)
//if you need relative positioning, see #iturki answer above
((ViewGroup) anchor.getRootView()).addView(this);
}
private void dismiss() {
((ViewGroup) getParent()).removeView(this);
}
Handle clicks inside child:
child.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//write your code here to handle clicks inside
}
});
To dismiss my View by click outside WITHOUT delegating touch to underlying View:
BalloonView.this.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
To dismiss my View by click outside WITH delegating touch to underlying View:
#Override
public boolean onTouchEvent(MotionEvent event) {
dismiss();
return false; //allows underlying View to handle touch
}
To dismiss on Back button pressed:
//do this in constructor to be able to intercept key
setFocusableInTouchMode(true);
requestFocus();
#Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
dismiss();
return true;
}
return super.onKeyPreIme(keyCode, event);
}
I want to share my solution which I think it could be useful if :
you are able to add a custom ViewGroup as root layout
also the view which you want to disappear can be a custom one.
First, we create a custom ViewGroup to intercept touch events:
class OutsideTouchDispatcherLayout #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val rect = Rect()
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (ev.action == MotionEvent.ACTION_DOWN) {
val x = ev.x.roundToInt()
val y = ev.y.roundToInt()
traverse { view ->
if (view is OutsideTouchInterceptor) {
view.getGlobalVisibleRect(rect)
val isOutside = rect.contains(x, y).not()
if (isOutside) {
view.interceptOutsideTouch(ev)
}
}
}
}
return false
}
interface OutsideTouchInterceptor {
fun interceptOutsideTouch(ev: MotionEvent)
}
}
fun ViewGroup.traverse(process: (View) -> Unit) {
for (i in 0 until childCount) {
val child = getChildAt(i)
process(child)
if (child is ViewGroup) {
child.traverse(process)
}
}
}
As you see, OutsideTouchDispatcherLayout intercepts touch events and informs each descendent view which implenets OutsideTouchInterceptor that some touch event occured outside of that view.
Here is how the descendent view could handle this event. Notice that it must implement OutsideTouchInterceptor interface:
class OutsideTouchInterceptorView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr),
OutsideTouchDispatcherLayout.OutsideTouchInterceptor {
override fun interceptOutsideTouch(ev: MotionEvent) {
visibility = GONE
}
}
Then you have outside touch detection easily just by a child-parent relation:
<?xml version="1.0" encoding="utf-8"?>
<com.example.touchinterceptor.OutsideTouchDispatcherLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.touchinterceptor.OutsideTouchInterceptorView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#eee"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.example.touchinterceptor.OutsideTouchDispatcherLayout>
Here's a simple approach to get your work done:
Step 1: Create an ID for the outside container of your element for which you want to generate a click outside event.
In my case, it is a Linear Layout for which I've given id as 'outsideContainer'
Step 2: Set an onTouchListener for that outside container which will simply act as a click outside event for your inner elements!
outsideContainer.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// perform your intended action for click outside here
Toast.makeText(YourActivity.this, "Clicked outside!", Toast.LENGTH_SHORT).show();
return false;
}
}
);
Wrapper layout that notifies us when a click occurred outside a given view:
class OutsideClickConstraintLayout(context: Context, attrs: AttributeSet?) :
ConstraintLayout(context, attrs) {
private var viewOutsideClickListenerMap = mutableMapOf<View, () -> Unit>()
fun setOnOutsideClickListenerForView(view: View, listener: () -> Unit) {
viewOutsideClickListenerMap[view] = listener
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
viewOutsideClickListenerMap.forEach { (view, function) ->
if (isMotionEventOutsideView(view, ev)) function.invoke()
}
return super.onInterceptTouchEvent(ev)
}
private fun isMotionEventOutsideView(view: View, motionEvent: MotionEvent): Boolean {
val viewRectangle = Rect()
view.getGlobalVisibleRect(viewRectangle)
return !viewRectangle.contains(motionEvent.rawX.toInt(), motionEvent.rawY.toInt())
}
}
Usage:
....
outsideClickContainerView.setOnOutsideClickListenerForView(someView) {
// handle click outside someView
}
....
thank #ituki for idea
FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:clickable="true">
<LinearLayout
android:clickable="true" // not trigger
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#FFF"
android:orientation="vertical"
android:padding="20dp">
...............
</LinearLayout>
</FrameLayout>
and java code
mContainer = (View) view.findViewById(R.id.search_container);
mContainer.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
Log.d("aaaaa", "outsite");
return true;
}
return false;
}
});
it's work when touch outside LinearLayout
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.