10

I am trying to get the profile pic of a user from FB and display it in a circular image on my app.I am able to retrieve and display the display pic in ProfilePictureView widget provided by Facebook SDK. But when I try to get the bitmap of that image and try o put it in a circular image view, I get the default FB display pic which everyone is assigned if you dont have a display pic. I am not able to solve this issue. This is what I do to get the bitmap out of ProfilePictureView and set it to my circular view.

if (session == Session.getActiveSession()) {
                if (user != null) {

                    // Set the id for the ProfilePictureView
                    // view that in turn displays the profile picture.

                    profilePictureView.setDrawingCacheEnabled(true);
                    profilePictureView.setProfileId(user.getId());



                    Bitmap bitmap = profilePictureView.getDrawingCache();


                        Bitmap circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

                        BitmapShader shader = new BitmapShader (bitmap,  TileMode.CLAMP, TileMode.CLAMP);
                        Paint paint = new Paint();
                        paint.setShader(shader);
                        Canvas c = new Canvas(circleBitmap);
                     c.drawCircle(bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, paint);

                        profilePic.setImageBitmap(circleBitmap);
                     //profilePictureView.draw(c);
                     //c.drawCircle(bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, paint);

}

Here profilePic is the ImageView to which I am trying to put circle shape and profilePictureView is the ProfilePictureView id which has the display pic.

Viswanth
  • 151
  • 1
  • 2
  • 8

7 Answers7

36

I took the code from facebook ProfilePictureView and modified it a bit:

Here is the code:

package com.myapp.mypackage.view;

/**
 * Copyright 2010-present Facebook.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.facebook.FacebookException;
import com.facebook.LoggingBehavior;
import com.facebook.android.R;
import com.facebook.internal.ImageDownloader;
import com.facebook.internal.ImageRequest;
import com.facebook.internal.ImageResponse;
import com.facebook.internal.Logger;
import com.facebook.internal.Utility;

import java.net.URISyntaxException;

/**
 * View that displays the profile photo of a supplied profile ID, while conforming
 * to user specified dimensions.
 */
public class ProfilePictureView extends FrameLayout {

    /**
     * Callback interface that will be called when a network or other error is encountered
     * while retrieving profile pictures.
     */
    public interface OnErrorListener {
        /**
         * Called when a network or other error is encountered.
         *
         * @param error a FacebookException representing the error that was encountered.
         */
        void onError(FacebookException error);
    }

    /**
     * Tag used when logging calls are made by ProfilePictureView
     */
    public static final String TAG = ProfilePictureView.class.getSimpleName();

    /**
     * Indicates that the specific size of the View will be set via layout params.
     * ProfilePictureView will default to NORMAL X NORMAL, if the layout params set on
     * this instance do not have a fixed size.
     * Used in calls to setPresetSize() and getPresetSize().
     * Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
     */
    public static final int CUSTOM = -1;

    /**
     * Indicates that the profile image should fit in a SMALL X SMALL space, regardless
     * of whether the cropped or un-cropped version is chosen.
     * Used in calls to setPresetSize() and getPresetSize().
     * Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
     */
    public static final int SMALL = -2;

    /**
     * Indicates that the profile image should fit in a NORMAL X NORMAL space, regardless
     * of whether the cropped or un-cropped version is chosen.
     * Used in calls to setPresetSize() and getPresetSize().
     * Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
     */
    public static final int NORMAL = -3;

    /**
     * Indicates that the profile image should fit in a LARGE X LARGE space, regardless
     * of whether the cropped or un-cropped version is chosen.
     * Used in calls to setPresetSize() and getPresetSize().
     * Corresponds with the preset_size Xml attribute that can be set on ProfilePictureView.
     */
    public static final int LARGE = -4;

    private static final int MIN_SIZE = 1;
    private static final boolean IS_CROPPED_DEFAULT_VALUE = true;
    private static final String SUPER_STATE_KEY = "ProfilePictureView_superState";
    private static final String PROFILE_ID_KEY = "ProfilePictureView_profileId";
    private static final String PRESET_SIZE_KEY = "ProfilePictureView_presetSize";
    private static final String IS_CROPPED_KEY = "ProfilePictureView_isCropped";
    private static final String BITMAP_KEY = "ProfilePictureView_bitmap";
    private static final String BITMAP_WIDTH_KEY = "ProfilePictureView_width";
    private static final String BITMAP_HEIGHT_KEY = "ProfilePictureView_height";
    private static final String PENDING_REFRESH_KEY = "ProfilePictureView_refresh";

    private String profileId;
    private int queryHeight = ImageRequest.UNSPECIFIED_DIMENSION;
    private int queryWidth = ImageRequest.UNSPECIFIED_DIMENSION;
    private boolean isCropped = IS_CROPPED_DEFAULT_VALUE;
    private Bitmap imageContents;
    private ImageView image;
    private int presetSizeType = CUSTOM;
    private ImageRequest lastRequest;
    private OnErrorListener onErrorListener;
    private Bitmap customizedDefaultProfilePicture = null;

    /**
     * Constructor
     *
     * @param context Context for this View
     */
    public ProfilePictureView(Context context) {
        super(context);
        initialize(context);
    }

    /**
     * Constructor
     *
     * @param context Context for this View
     * @param attrs   AttributeSet for this View.
     *                The attribute 'preset_size' is processed here
     */
    public ProfilePictureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize(context);
        parseAttributes(attrs);
    }

    /**
     * Constructor
     *
     * @param context  Context for this View
     * @param attrs    AttributeSet for this View.
     *                 The attribute 'preset_size' is processed here
     * @param defStyle Default style for this View
     */
    public ProfilePictureView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize(context);
        parseAttributes(attrs);
    }

    /**
     * Gets the current preset size type
     *
     * @return The current preset size type, if set; CUSTOM if not
     */
    public final int getPresetSize() {
        return presetSizeType;
    }

    /**
     * Apply a preset size to this profile photo
     *
     * @param sizeType The size type to apply: SMALL, NORMAL or LARGE
     */
    public final void setPresetSize(int sizeType) {
        switch (sizeType) {
            case SMALL:
            case NORMAL:
            case LARGE:
            case CUSTOM:
                this.presetSizeType = sizeType;
                break;

            default:
                throw new IllegalArgumentException("Must use a predefined preset size");
        }

        requestLayout();
    }

    /**
     * Indicates whether the cropped version of the profile photo has been chosen
     *
     * @return True if the cropped version is chosen, false if not.
     */
    public final boolean isCropped() {
        return isCropped;
    }

    /**
     * Sets the profile photo to be the cropped version, or the original version
     *
     * @param showCroppedVersion True to select the cropped version
     *                           False to select the standard version
     */
    public final void setCropped(boolean showCroppedVersion) {
        isCropped = showCroppedVersion;
        // No need to force the refresh since we will catch the change in required dimensions
        refreshImage(false);
    }

    /**
     * Returns the profile Id for the current profile photo
     *
     * @return The profile Id
     */
    public final String getProfileId() {
        return profileId;
    }

    /**
     * Sets the profile Id for this profile photo
     *
     * @param profileId The profileId
     *                  NULL/Empty String will show the blank profile photo
     */
    public final void setProfileId(String profileId) {
        boolean force = false;
        if (Utility.isNullOrEmpty(this.profileId) || !this.profileId.equalsIgnoreCase(profileId)) {
            // Clear out the old profilePicture before requesting for the new one.
            setBlankProfilePicture();
            force = true;
        }

        this.profileId = profileId;
        refreshImage(force);
    }

    /**
     * Returns the current OnErrorListener for this instance of ProfilePictureView
     *
     * @return The OnErrorListener
     */
    public final OnErrorListener getOnErrorListener() {
        return onErrorListener;
    }

    /**
     * Sets an OnErrorListener for this instance of ProfilePictureView to call into when
     * certain exceptions occur.
     *
     * @param onErrorListener The Listener object to set
     */
    public final void setOnErrorListener(OnErrorListener onErrorListener) {
        this.onErrorListener = onErrorListener;
    }

    /**
     * The ProfilePictureView will display the provided image while the specified
     * profile is being loaded, or if the specified profile is not available.
     *
     * @param inputBitmap The bitmap to render until the actual profile is loaded.
     */
    public final void setDefaultProfilePicture(Bitmap inputBitmap) {
        customizedDefaultProfilePicture = inputBitmap;
    }

    /**
     * Overriding onMeasure to handle the case where WRAP_CONTENT might be
     * specified in the layout. Since we don't know the dimensions of the profile
     * photo, we need to handle this case specifically.
     * <p/>
     * The approach is to default to a NORMAL sized amount of space in the case that
     * a preset size is not specified. This logic is applied to both width and height
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        ViewGroup.LayoutParams params = getLayoutParams();
        boolean customMeasure = false;
        int newHeight = MeasureSpec.getSize(heightMeasureSpec);
        int newWidth = MeasureSpec.getSize(widthMeasureSpec);
        if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY &&
                params.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            newHeight = getPresetSizeInPixels(true); // Default to a preset size
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
            customMeasure = true;
        }

        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY &&
                params.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            newWidth = getPresetSizeInPixels(true); // Default to a preset size
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
            customMeasure = true;
        }

        if (customMeasure) {
            // Since we are providing custom dimensions, we need to handle the measure
            // phase from here
            setMeasuredDimension(newWidth, newHeight);
            measureChildren(widthMeasureSpec, heightMeasureSpec);
        } else {
            // Rely on FrameLayout to do the right thing
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    /**
     * In addition to calling super.Layout(), we also attempt to get a new image that
     * is properly size for the layout dimensions
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // See if the image needs redrawing
        refreshImage(false);
    }

    /**
     * Some of the current state is returned as a Bundle to allow quick restoration
     * of the ProfilePictureView object in scenarios like orientation changes.
     *
     * @return a Parcelable containing the current state
     */
    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        Bundle instanceState = new Bundle();
        instanceState.putParcelable(SUPER_STATE_KEY, superState);
        instanceState.putString(PROFILE_ID_KEY, profileId);
        instanceState.putInt(PRESET_SIZE_KEY, presetSizeType);
        instanceState.putBoolean(IS_CROPPED_KEY, isCropped);
        instanceState.putParcelable(BITMAP_KEY, imageContents);
        instanceState.putInt(BITMAP_WIDTH_KEY, queryWidth);
        instanceState.putInt(BITMAP_HEIGHT_KEY, queryHeight);
        instanceState.putBoolean(PENDING_REFRESH_KEY, lastRequest != null);

        return instanceState;
    }

    /**
     * If the passed in state is a Bundle, an attempt is made to restore from it.
     *
     * @param state a Parcelable containing the current state
     */
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state.getClass() != Bundle.class) {
            super.onRestoreInstanceState(state);
        } else {
            Bundle instanceState = (Bundle) state;
            super.onRestoreInstanceState(instanceState.getParcelable(SUPER_STATE_KEY));

            profileId = instanceState.getString(PROFILE_ID_KEY);
            presetSizeType = instanceState.getInt(PRESET_SIZE_KEY);
            isCropped = instanceState.getBoolean(IS_CROPPED_KEY);
            queryWidth = instanceState.getInt(BITMAP_WIDTH_KEY);
            queryHeight = instanceState.getInt(BITMAP_HEIGHT_KEY);

            setImageBitmap((Bitmap) instanceState.getParcelable(BITMAP_KEY));

            if (instanceState.getBoolean(PENDING_REFRESH_KEY)) {
                refreshImage(true);
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        // Null out lastRequest. This way, when the response is returned, we can ascertain
        // that the view is detached and hence should not attempt to update its contents.
        lastRequest = null;
    }

    private void initialize(Context context) {
        // We only want our ImageView in here. Nothing else is permitted
        removeAllViews();

        image = new ImageView(context);

        LayoutParams imageLayout = new LayoutParams(
                LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);

        image.setLayoutParams(imageLayout);

        // We want to prevent up-scaling the image, but still have it fit within
        // the layout bounds as best as possible.
        image.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
        addView(image);
    }

    private void parseAttributes(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.com_facebook_profile_picture_view);
        setPresetSize(a.getInt(R.styleable.com_facebook_profile_picture_view_preset_size, CUSTOM));
        isCropped = a.getBoolean(R.styleable.com_facebook_profile_picture_view_is_cropped, IS_CROPPED_DEFAULT_VALUE);
        a.recycle();
    }

    private void refreshImage(boolean force) {
        boolean changed = updateImageQueryParameters();
        // Note: do not use Utility.isNullOrEmpty here as this will cause the Eclipse
        // Graphical Layout editor to fail in some cases
        if (profileId == null || profileId.length() == 0 ||
                ((queryWidth == ImageRequest.UNSPECIFIED_DIMENSION) &&
                        (queryHeight == ImageRequest.UNSPECIFIED_DIMENSION))) {
            setBlankProfilePicture();
        } else if (changed || force) {
            sendImageRequest(true);
        }
    }

    private void setBlankProfilePicture() {
        if (customizedDefaultProfilePicture == null) {
            int blankImageResource = isCropped() ?
                    R.drawable.com_facebook_profile_picture_blank_square :
                    R.drawable.com_facebook_profile_picture_blank_portrait;
            setImageBitmap(BitmapFactory.decodeResource(getResources(), blankImageResource));
        } else {
            // Update profile image dimensions.
            updateImageQueryParameters();
            // Resize inputBitmap to new dimensions of queryWidth and queryHeight.
            Bitmap scaledBitmap = Bitmap.createScaledBitmap(customizedDefaultProfilePicture, queryWidth, queryHeight, false);
            setImageBitmap(scaledBitmap);
        }
    }

    private void setImageBitmap(Bitmap imageBitmap) {
        if (image != null && imageBitmap != null) {
            imageContents = imageBitmap; // Hold for save-restore cycles
            image.setImageBitmap(ProfilePictureView.getRoundedBitmap(imageBitmap));
        }
    }

    private void sendImageRequest(boolean allowCachedResponse) {
        try {
            ImageRequest.Builder requestBuilder = new ImageRequest.Builder(
                    getContext(),
                    ImageRequest.getProfilePictureUrl(profileId, queryWidth, queryHeight));

            ImageRequest request = requestBuilder.setAllowCachedRedirects(allowCachedResponse)
                    .setCallerTag(this)
                    .setCallback(
                            new ImageRequest.Callback() {
                                @Override
                                public void onCompleted(ImageResponse response) {
                                    processResponse(response);
                                }
                            }
                    )
                    .build();

            // Make sure to cancel the old request before sending the new one to prevent
            // accidental cancellation of the new request. This could happen if the URL and
            // caller tag stayed the same.
            if (lastRequest != null) {
                ImageDownloader.cancelRequest(lastRequest);
            }
            lastRequest = request;

            ImageDownloader.downloadAsync(request);
        } catch (URISyntaxException e) {
            Logger.log(LoggingBehavior.REQUESTS, Log.ERROR, TAG, e.toString());
        }
    }

    private void processResponse(ImageResponse response) {
        // First check if the response is for the right request. We may have:
        // 1. Sent a new request, thus super-ceding this one.
        // 2. Detached this view, in which case the response should be discarded.
        if (response.getRequest() == lastRequest) {
            lastRequest = null;
            Bitmap responseImage = response.getBitmap();
            Exception error = response.getError();
            if (error != null) {
                OnErrorListener listener = onErrorListener;
                if (listener != null) {
                    listener.onError(new FacebookException(
                            "Error in downloading profile picture for profileId: " + getProfileId(), error));
                } else {
                    Logger.log(LoggingBehavior.REQUESTS, Log.ERROR, TAG, error.toString());
                }
            } else if (responseImage != null) {
                setImageBitmap(responseImage);

                if (response.isCachedRedirect()) {
                    sendImageRequest(false);
                }
            }
        }
    }

    private boolean updateImageQueryParameters() {
        int newHeightPx = getHeight();
        int newWidthPx = getWidth();
        if (newWidthPx < MIN_SIZE || newHeightPx < MIN_SIZE) {
            // Not enough space laid out for this View yet. Or something else is awry.
            return false;
        }

        int presetSize = getPresetSizeInPixels(false);
        if (presetSize != ImageRequest.UNSPECIFIED_DIMENSION) {
            newWidthPx = presetSize;
            newHeightPx = presetSize;
        }

        // The cropped version is square
        // If full version is desired, then only one dimension is required.
        if (newWidthPx <= newHeightPx) {
            newHeightPx = isCropped() ? newWidthPx : ImageRequest.UNSPECIFIED_DIMENSION;
        } else {
            newWidthPx = isCropped() ? newHeightPx : ImageRequest.UNSPECIFIED_DIMENSION;
        }

        boolean changed = (newWidthPx != queryWidth) || (newHeightPx != queryHeight);

        queryWidth = newWidthPx;
        queryHeight = newHeightPx;

        return changed;
    }

    private int getPresetSizeInPixels(boolean forcePreset) {
        int dimensionId;
        switch (presetSizeType) {
            case SMALL:
                dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_small;
                break;
            case NORMAL:
                dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_normal;
                break;
            case LARGE:
                dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_large;
                break;
            case CUSTOM:
                if (!forcePreset) {
                    return ImageRequest.UNSPECIFIED_DIMENSION;
                } else {
                    dimensionId = R.dimen.com_facebook_profilepictureview_preset_size_normal;
                    break;
                }
            default:
                return ImageRequest.UNSPECIFIED_DIMENSION;
        }

        return getResources().getDimensionPixelSize(dimensionId);
    }

    public static Bitmap getRoundedBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawOval(rectF, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }
}

