SpecialCyCi / AndroidResideMenu

The idea of ResideMenu is from Dribbble 1 and 2. It has come true and run in iOS devices. iOS ResideMenu This project is the RefsideMenu Android version. The visual effect is partly referred to iOS version of ResideMenu. And thanks to the authors for the above idea and contribution.
MIT License
2.85k stars 1.1k forks source link

Wrong decorView height on Lolipop devices, after adding ActionBar/Toolbar #38

Open kmalmur opened 9 years ago

kmalmur commented 9 years ago

I did a slight modification to the bottom menu bar. Instead of using custom view, I added an Action Bar. Everything works fine up to Android 4.4.4.

On Lolipop devices, the decorView size is equal to screen size.

The only modifications I made are:

It seems that the menu doesn't calculate the space needed for notification bar and software buttons. For example the following code:

View decorView = getWindow().getDecorView();
ALog.i("Window Size: %d x %d", decorView.getWidth(), decorView.getHeight());

Any ideas how to fix it?

SpecialCyCi commented 9 years ago

Hi, mack. Are you taking about the problem like this? http://stackoverflow.com/questions/27165690/the-getdecorview-method-return-view-include-navigation-bar-view-on-lollipop But I have not found this problem on Lolipop. Could you take some screenshots to describe how it go wrong on Lolipop? image

kmalmur commented 9 years ago

Hi, thanks for the quick response.

Yes, I think the problem seems to be related to the SlidingMenu problems. Here is the issue on GitHub: https://github.com/jfeinstein10/SlidingMenu/issues/680

Below are the screenshots from Genymotion 5.0 (Nexus 4):

As you can see, the problem exists only when the menu is hidden.

I've made a fork: https://github.com/mack369/AndroidResideMenu You can check out the problem there. It works with Android Studio 1.0 and the problem is visible under Genymotion's Nexus4/5.0

SpecialCyCi commented 9 years ago

I am sorry that I still have not found the problem from your fork. https://github.com/mack369/AndroidResideMenu This fork looks like different from your screenshots.

image

kmalmur commented 9 years ago

That's pretty odd. Below is the screen from my Genymotion machine.

Try to use my APK (I've just added it to the fork). We will see if the difference is because the build dependencies or the Genymotion version

If my APK shows the problem at your side, please send me your APK. I will try to decompile it and check out the difference in dependencies.

zrzut ekranu 2014-12-10 16 04 37

SpecialCyCi commented 9 years ago

Emmm.. I installed your APK and the problem comes out this time. This problem may relate to the building SDK version(mine is API 19) or android-support-v7-appcompact. I have pushed a APK to this branch, just decompile it.

image

kmalmur commented 9 years ago

The differences I've noticed are:

As for the appcompat, if you compiled the code from my fork you should have the same version as mine.

I updated my fork so that you should be able to reproduce the problem on your side. Try pure gradle wrapper instead of Android Studio. Just execute:

gradlew build

In the meantime, I've check that one of the methods you are overriding:

boolean fitSystemWindows(Rect insets)

is deprecated since API 20. I think that this method may cause the problems.

SpecialCyCi commented 9 years ago

Hi, mack. I believe this problem is relative to targetSdkVersion. The problem could be found out if I set targetSdkVersion to 21. But I have no idea what causes this now.

image image

SpecialCyCi commented 9 years ago

@mack369 You can fix your problem with this answer.

kmalmur commented 9 years ago

@SpecialCyCi Thanks a lot for your help. Now it works fine. Below is the complete method definition from class ResideMenu:

@Override
protected boolean fitSystemWindows(Rect insets) {
    // Applies the content insets to the view's padding, consuming that content (modifying the insets to be 0), 
    // and returning true. This behavior is off by default and can be enabled through setFitsSystemWindows(boolean)
    // in api14+ devices.

    int bottomPadding = insets.bottom;

    if(Build.VERSION.SDK_INT >= 21){
        Resources resources = getContext().getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            bottomPadding += resources.getDimensionPixelSize(resourceId);
        }
    }

    this.setPadding(viewActivity.getPaddingLeft() + insets.left, viewActivity.getPaddingTop() + insets.top,
            viewActivity.getPaddingRight() + insets.right, viewActivity.getPaddingBottom() + bottomPadding);
    insets.left = insets.top = insets.right = insets.bottom = 0;
    return true;
}
SpecialCyCi commented 9 years ago

Yep, however, it would not work if I don't use the actionbar. So, this answer is not the best solution.

image

kmalmur commented 9 years ago

yes, I see, it is hard to do it more generic. Maybe this workaround?

@Override
protected boolean fitSystemWindows(Rect insets) {
    // Applies the content insets to the view's padding, consuming that content (modifying the insets to be 0), 
    // and returning true. This behavior is off by default and can be enabled through setFitsSystemWindows(boolean)
    // in api14+ devices.

    int bottomPadding = insets.bottom;

    if(Build.VERSION.SDK_INT >= 21 && hasActionBar()){
        Resources resources = getContext().getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            bottomPadding += resources.getDimensionPixelSize(resourceId);
        }
    }

    this.setPadding(viewActivity.getPaddingLeft() + insets.left, viewActivity.getPaddingTop() + insets.top,
            viewActivity.getPaddingRight() + insets.right, viewActivity.getPaddingBottom() + bottomPadding);
    insets.left = insets.top = insets.right = insets.bottom = 0;
    return true;
}

