java - 共享元素转换未正确退出

标签 java android android-animation android-transitions shared-element-transition

我有一个 fragment ,我正在从中启动带有 viewpager 的共享元素转换的 Activity ,输入转换工作正常,但是当我在 View 寻呼机中滚动并完成转换时,共享图像来自左侧,这是不需要的应该将自身重新定位到启动的位置,这是我的代码:

Intent myIntent = new Intent(getActivity(), EnlargeActivity.class);

            ActivityOptionsCompat options = ActivityOptionsCompat.
                    makeSceneTransitionAnimation(getActivity(),
                            imageView,
                            ViewCompat.getTransitionName(imageView));
            startActivity(myIntent, options.toBundle());

我在完成 Activity 时更新包含 viewpager 的 Activity 中的 View 及其名称,但它与闪烁一起使用:

public void finishAfterTransition() {
    setEnterSharedElementCallback(new SharedElementCallback() {
        @Override
        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
            // Clear all current shared views and names
            names.clear();
            sharedElements.clear();

            ViewGroup viewGroup = (ViewGroup) viewPagerDetail.getAdapter()
                    .instantiateItem(viewPagerDetail, viewPagerDetail.getCurrentItem());

            if (viewGroup == null) {
                return;
            }

            // Map the first shared element name to the child ImageView.
            sharedElements.put(viewGroup.findViewById(R.id.img).getTransitionName(), viewGroup.findViewById(R.id.img));

           // setExitSharedElementCallback((SharedElementCallback) this);
        }
    });

    super.finishAfterTransition();

最佳答案

基本上,Android 会使用您预定义的 View 开始转换。和transitionName并自动使用相同的属性进行返回转换。当您在 ViewPager 中更改焦点 View 时,Android 不会知道这一点,并会在返回时保持前一个 View 的转换。因此您需要将更改通知 Android:

  • 重新映射过渡属性:使用 setEnterSharedElementCallback更改 transitionNameViewActivity2返回之前到新的.
  • 等待Activity1完成渲染addOnPreDrawListener .

最终的实现有点复杂。不过你可以看看我的示例代码https://github.com/tamhuynhit/PhotoGallery 。我尝试实现从许多简单部分到复杂部分的共享元素转换。 您的问题出现在 Level 3并在 Level 4 中解决。

我正在写一个关于此的教程,但它不是英文的,所以希望代码可以提供帮助

更新 1:工作流程