At the bottom you can see a new method: getRoundedBitmap

lepe
  • 24,677
  • 9
  • 99
  • 108
Benoit
  • 4,549
  • 3
  • 28
  • 45
  • 1
    Hi Benoit, I tried something like this and added my own method to the ProfilePictureView class of FB SDK. But while trying to access that method from my activity using profilePictureView.getRoundedBitmap(as in your case), I am not able to access this method which is added. Am I doing something wrong here? – Viswanth May 17 '14 at 21:14
  • 1
    wen I try to access this method, I get the error " this method is not defined for the type ProfilePictureView " – Viswanth May 18 '14 at 00:08
  • Actually you do not need to call that method from outside that class. Make sure to use this new custom view in your xml and you are done – Benoit May 18 '14 at 11:50
  • 2
    I dont understand. How can the view just include the new method without ever calling it? This getRoundedBitmap must be passed the parameter bitmap at some point right? – Viswanth May 18 '14 at 16:26
  • It is called in that class look at the code and use Ctrl f – Benoit May 18 '14 at 16:27
  • I am sorry if I am missing something here, but that doesn't seem to work. i still get the square image view. – Viswanth May 18 '14 at 16:40
  • 1
    @Benoit Great answer !!!!Is there any way to change the radius of the circularview ??Please help... – Nevaeh Oct 06 '14 at 18:23
  • 1
    @Benoit why are you not using inheritance? – tread Mar 25 '15 at 07:11
  • 1
    Because as you can see all methods are marked as final. Great job from silly Facebook sdk maker... – Benoit Mar 25 '15 at 07:55
  • 1
    @Tushar Patil you need to copy that class from Facebook sdk or simply copy the class in this answer. Note that this was done with an older version of fb sdk. Maybe they allow to subclass their views now... – Benoit Jun 02 '15 at 10:42
  • the method get rounded bitmap was enough for me, kudos to you – Akhil Jain Sep 22 '15 at 11:03
  • 1
    @NJay can you go into details about how I can use this function? Let's say I put it into my facebook ProfilePictureView, what do I need to pass into the function to actually make the picture circular? If I pass in the correct bitmap, is this all I need to do to make the picture circular? – user1871869 Nov 10 '15 at 08:30
  • Awesome answer, with the new SDK it look more like this now: http://pastebin.com/fQNiU64n – RaccoonDeveloper May 31 '16 at 08:27
