Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
189 views
in Technique[技术] by (71.8m points)

android - How to animate ImageView from center-crop to fill the screen and vice versa (facebook style)?

Background

Facebook app has a nice transition animation between a small image on a post, and an enlarged mode of it that the user can also zoom to it.

As I see it, the animation not only enlarges and moves the imageView according to its previous location and size, but also reveals content instead of stretching the content of the imageView.

This can be seen using the next sketch i've made:

enter image description here

The question

How did they do it? did they really have 2 views animating to reveal the content?

How did they make it so fluid as if it's a single view?

the only tutorial i've seen (link here) of an image that is enlarged to full screen doesn't show well when the thumbnail is set to be center-crop.

Not only that, but it works even on low API of Android.

does anybody know of a library that has a similar ability?


EDIT: I've found a way and posted an answer, but it's based on changing the layoutParams , and i think it's not efficient and recommended.

I've tried using the normal animations and other animation tricks, but for now that's the only thing that worked for me.

If anyone know what to do in order to make it work in a better way, please write it down.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, i've found a possible way to do it. i've made the layoutParams as variables that keep changing using the ObjectAnimator of the nineOldAndroids library. i think it's not the best way to achieve it since it causes a lot of onDraw and onLayout, but if the container has only a few views and doesn't change its size, maybe it's ok.

the assumption is that the imageView that i animate will take the exact needed size in the end, and that (currently) both the thumbnail and the animated imageView have the same container (but it should be easy to change it.

as i've tested, it is also possible to add zoom features by extending the TouchImageView class . you just set the scale type in the beginning to center-crop, and when the animation ends you set it back to matrix, and if you want, you can set the layoutParams to fill the entire container (and set the margin to 0,0).

i also wonder how come the AnimatorSet didn't work for me, so i will show here something that works, hoping someone could tell me what i should do.

here's the code:

MainActivity.java

public class MainActivity extends Activity {
    private static final int IMAGE_RES_ID = R.drawable.test_image_res_id;
    private static final int ANIM_DURATION = 5000;
    private final Handler mHandler = new Handler();
    private ImageView mThumbnailImageView;
    private CustomImageView mFullImageView;
    private Point mFitSizeBitmap;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mFullImageView = (CustomImageView) findViewById(R.id.fullImageView);
        mThumbnailImageView = (ImageView) findViewById(R.id.thumbnailImageView);
        mHandler.postDelayed(new Runnable() {

            @Override
            public void run() {
                prepareAndStartAnimation();
            }

        }, 2000);
    }

    private void prepareAndStartAnimation() {
        final int thumbX = mThumbnailImageView.getLeft(), thumbY = mThumbnailImageView.getTop();
        final int thumbWidth = mThumbnailImageView.getWidth(), thumbHeight = mThumbnailImageView.getHeight();
        final View container = (View) mFullImageView.getParent();
        final int containerWidth = container.getWidth(), containerHeight = container.getHeight();
        final Options bitmapOptions = getBitmapOptions(getResources(), IMAGE_RES_ID);
        mFitSizeBitmap = getFitSize(bitmapOptions.outWidth, bitmapOptions.outHeight, containerWidth, containerHeight);

        mThumbnailImageView.setVisibility(View.GONE);
        mFullImageView.setVisibility(View.VISIBLE);
        mFullImageView.setContentWidth(thumbWidth);
        mFullImageView.setContentHeight(thumbHeight);
        mFullImageView.setContentX(thumbX);
        mFullImageView.setContentY(thumbY);
        runEnterAnimation(containerWidth, containerHeight);
    }

    private Point getFitSize(final int width, final int height, final int containerWidth, final int containerHeight) {
        int resultHeight, resultWidth;
        resultHeight = height * containerWidth / width;
        if (resultHeight <= containerHeight) {
            resultWidth = containerWidth;
        } else {
            resultWidth = width * containerHeight / height;
            resultHeight = containerHeight;
        }
        return new Point(resultWidth, resultHeight);
    }

    public void runEnterAnimation(final int containerWidth, final int containerHeight) {
        final ObjectAnimator widthAnim = ObjectAnimator.ofInt(mFullImageView, "contentWidth", mFitSizeBitmap.x)
                .setDuration(ANIM_DURATION);
        final ObjectAnimator heightAnim = ObjectAnimator.ofInt(mFullImageView, "contentHeight", mFitSizeBitmap.y)
                .setDuration(ANIM_DURATION);
        final ObjectAnimator xAnim = ObjectAnimator.ofInt(mFullImageView, "contentX",
                (containerWidth - mFitSizeBitmap.x) / 2).setDuration(ANIM_DURATION);
        final ObjectAnimator yAnim = ObjectAnimator.ofInt(mFullImageView, "contentY",
                (containerHeight - mFitSizeBitmap.y) / 2).setDuration(ANIM_DURATION);
        widthAnim.start();
        heightAnim.start();
        xAnim.start();
        yAnim.start();
        // TODO check why using AnimatorSet doesn't work here:
        // final com.nineoldandroids.animation.AnimatorSet set = new AnimatorSet();
        // set.playTogether(widthAnim, heightAnim, xAnim, yAnim);
    }

    public static BitmapFactory.Options getBitmapOptions(final Resources res, final int resId) {
        final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
        bitmapOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, bitmapOptions);
        return bitmapOptions;
    }

}

activity_main.xml

<RelativeLayout 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"
    tools:context=".MainActivity" >

    <com.example.facebookstylepictureanimationtest.CustomImageView
        android:id="@+id/fullImageView"
        android:layout_width="0px"
        android:layout_height="0px"
        android:background="#33ff0000"
        android:scaleType="centerCrop"
        android:src="@drawable/test_image_res_id"
        android:visibility="invisible" />

    <ImageView
        android:id="@+id/thumbnailImageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:scaleType="centerCrop"
        android:src="@drawable/test_image_res_id" />

</RelativeLayout>

CustomImageView.java

public class CustomImageView extends ImageView {
    public CustomImageView(final Context context) {
        super(context);
    }

    public CustomImageView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomImageView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setContentHeight(final int contentHeight) {
        final LayoutParams layoutParams = getLayoutParams();
        layoutParams.height = contentHeight;
        setLayoutParams(layoutParams);
    }

    public void setContentWidth(final int contentWidth) {
        final LayoutParams layoutParams = getLayoutParams();
        layoutParams.width = contentWidth;
        setLayoutParams(layoutParams);
    }

    public int getContentHeight() {
        return getLayoutParams().height;
    }

    public int getContentWidth() {
        return getLayoutParams().width;
    }

    public int getContentX() {
        return ((MarginLayoutParams) getLayoutParams()).leftMargin;
    }

    public void setContentX(final int contentX) {
        final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
        layoutParams.leftMargin = contentX;
        setLayoutParams(layoutParams);
    }

    public int getContentY() {
        return ((MarginLayoutParams) getLayoutParams()).topMargin;
    }

    public void setContentY(final int contentY) {
        final MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
        layoutParams.topMargin = contentY;
        setLayoutParams(layoutParams);
    }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...