24

バックグラウンド

Facebook アプリには、投稿の小さな画像と、ユーザーがズームできる拡大モードとの間の優れた遷移アニメーションがあります。

私が見たように、アニメーションは imageView を以前の位置とサイズに従って拡大および移動するだけでなく、imageView のコンテンツを引き伸ばすのではなく、コンテンツを表示します。

これは、私が作成した次のスケッチを使用して確認できます。

ここに画像の説明を入力

質問

彼らはどのようにそれをしましたか?コンテンツを明らかにするためにアニメーション化された 2 つのビューが本当にありましたか?

単一のビューであるかのように、どのようにして流動的になったのでしょうか?

サムネイルがセンタークロップに設定されている場合、フルスクリーンに拡大された画像の私が見た唯一のチュートリアル(ここにリンク)はうまく表示されません。

それだけでなく、Androidの低APIでも動作します。

同様の機能を持つライブラリを知っている人はいますか?


編集:私は方法を見つけて回答を投稿しましたが、それは layoutParams の変更に基づいており、効率的ではなく、推奨されていないと思います。

通常のアニメーションやその他のアニメーションのトリックを使用してみましたが、今のところそれが唯一うまくいきました。

より良い方法で機能させるために何をすべきかを誰かが知っている場合は、それを書き留めてください.

4

7 に答える 7

4

わかりました、私はそれを行う可能な方法を見つけました。私は、nineOldAndroids ライブラリの ObjectAnimator を使用して変化し続ける変数として、layoutParams を作成しました。多くの onDraw と onLayout が発生するため、それを達成するための最良の方法ではないと思いますが、コンテナーにいくつかのビューしかなく、サイズが変わらない場合は、おそらく問題ありません。

アニメーション化する imageView が最終的に必要な正確なサイズを取得し、(現在) サムネイルとアニメーション化された imageView の両方が同じコンテナーを持っていることを前提としています (ただし、簡単に変更できるはずです。

私がテストしたように、TouchImageViewクラスを拡張してズーム機能を追加することもできます。最初にスケール タイプを center-crop に設定し、アニメーションが終了したらマトリックスに戻します。必要に応じて、layoutParams を設定してコンテナ全体を埋めることができます (そして余白を 0,0 に設定します)。 )。

また、なぜ AnimatorSet が機能しなかったのか疑問に思っているので、ここで機能するものを示します。誰かが私に何をすべきか教えてくれることを願っています。

コードは次のとおりです。

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);
    }

}
于 2013-10-27T09:15:55.027 に答える
2

別の解決策として、小さい画像から大きい画像へのアニメーションを作成するだけの場合は、ActivityOptions.makeThumbnailScaleUpAnimation または makeScaleUpAnimation を試して、それらが自分に合っているかどうかを確認できます。

http://developer.android.com/reference/android/app/ActivityOptions.html#makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int)

于 2014-06-24T09:29:18.253 に答える
0

簡単なプロトタイプで同様の効果を得る方法を見つけました。本番環境での使用には適さないかもしれませんが (まだ調査中です)、すばやく簡単に使用できます。

  1. アクティビティ/フラグメント トランジションでフェード トランジションを使用します (まったく同じ位置の ImageView から始まります)。フラグメントのバージョン:

    final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();    
    fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);    
    ...etc    
    

    アクティビティ バージョン:

    Intent intent = new Intent(context, MyDetailActivity.class);
    startActivity(intent);
    getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
    

    これにより、ちらつきのないスムーズな遷移が得られます。

  2. 新しいフラグメントの onStart() でレイアウトを動的に調整します (メンバー フィールドを onCreateView の UI の適切な部分に保存し、このコードが 1 回だけ呼び出されるようにいくつかのフラグを追加する必要があります)。

    @Override    
    public void onStart() {    
        super.onStart();
    
        // Remove the padding on any layouts that the image view is inside
        mMainLayout.setPadding(0, 0, 0, 0);
    
        // Get the screen size using a utility method, e.g. 
        //  http://stackoverflow.com/a/12082061/112705
        // then work out your desired height, e.g. using the image aspect ratio.
        int desiredHeight = (int) (screenWidth * imgAspectRatio);
    
        // Resize the image to fill the whole screen width, removing 
        // any layout margins that it might have (you may need to remove 
        // padding too)
        LinearLayout.LayoutParams layoutParams =
                new LinearLayout.LayoutParams(screenWidth, desiredHeight);
        layoutParams.setMargins(0, 0, 0, 0);
    
        mImageView.setLayoutParams(layoutParams);
    }    
    
于 2014-01-16T23:07:04.760 に答える
0

最も簡単な方法は、scaleType を centerCrop に最大の高さまで維持しながら、ImageView の高さをアニメーション化することだと思います (通常のイメージビューで、カスタム ビューは必要ありません)。レイアウトを作成し、ViewTreeObserver を使用してレイアウトがいつ終了したかを知ることで、ImageView の高さを取得してから、新しい「折りたたまれた」高さを設定できます。私はそれをテストしていませんが、これは私がそれを行う方法です。

この投稿を見ることもできます。彼らは似たようなことをしていますhttp://nerds.airbnb.com/host-experience-android/

于 2014-06-23T07:38:06.290 に答える