7

As explained here: FrameLayout, Your Best UI Friend you can use a FrameLayout to put anything drawable on top of a picture. In particular you can place an image or an SVG path (API level 21 and above) on top of a ProfilePictureView.

For example you can use the following layout:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentTop="true"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true">

    <com.facebook.login.widget.ProfilePictureView
        android:id="@+id/profile_picture"
        android:layout_width="160dp"
        android:layout_height="160dp"></com.facebook.login.widget.ProfilePictureView>

    <ImageView
        android:id="@+id/android"
        android:layout_width="160dp"
        android:layout_height="160dp"
        android:src="@drawable/subtracted_circle"
        android:contentDescription="@null" />

</FrameLayout>

Along with the following SVG path which represents a rectangle with a subtracted circle:

<?xml version="1.0" encoding="utf-8"?>
<!-- res/drawable/subtracted_circle.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="160dp"
    android:width="160dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
    <group android:name="subtracted_circle">
        <path
            android:pathData="M 0,0 0,24 C 0,10.745166 10.745166,0 24,0 L 0,0 z M 24,0 C 37.254834,0 48,10.745166 48,24 L 48,0 24,0 z M 48,24 C 48,37.254834 37.254834,48 24,48 l 24,0 0,-24 z M 24,48 C 10.745166,48 0,37.254834 0,24 l 0,24 24,0 z"
            android:strokeWidth="0"
                android:fillColor="@color/background_floating_material_light"
            android:strokeMiterLimit="4"/>
    </group>
