37

The new Facebook SDK for Android (v4.0) that was released recently caused strange behavior for a customized LoginButton I'm using. Below is a comparison of how the same XML is rendered in different SDK versions.

The problem seems to be that the FB icon in SDK 4.x doesn't stretch properly to fit a custom-sized button, and at 4.0.1 the android:layout_height property is ignored altogether.

My question is how do I make the button appear in SDK 4.x like it did in SDK 3.x? Both XML and Java solutions are perfectly acceptable.

XML for SDK 3.x:

<com.facebook.widget.LoginButton
        android:background="@color/com_facebook_blue"
        android:id="@+id/login_btn_facebook"
        android:layout_width="225dp"
        android:layout_height="50dp"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:layout_gravity="center"
        android:onClick="onFacebookLoginClick"
            />

How it looks on SDK 3.x (screenshot taken on a OnePlus One, running CM11S): XML rendering when running with SDK v3.x

XML for SDK 4.x (the button's package was renamed + I had to change the width & font a bit to match the g+ button):

<com.facebook.login.widget.LoginButton
        android:background="@color/com_facebook_blue"
        android:id="@+id/login_btn_facebook"
        android:layout_width="221dp"
        android:layout_height="50dp"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:layout_gravity="center"
        android:textSize="7pt"
        android:onClick="onFacebookLoginClick"
            />

How it looks on SDK 4.0 (Screenshot taken on a Genymotion Nexus 5, running unmodified 4.4.4): enter image description here

How it looks on SDK 4.0.1 (Same Genymotion Nexus 5): enter image description here

Additional information

  1. Excerpt from the 4.0 -> 4.0.1 SDK change log:

Login button is updated to properly measure its size.

  1. Related posts:

  2. To support different screen sizes, above the login buttons I have a ViewPagerIndicator and a ViewPager that is configured to take up all available vertical space which remains after positioning elements with defined height.

Community
  • 1
  • 1
Dev-iL
  • 23,742
  • 7
  • 57
  • 99

6 Answers6

62

I managed to get the desired result by following the following steps:

  1. Opened the Facebook SDK 3.x LoginButton code and saw how the button was styled there:

    this.setBackgroundResource(R.drawable.com_facebook_button_blue);
    this.setCompoundDrawablesWithIntrinsicBounds(
                 R.drawable.com_facebook_inverse_icon, 0, 0, 0);
    this.setCompoundDrawablePadding(getResources().getDimensionPixelSize(
                 R.dimen.com_facebook_loginview_compound_drawable_padding));
    this.setPadding(getResources().getDimensionPixelSize(
                    R.dimen.com_facebook_loginview_padding_left),
                getResources().getDimensionPixelSize(
                    R.dimen.com_facebook_loginview_padding_top),
                getResources().getDimensionPixelSize(
                    R.dimen.com_facebook_loginview_padding_right),
                getResources().getDimensionPixelSize(
                    R.dimen.com_facebook_loginview_padding_bottom));
    
  2. Based on the solution presented in this answer, I changed the button parameters during onPostCreate() as follows:

    float fbIconScale = 1.45F;
    Drawable drawable = hostActivity.getResources().getDrawable(
                                   com.facebook.R.drawable.com_facebook_button_icon);
    drawable.setBounds(0, 0, (int)(drawable.getIntrinsicWidth()*fbIconScale),
                             (int)(drawable.getIntrinsicHeight()*fbIconScale));
    authButton.setCompoundDrawables(drawable, null, null, null); 
    authButton.setCompoundDrawablePadding(hostActivity.getResources().
                      getDimensionPixelSize(R.dimen.fb_margin_override_textpadding));
    authButton.setPadding(
            hostActivity.getResources().getDimensionPixelSize(
                                                      R.dimen.fb_margin_override_lr),
            hostActivity.getResources().getDimensionPixelSize(
                                                     R.dimen.fb_margin_override_top),
            hostActivity.getResources().getDimensionPixelSize(
                                                      R.dimen.fb_margin_override_lr),
            hostActivity.getResources().getDimensionPixelSize(
                                                 R.dimen.fb_margin_override_bottom));
    

    Where my custom dimensions are as follows:

    <dimen name="fb_margin_override_top">13dp</dimen>
    <dimen name="fb_margin_override_bottom">13dp</dimen>
    <!--The next value changes the margin between the FB icon and the left border:-->
    <dimen name="fb_margin_override_lr">10dp</dimen>
    <!--The next value changes the margin between the FB icon and the login text:-->
    <dimen name="fb_margin_override_textpadding">17dp</dimen>
    

This results in the desired layout:

The desired layout :D

Community
  • 1
  • 1
Dev-iL
  • 23,742
  • 7
  • 57
  • 99
12

Height of LoginButton is related to his paddings and text size:

//LoginButton.java

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int height = (getCompoundPaddingTop() +
            (int)Math.ceil(Math.abs(fontMetrics.top) + Math.abs(fontMetrics.bottom)) +
            getCompoundPaddingBottom());
    //...
}

