Davide3i Davide3i - 4 months ago 41
Android Question

onTouchListener on FrameLayout: close it when clicked outside

I'm finding difficult to implement a probably simply feature.
I've a FrameLayout containing a dropdown menu (like the one in WhatsApp, popping up when you click on the Attachment icon).
I want to close it when I click outside of said FrameLayout.

I'm trying to implement it using an onTouch event, with no luck.

FrameLayout

My code:

public class MainActivity extends AppCompatActivity {

private LinearLayout mRevealView;
private FrameLayout mRevealFrame;

...

@Override
protected void onCreate(Bundle savedInstanceState) {

...

mRevealView = (LinearLayout) findViewById(R.id.rows);
mRevealFrame = (FrameLayout) findViewById(R.id.reveal_frame);

...

}

// This method is called when I click on the right menu icon.
public void fuelTypes() {
// Looking for X and Y coordinates.
int cx = (mRevealView.getLeft() + mRevealView.getRight());
int cy = (mRevealView.getTop());

// Looking for radius when icon is tapped for showing layout.
int startRadius = 0;
int endRadius = Math.max(mRevealView.getWidth(), mRevealView.getHeight());

// Performing circular reveal when icon will be tapped.
Animator animator = ViewAnimationUtils.createCircularReveal(mRevealView, cx, cy, startRadius, endRadius);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(400);

// Reverse animation to find radius when icon is tapped again for hiding layout.
// The starting radius will be the radius or the extent to which circular reveal animation is to be shown.
int reverseStartRadius = Math.max(mRevealView.getWidth(), mRevealView.getHeight());

// End - radius will be zero.
int reverseEndRadius = 0;

// Performing circular reveal for reverse animation.
Animator animate = ViewAnimationUtils.createCircularReveal(mRevealView, cx, cy, reverseStartRadius, reverseEndRadius);

if (hidden) {
// To show the layout when the icon is tapped.
mRevealView.setVisibility(View.VISIBLE);
mRevealFrame.setVisibility(View.VISIBLE);
animator.start();
hidden = false;
} else {
mRevealView.setVisibility(View.VISIBLE);
mRevealFrame.setVisibility(View.VISIBLE);

// To hide layout on animation end.
animate.addListener(new AnimatorListenerAdapter() {

@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mRevealView.setVisibility(View.INVISIBLE);
mRevealFrame.setVisibility(View.INVISIBLE);
hidden = true;
}

});

animate.start();
}

mRevealFrame.setOnTouchListener(new FrameLayout.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.d("MOTION_EVENT", "getAction(): " + motionEvent.getAction());

switch (motionEvent.getAction()) {

case MotionEvent.ACTION_UP:
Log.d("REVEAL_FRAME", "motion event: " + "ACTION_UP");
fuelTypes();
return true;

}

return true;
}
});

}


The first
Log.d()
is working, showing that the listener is triggered.
Still, the method is not going inside the
switch
: I've already tried a lot of flags, with no luck.
I've read about
onInterceptTouchEvent
and
onTouchEvent
, but I don't know how to use them instead of the
OnTouchListener
.

Thank you for any help.

Answer

Your layout :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_layout"       // give id here
tools:context="com.myfuel.MainActivity">

<include layout="@layout/activity_main"/>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/reveal_frame"
    android:layout_marginTop="?attr/actionBarSize">

    // EVERYTHING INSIDE THIS WILL BE AS IT IS 

</FrameLayout>

</FrameLayout>

Your Activity

public class MainActivity extends AppCompatActivity {

private LinearLayout mRevealView;
private FrameLayout mRevealFrame;
private FrameLayout mMainLayout;
...

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    mRevealView = (LinearLayout) findViewById(R.id.rows);
    mRevealFrame = (FrameLayout) findViewById(R.id.reveal_frame);
    mMainLayout = (FrameLayout) findViewById(R.id.main_layout);

    mMainLayout.setOnTouchListener(new FrameLayout.OnTouchListener() 
    {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            Log.d("MOTION_EVENT", "getAction(): " + motionEvent.getAction());

                    //Things changed here                    

                    if(!hidden)
                       fuelTypes();


            return false;
        }
    });
    ...

}

As your mRevealFrame does not define the whole FrameLayout of your activity we can not use that for closing mRevealView. See i have defined mMainLayout which covers the WHOLE layout it will work now.

Try it and let me know if there is still a problem.

Comments