</vector>

And you'll get something like this:

enter image description here

gusridd
  • 864
  • 10
  • 16
3

I spent quite a bit of time investigating the options for displaying a circular Facebook profile image and I think it's easiest to leave the Android SDK aside and just leverage your existing image library's transformations.

Instead of using Facebook's ProfilePictureView, use ImageView. Then, load the image with Picasso, Glide, or whatever library you prefer. Below I'm using Picasso with Picasso-Transformations.

Now:

ImageView profilePic = (ImageView) findViewById(R.id.ivProfilePic);

Picasso.with(this)
     .load("https://graph.facebook.com/v2.2/" + user.getUserId() + "/picture?height=120&type=normal") //extract as User instance method
     .transform(new CropCircleTransformation())
     .resize(120, 120)
     .into(profilePic);

Before:

ProfilePictureView profilePic = (ProfilePictureView)findViewById(R.id.ivProfilePic);
profilePic.setProfileId(user.getUserId());
//custom transformation code
bigtex777
  • 1,150
  • 10
  • 15
2

You can adapt gusridd answer with some PNG file (with transparency in the middle) using FrameLayout approach. Instead of using vector (API +21), drawable/subtracted_circle will be a file at res/drawable/subtracted_circle.png. Even tough you need to generate this file, you can use any shape you want. You also need to use the same color of your background in the picture.

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">