So if you want to change its height via xml file use android:padding* and android:textSize properties or create style for it:

<style name="FacebookLoginButtonStyle">
    <item name="android:textSize">16sp</item>
    <item name="android:paddingTop">10sp</item>
    <item name="android:paddingBottom">10sp</item>
</style>
froger_mcs
  • 13,972
  • 5
  • 23
  • 34
  • 1
    Changing the padding in XML is definitely a good tip, however this answer is incomplete - it doesn't address the FB icon scale (which remains tiny), nor does it facilitate the shifting of the text within the button (to be able to align it with the g+ text below). – Dev-iL Apr 27 '15 at 19:17
4

i faced the same issue and i solved it by setting the padding and the drawables in java code like this:

authButton.setPadding(0, myTopDp, 0, myBottomDp);
    authButton.setCompoundDrawablePadding(hostActivity.getResources().getDimensionPixelSize(R.dimen.fb_margin_override_textpadding));
    authButton.setCompoundDrawablesWithIntrinsicBounds(myFbResource, 0, 0, 0);

or if you use your image as drawable

authButton.setCompoundDrawablesWithIntrinsicBounds(myFbDrawable, null, null, null);

I believe the OnPostCreate method is not required.

Bronx
  • 4,480
  • 4
  • 34
  • 44
  • Hi, and thanks for the answer. I just tested this code and the drawable doesn't seem to scale + it is also stuck to the left border of the button. Oh, and my IDE seems to like `authButton.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);` better (`0` instead of `null` generates a lint error; tested on API 22). – Dev-iL Apr 27 '15 at 19:28
  • Hi, the method setCompoundDrawablesWithIntrinsicBounds accepts resource ids or drawables, in my example i was referring to resource ids (maybe i have to update my answer). To scale the drawable you have to set the padding as you do in your answer. i will update my answer – Bronx Apr 28 '15 at 07:54
2

Because I wanted to customize the login text gravity start, I met a problem with the setCompoundDrawablePadding method. Finally I solved it by using a custom layout. I think it's a much easier way to customize the Facebook login button.

Here's the end result:

Facebook login button and Google sign in button

The layout xml:

<LinearLayout
        android:id="@+id/fb_login_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:background="@drawable/com_facebook_button_background"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="38dp"
            android:layout_height="38dp"
            android:layout_gravity="center_vertical"
            android:layout_marginBottom="1dp"
            android:layout_marginLeft="1dp"
            android:layout_marginStart="1dp"
            android:layout_marginTop="1dp"
            android:padding="8dp"
            android:src="@drawable/com_facebook_button_icon" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:padding="8dp"
            android:text="Log in with Facebook"
            android:textColor="@color/white"
            android:textSize="14sp"
            android:textStyle="bold" />
    </LinearLayout>

