web-dev-qa-db-ja.com

Android-リストビューのアイテムからアクティビティ全体への遷移を作成する方法は?

私が欲しいのは、ユーザーがListViewのリスト項目をクリックすると、アクティビティ全体に変換されることです(次の例で見ることができます)が、これを説明するチュートリアルを見つけることができず、実際にはこの動きがどのように呼ばれているのか分からない。

言い換えれば、私が達成したいことは:

  1. クリックするとリストアイテムの昇格が増加します(右のgifを参照)。

  2. リスト項目を展開し、クリックされた項目に関する詳細情報を含む次のフラグメント/アクティビティレイアウトに変換します

enter image description here

私は多くの移行を試みましたが、運はありませんでした。誰でもこれを達成するのを手伝ってくれますか?

24
Antonio

目的の効果を伴う2つのアクティビティ間を遷移する小さなサンプルアプリケーションを作成します。 Sample Application

ただし、提供されるgifのトランジションはわずかに異なります。左側のgifのトランジションは、リスト要素を2番目のアクティビティのコンテンツ領域にトランジションします(ツールバーはそのままです)。右側のgifで、遷移はリスト要素を2番目のアクティビティの完全な画面に変換します。次のコードは、左のgifで効果を提供します。ただし、適切なgifの移行を実現するために、わずかな修正を加えてソリューションを適応させることは可能です。

これはLollipopでのみ機能することに注意してください。ただし、古いデバイスで異なる効果を模倣することは可能です。さらに、提供されたコードの唯一の目的は、それがどのように行われるかを示すことです。これをアプリで直接使用しないでください。

主な活動:

public class MainActivity extends AppCompatActivity {

    MyAdapter myAdapter;

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

        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
        ListView listView = (ListView) findViewById(R.id.list_view);

        myAdapter = new MyAdapter(this, 0, DataSet.get());

        listView.setAdapter(myAdapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
                startTransition(view, myAdapter.getItem(position));
            }
        });
    }

    private void startTransition(View view, Element element) {
        Intent i = new Intent(MainActivity.this, DetailActivity.class);
        i.putExtra("ITEM_ID", element.getId());

        Pair<View, String>[] transitionPairs = new Pair[4];
        transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
        transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)

        // We also want to transition the status and navigation bar barckground. Otherwise they will flicker
        transitionPairs[2] = Pair.create(findViewById(Android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
        transitionPairs[3] = Pair.create(findViewById(Android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
        Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();

        ActivityCompat.startActivity(MainActivity.this, i, b);
    }
}

activity_main.xml:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"
        Android:layout_width="match_parent"
        Android:layout_height="?attr/actionBarSize"
        Android:background="@color/colorPrimary"
        Android:transitionName="toolbar" />

    <ListView
        Android:id="@+id/list_view"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</LinearLayout>

DetailActivity:

public class DetailActivity extends AppCompatActivity {

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

        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));

        long elementId = getIntent().getLongExtra("ITEM_ID", -1);
        Element element = DataSet.find(elementId);


        ((TextView) findViewById(R.id.title)).setText(element.getTitle());
        ((TextView) findViewById(R.id.description)).setText(element.getDescription());

        // if we transition the status and navigation bar we have to wait till everything is available
        TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
        // set a custom shared element enter transition
        TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
    }
}

activity_detail.xml:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"
        Android:layout_width="match_parent"
        Android:layout_height="?attr/actionBarSize"
        Android:background="@color/colorPrimary"
        Android:transitionName="toolbar" />

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:background="#abc"
        Android:orientation="vertical"
        Android:paddingBottom="200dp"
        Android:transitionName="content_area"
        Android:elevation="10dp">

        <TextView
            Android:id="@+id/title"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content" />

        <TextView
            Android:id="@+id/description"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>

detail_activity_shared_element_enter_transition.xml(/ res/transition /):

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:transitionOrdering="together">
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <changeImageTransform/>
    <transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>

my.application.transitions.ElevationTransition:

@TargetApi(Build.VERSION_CODES.Lollipop)
public class ElevationTransition extends Transition {

    private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";

    public ElevationTransition() {
    }

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

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    private void captureValues(TransitionValues transitionValues) {
        Float elevation = transitionValues.view.getElevation();
        transitionValues.values.put(PROPNAME_ELEVATION, elevation);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }

        Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
        Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
        if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
            return null;
        }

        final View view = endValues.view;
        ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
        a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                view.setElevation((float)animation.getAnimatedValue());
            }
        });

        return a;
    }
}

TransitionHelper:

public class TransitionHelper {

    public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop)
            return;

        final View decor = activity.getWindow().getDecorView();
        if (decor == null)
            return;
        activity.postponeEnterTransition();
        decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @TargetApi(Build.VERSION_CODES.Lollipop)
            @Override
            public boolean onPreDraw() {
                decor.getViewTreeObserver().removeOnPreDrawListener(this);
                activity.startPostponedEnterTransition();
                return true;
            }
        });
    }

    public static void setSharedElementEnterTransition(final Activity activity, int transition) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop)
            return;
        activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
    }
}

それでは、ここで異なる部分は何ですか:2つのアクティビティがあります。移行中に、アクティビティ間で4つのビューが移行されます。

  • ツールバー:左のgifのように、ツールバーは他のコンテンツとともに移動しません。

  • ListView要素View->は、DetailActivityのコンテンツビューになります

  • ステータスバーとナビゲーションバーの背景:これらのビューを遷移ビューのセットに追加しない場合、遷移中にフェードアウトします。ただし、これにはEnter遷移の遅延が必要です(TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar

MainActivityでは、DetailActivityを開始するために使用されるバンドルに遷移ビューが追加されます。さらに、両方のアクティビティで、遷移したビューに名前を付ける必要があります(transitionName)。これは、プログラムだけでなくレイアウトxmlでも実行できます。

共有要素の遷移中に使用される遷移のデフォルトセットは、ビューのさまざまな側面に影響します(たとえば、ビューの境界- 2 を参照)。ただし、ビューの標高の違いはアニメーション化されません。これが、提示されたソリューションがカスタムのElevationTransitionを使用する理由です。

17
Andreas Wenger

これを試してください。 Material-Animations

blueIconImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, SharedElementActivity.class);

        View sharedView = blueIconImageView;
        String transitionName = getString(R.string.blue_name);

        ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
        startActivity(i, transitionActivityOptions.toBundle());
    }
});

SharedElements

4
Carlos.V

この壮大なウェブページをお試しください@ アクティビティとフラグメントの移行の開始(パート1) 。ここで、彼らはアクティビティとフラグメントの移行について話しました。試したことがありません。私の見解では、Fragment Transitionsはより優れており、コンピューターへの負荷が少ないので、良いスタートです。また、ツールバーを変更する必要はないかもしれません。ツールバーを表示/非表示できます。

別の良いSOリンクは@ フラグメント間の遷移をアニメーション化する 、ベストアンサーを見てください。その投稿で、彼らは objectAnimator

別の意見としては、投稿したサンプルアニメーションに関するもので、ある芸術から別の芸術へのスムーズなアニメーションは表示されません。アニメーションが滑らかでない場合は、それほど印象的ではありません。

頑張って、楽しんで、みんなに投稿してください。