以下是我在代码中实现它的方法:

  • 覆盖finishAfterTransition在 Activity2 中并调用 setEnterSharedElementCallback方法重新映射 ViewPager 中当前选定的项目。另外,请调用setResult将新选定的索引传递回此处的先前 Activity 。

    @Override 
    @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
    public void finishAfterTransition() {
        setEnterSharedElementCallback(new SharedElementCallback() {
            @Override
            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
                View selectedView = getSelectedView();
                if (selectedView == null)
                    return;
    
                // Clear all current shared views and names
                names.clear();
                sharedElements.clear();
    
                // Store new selected view and name
                String transitionName = ViewCompat.getTransitionName(selectedView);
                names.add(transitionName);
                sharedElements.put(transitionName, selectedView);
    
                setExitSharedElementCallback((SharedElementCallback) null);
            }
        });
    
        Intent intent = new Intent();
        intent.putExtra(PHOTO_FOCUSED_INDEX, mCurrentIndex);
        setResult(RESULT_PHOTO_CLOSED, intent);
    
        super.finishAfterTransition();
    }
    
  • 编写自定义 ShareElementCallback所以我可以在知道哪个 View 之前设置回调即将被使用。

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static class CustomSharedElementCallback extends SharedElementCallback {
        private View mView;
    
        /**
         * Set the transtion View to the callback, this should be called before starting the transition so the View is not null
         */
        public void setView(View view) {
            mView = view;
        }
    
        @Override
        public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
            // Clear all current shared views and names
            names.clear();
            sharedElements.clear();
    
            // Store new selected view and name
            String transitionName = ViewCompat.getTransitionName(mView);
            names.add(transitionName);
            sharedElements.put(transitionName, mView);
        }
    }
    
  • 覆盖onActivityReenter在Activity1中,从结果Intent中获取选定的索引。设置setExitSharedElementCallback重新映射新选定的View当过渡开始时。调用 supportPostponeEnterTransition延迟一下,因为你的新 View此时可能无法渲染。使用getViewTreeObserver().addOnPreDrawListener要监听布局更改,请找到正确的 View按所选索引并继续转换 supportStartPostponedEnterTransition .

    @Override
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void onActivityReenter(int resultCode, Intent data) {
        if (resultCode != LevelFourFullPhotoActivity.RESULT_PHOTO_CLOSED || data == null)
            return;
    
        final int selectedIndex = data.getIntExtra(LevelFourFullPhotoActivity.PHOTO_FOCUSED_INDEX, -1);
        if (selectedIndex == -1)
            return;
    
        // Scroll to the new selected view in case it's not currently visible on the screen
        mPhotoList.scrollToPosition(selectedIndex);
    
        final CustomSharedElementCallback callback = new CustomSharedElementCallback();
        getActivity().setExitSharedElementCallback(callback);
    
        // Listen for the transition end and clear all registered callback
        getActivity().getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {}
    
            @Override
            public void onTransitionPause(Transition transition) {}
    
            @Override
            public void onTransitionResume(Transition transition) {}
    
            @Override
            public void onTransitionEnd(Transition transition) {
                removeCallback();
            }
    
            @Override
            public void onTransitionCancel(Transition transition) {
                removeCallback();
            }
    
            private void removeCallback() {
                if (getActivity() != null) {
                    getActivity().getWindow().getSharedElementExitTransition().removeListener(this);
                    getActivity().setExitSharedElementCallback((SharedElementCallback) null);
                }
            }
        });
    
        // Pause transition until the selected view is fully drawn
        getActivity().supportPostponeEnterTransition();
    
        // Listen for the RecyclerView pre draw to make sure the selected view is visible,
        //  and findViewHolderForAdapterPosition will return a non null ViewHolder
        mPhotoList.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mPhotoList.getViewTreeObserver().removeOnPreDrawListener(this);
    
                RecyclerView.ViewHolder holder = mPhotoList.findViewHolderForAdapterPosition(selectedIndex);
                if (holder instanceof ViewHolder) {
                    callback.setView(((ViewHolder) holder).mPhotoImg);
                }
    
                // Continue the transition
                getActivity().supportStartPostponedEnterTransition();
    
                return true;
            }
        });
    }
    

更新 2:getSelectedItem

要从 ViewPager 获取选定的 View ,请勿使用 getChildAt或者你得到错误的 View ,使用findViewWithTag相反

PagerAdapter.instantiateItem ,使用position作为每个View的标签:

@Override
public View instantiateItem(ViewGroup container, int position) {
    // Create the View
    view.setTag(position)

    // ...
}

onPageSelected获取所选索引的事件:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        mSelectedIndex = position;
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});

调用getSelectedView通过所选索引获取当前 View

private View getSelectedView() {
    try {
        return mPhotoViewPager.findViewWithTag(mSelectedIndex);
    } catch (IndexOutOfBoundsException | NullPointerException ex) {
        return null;
    }
}

关于java - 共享元素转换未正确退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50189286/

相关文章:

字段名称作为关键字的 Java 约定

Java 二维字符串数组 : find column where all elements are the same

android - ImageView 从左到右滑动动画,反之亦然

java - Android 致命信号 11 (SIGSEGV),代码 1,tid 29092 中的故障地址 0x0

java - 嵌套首选项屏幕上的 ActionBar 着色丢失 (android)

java - 比较可能为空的字符串的更好方法

java - 将 pcm 转换为 .wav - 有噪音

Android webview 滚动问题

android - 获取 Gmail 个人资料图片和全名

android - 未调用自定义 ScaleAnimation/applytransformation