<com.facebook.login.widget.ProfilePictureView
    android:id="@+id/profile_picture"
    android:layout_width="160dp"
    android:layout_height="160dp"></com.facebook.login.widget.ProfilePictureView>

<ImageView
    android:id="@+id/android"
    android:layout_width="160dp"
    android:layout_height="160dp"
    android:src="@drawable/subtracted_circle"
    android:contentDescription="@null" />

 </FrameLayout>

res/drawable/subtracted_circle.png

1

I found a helpful working code here. Hope it helps. http://curious-blog.blogspot.in/2014/05/create-circle-bitmap-in-android.html

activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@android:color/darker_gray"
    tools:context="com.example.circlebitmaptest.MainActivity" >

    <ImageView 
        android:id="@+id/image"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:scaleType="fitXY"/>

    </LinearLayout>

getCircleBitmap function

    private Bitmap getCircleBitmap(Bitmap bitmap) {
    final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
    bitmap.getHeight(), Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(output);

 final int color = Color.RED;
 final Paint paint = new Paint();
 final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
 final RectF rectF = new RectF(rect);

 paint.setAntiAlias(true);
 canvas.drawARGB(0, 0, 0, 0);
 paint.setColor(color);
 canvas.drawOval(rectF, paint);

 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
 canvas.drawBitmap(bitmap, rect, rect, paint);

 bitmap.recycle();

 return output;
}

