我想要的是,当用户单击ListView中的列表项时,它会转换为整个 Activity (如下面的示例所示),但是我找不到能解释这一点的教程,实际上,不知道怎么称呼这种运动。

换句话说,我想要实现的是:

  • 单击时增加列表项的高度(如右图所示)
  • 扩展列表项并将其转换为下一个片段/ Activity 布局,其中包含有关单击的项目
  • 的详细信息

    android - Android-如何创建从列表 View 中的项到整个 Activity 的过渡?-LMLPHP

    我尝试了很多转换,但是没有运气。谁能帮我实现这一目标?

    最佳答案

    我构建了一个小示例应用程序,该应用程序在两个 Activity 之间进行了转换,并达到了预期的效果:
    android - Android-如何创建从列表 View 中的项到整个 Activity 的过渡?-LMLPHP

    但是,提供的gif中的过渡效果略有不同。的
    左侧gif中的transition会将list元素转换为第二个 Activity 的内容区域(工具栏保持原位)。在右侧的gif中,转换将列表元素转换为第二个 Activity 的完整屏幕。以下代码在左侧gif中提供了效果。但是,应该可以对解决方案进行少量修改,以实现正确的gif过渡。

    请注意,这仅适用于棒棒糖。但是,可以在旧设备上模拟不同的效果。
    此外,提供的代码的唯一目的是演示如何实现。不要在您的应用程序中直接使用它。

    主要 Activity :

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

    那么这里有哪些不同的部分:
    我们有两个 Activity 。在过渡期间, Activity 之间会过渡四个 View 。
  • 工具栏:就像左gif中一样,工具栏不会随其余内容一起移动。
  • ListView元素View->成为DetailActivity
  • 的内容 View
  • StatusBar和NavigationBar背景:如果我们不将这些 View 添加到过渡 View 集中,它们将在过渡期间淡出并返回。但是,这需要延迟输入转换(请参阅:TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)

  • MainActivity中,将转换后的 View 添加到用于启动DetailActivity的Bundle中。此外,在两个 Activity 中都需要命名转换后的 View (transitionName)。这可以在布局xml中以及以编程方式完成。

    在共享元素过渡期间使用的默认过渡集会影响 View 的不同方面(例如: View 边界-请参见2)。但是, View 高程的差异不会显示动画。这就是为什么提出的解决方案使用自定义ElevationTransition的原因。

    07-24 09:45
    查看更多