Java code that handles the custom Facebook login button clicks:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    findViewById(R.id.fb_login_btn).setOnClickListener(this);
    boolean login = AccessToken.getCurrentAccessToken() != null;
    updateFacebookLogInButton(login);

    new AccessTokenTracker() {
        @Override
        protected void onCurrentAccessTokenChanged(
                AccessToken oldAccessToken,
                AccessToken currentAccessToken) {
            if (currentAccessToken == null) {
                updateFacebookLogInButton(false);
            }
        }
    };

    FacebookCallback<LoginResult> facebookCallback = new FacebookCallback<LoginResult>() {
        @Override
        public void onSuccess(LoginResult loginResult) {
            updateFacebookLogInButton(true);
            handleLoginResult(loginResult.getAccessToken());
        }

        @Override
        public void onCancel() {

        }

        @Override
        public void onError(FacebookException error) {
            error.printStackTrace();
        }
    };
    callbackManager = CallbackManager.Factory.create();

    loginManager = LoginManager.getInstance();
    loginManager.registerCallback(callbackManager, facebookCallback);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    callbackManager.onActivityResult(requestCode, resultCode, data);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.fb_login_btn:
            if (AccessToken.getCurrentAccessToken() == null) {
                loginManager.logInWithReadPermissions(this, Arrays.asList("public_profile", "email"));
            } else {
                String logout = getResources().getString(
                        com.facebook.R.string.com_facebook_loginview_log_out_action);
                String cancel = getResources().getString(
                        com.facebook.R.string.com_facebook_loginview_cancel_action);
                String message;
                Profile profile = Profile.getCurrentProfile();
                if (profile != null && profile.getName() != null) {
                    message = String.format(
                            getResources().getString(
                                    com.facebook.R.string.com_facebook_loginview_logged_in_as),
                            profile.getName());
                } else {
                    message = getResources().getString(
                            com.facebook.R.string.com_facebook_loginview_logged_in_using_facebook);
                }
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(message)
                        .setCancelable(true)
                        .setPositiveButton(logout, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                loginManager.logOut();
                            }
                        })
                        .setNegativeButton(cancel, null);
                builder.create().show();
            }
            break;
        default:
            break;
    }
}

private Bundle getRequestParameters() {
    Bundle parameters = new Bundle();
    parameters.putString("fields", "id,name,email,gender");
    return parameters;
}

private void updateFacebookLogInButton(boolean login) {
    TextView loginTextView = (TextView) findViewById(R.id.fb_login_tv);
    if (login) {
        loginTextView.setText("Log out");
    } else {
        loginTextView.setText("Log in with Facebook");
    }
}

private void handleRequestResult(JSONObject object) {
    // handle GraphRequest here
}
Dev-iL
  • 23,742
  • 7
  • 57
  • 99
atarsdnh
  • 21
  • 4
  • This answer is currently incomplete because the resulting custom layout does not have the same functionality as the original FB login button (examples: the `onClick` behavior which interfaces with the SDK and gives you login and logout capabilities; and automatic changes of the button text based on the login state). If you want to improve the answer, I suggest you also provide Java code to "get back" the lost functionality. – Dev-iL Jul 02 '16 at 14:03
  • Thanks @Dev-iL. I improved the answer. – atarsdnh Jul 03 '16 at 08:25
  • Thanks. Customize Facebook by custom layout + LoginManager , I think it's the best solution. – Alexei Jul 26 '18 at 13:10
-1

Just put the button into a RelativeLayout. My Height is 60dp

 <RelativeLayout
    android:id="@+id/rlGoogle"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_marginTop="30dp"
    app:layout_constraintTop_toBottomOf="@+id/btnSingInWithFace">

    <com.google.android.gms.common.SignInButton
        android:id="@+id/btnSingInWithGoogle"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
-3

People, u need only set paddingTop and paddingBottom values.

            android:paddingTop="15dp"
            android:paddingBottom="15dp"