MainActivity Class

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

 // create bitmap from resource
 Bitmap bm = BitmapFactory.decodeResource(getResources(),
  R.drawable.simple_image);

 // set circle bitmap
 ImageView mImage = (ImageView) findViewById(R.id.image);
 mImage.setImageBitmap(getCircleBitmap(bm));

}
  • I don't understand R.drawable.simple_image being used.. do you already have the profile picture somehow? I was hoping to use the bitmap received from Facebook.. but it seems like then you have to deal with potential NetworkOnMainThreadException problems. – Pat Myron Jan 31 '16 at 04:19
0

My solution to this in PHP was this:

http://pastebin.com/RNthU2Pg

R3verse
  • 71
  • 2
  • 8
-1
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:facebook="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:gravity="center_horizontal"
    android:orientation="horizontal" >
<com.facebook.widget.ProfilePictureView
        android:id="@+id/welcome_profile_pic"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:gravity="center_horizontal"
        facebook:preset_size="small" />
<TextView
        android:id="@+id/welcome_user_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_gravity="center"
        android:textColor="#333"
        android:textSize="18sp" />


<Button 
    android:id="@+id/bHomeScreen"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="Go To Home Screen"
    android:textSize="24sp"
    />
</LinearLayout>
Viswanth
  • 151
  • 1
  • 2
  • 8
  • @Benoit Here is my layout using the ProfilePictureView – Viswanth May 18 '14 at 17:07
  • That's the problem: – Benoit May 18 '14 at 17:37
  • Well I made the change in the library itself and hoped that would suffice. Why wasn't that working? – Viswanth May 18 '14 at 17:39
  • Then I don't know. All I know is that I only do this in my Activity: profilePic.setProfileId(user.getId()); and I see a rounded profile img. Double check the method setImageBitmap in the ProfilePictureView it should call the getRoundedBitmap method – Benoit May 18 '14 at 18:12
  • Yea your solution works perfect. I was wondering wat was wrong wd my approach. Anyways thanks a lot for ur time and help – Viswanth May 18 '14 at 18:14
  • you are welcome. It was annoying for me too when I had to implement that. I couldn't extends Facebook's stuff which I found silly from them... – Benoit May 18 '14 at 18:15
  • 1
    @Benoit Hi I am trying to extract the bitmap of the profile picture now so as to store in Internal memory and use it in further activities. But if I try to extract the bitmap of the ProfilePictureView I get the default pic bitmap and not the bitmap of the actual Profile Picture. Can you help me with this please – Viswanth May 19 '14 at 19:44
  • Maybe it should be a new question – Benoit May 19 '14 at 19:45
  • @Benoit here is the new question. http://stackoverflow.com/questions/23746117/extracting-bitmap-of-facebook-profilepicture – Viswanth May 19 '14 at 19:54