boolean hasActionBar(){
    //check support action bar
    if(activity instanceof ActionBarActivity){
        ActionBarActivity actionBarActivity = (ActionBarActivity) activity;

        return actionBarActivity.getSupportActionBar() != null;
    }

    //check regular action bar
    return activity.getActionBar() != null;
}
GoMino commented 9 years ago

This solution works best as in doesn't need to change the lib source code

prashantwosti commented 9 years ago

Here is the simple workaround: In your styles, add the following line.

 <item name="android:windowDrawsSystemBarBackgrounds" tools:ignore="NewApi">false</item>

Regards,

joxad commented 9 years ago

Thanks for the work around ! I was afraid I can't make it working. I had the problem with some relative layout in alignparentbottom that were going outside of the view but now it is solved :+1:

tomkui32 commented 9 years ago

Hello, I am facing this problem and tried above suggestions but doesn't work. bottomPadding += getNavigationBarHeight(); gives the correct result, but it seems nothing change. do u have any suggestion?

SteveKamau72 commented 8 years ago

I know this may be a crude method but it works for me,but it may help someone who stumbled upon such a problem. Here is how i have handled:

@Override protected boolean fitSystemWindows(Rect insets) { // Applies the content insets to the view's padding, consuming that content (modifying the insets to be 0), // and returning true. This behavior is off by default and can be enabled through setFitsSystemWindows(boolean) // in api14+ devices. boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);

    if (hasBackKey && hasHomeKey) {
        // no navigation bar, unless it is enabled in the settings
        // Applies the content insets to the view's padding, consuming that
        // content (modifying the insets to be 0),
        // and returning true. This behavior is off by default and can be
        // enabled through setFitsSystemWindows(boolean)
        // in api14+ devices.

        // This is added to fix soft navigationBar's overlapping to content above LOLLIPOP
        int bottomPadding = viewActivity.getPaddingBottom() + insets.bottom;

        if (!hasBackKey || !hasHomeKey) {//there's a navigation bar
            bottomPadding += getNavigationBarHeight();
        }

        this.setPadding(viewActivity.getPaddingLeft() + insets.left,
                viewActivity.getPaddingTop() + insets.top,
                viewActivity.getPaddingRight() + insets.right,
                bottomPadding);
        insets.left = insets.top = insets.right = insets.bottom = 0;
    } else {
        // 99% sure there's a navigation bar

        int bottomPadding = insets.bottom;

        if(Build.VERSION.SDK_INT >= 21){
            Resources resources = getContext().getResources();
            int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
            if (resourceId > 0) {
                bottomPadding += resources.getDimensionPixelSize(resourceId);
            }
        }

        this.setPadding(viewActivity.getPaddingLeft() + insets.left, viewActivity.getPaddingTop() + insets.top,
                viewActivity.getPaddingRight() + insets.right, viewActivity.getPaddingBottom() + bottomPadding);
        insets.left = insets.top = insets.right = insets.bottom = 0;
    }

    return true;
}
RinkeshPatel9498 commented 8 years ago

I have comment this method then it's work perfect...

protected boolean fitSystemWindows(Rect insets) { // Applies the content insets to the view's padding, consuming that content (modifying the insets to be 0), // and returning true. This behavior is off by default and can be enabled through setFitsSystemWindows(boolean) // in api14+ devices.

int bottomPadding = insets.bottom;

if(Build.VERSION.SDK_INT >= 21 && hasActionBar()){
    Resources resources = getContext().getResources();
    int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        bottomPadding += resources.getDimensionPixelSize(resourceId);
    }
}

this.setPadding(viewActivity.getPaddingLeft() + insets.left, viewActivity.getPaddingTop() + insets.top,
        viewActivity.getPaddingRight() + insets.right, viewActivity.getPaddingBottom() + bottomPadding);
insets.left = insets.top = insets.right = insets.bottom = 0;
return true;

}

wondersoftwares671 commented 7 years ago

The Simple solution that is working for me at least for now is, Get Navigation bar height and set padding to root layout of activity in OnCreate() after setContentView.


.....
     int navHeight = getNavHeight();
     if (navHeight > 0) {
         (findViewById(R.id.rlMain)).setPadding(0, 0, 0, navHeight);
     }
.....

private int getNavHeight() {
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
         return 0;
     try {
         Resources resources = getResources();
         int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
         if (resourceId > 0) {
             return resources.getDimensionPixelSize(resourceId);
         }
     } catch (Exception ex) {
         return 0;
     }
     return 0;
 }
muzammilpeer commented 6 years ago

This thing works for me. I have created my own CustomizedResideMenu.

public class CustomizedResideMenu extends ResideMenu { public CustomizedResideMenu(Context context) { super(context); }

@Override
protected boolean fitSystemWindows(Rect insets) {
    // Applies the content insets to the view's padding, consuming that content (modifying the insets to be 0),
    // and returning true. This behavior is off by default and can be enabled through setFitsSystemWindows(boolean)
    // in api14+ devices.
    int bottomPadding = insets.bottom;

    if (Build.VERSION.SDK_INT >= 21) {
        Resources resources = getContext().getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            bottomPadding += resources.getDimensionPixelSize(resourceId);
        }
    }
    insets.bottom = bottomPadding;

    super.fitSystemWindows(insets);
    return true;
}

}