setEnterSharedElementCallback

setEnterSharedElementCallback

本文介绍了共享元素转换未正确退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有片段,正在使用其中具有viewpager的共享元素过渡来启动活动,entry过渡工作正常,但是当我在视图分页器中滚动并完成过渡时,共享图像来自左侧,这是不希望的应该将其自身重新定位到启动位置,这是我的代码:

I have fragment from which I'm launching activity with shared element transition that has viewpager in it, the enter transition works fine but when i scroll in view pager and finish transition the shared image comes from left side which is not desired it should reposition itself to where it was launched, here is my code:

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

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

完成活动时,我正在更新包含viewpager的活动中的视图及其名称,但它会闪烁:

I'm updating view and its name in activity that contains viewpager when finishing activity, but its going with blink:

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使用预先定义的ViewtransitionName开始过渡,并自动使用相同的属性进行返回过渡.当您在ViewPager中更改焦点视图时,Android对此一无所知,并且在返回时仍保持上一个过渡.因此,您需要通知Android有关更改:

Basically, Android start the transition with your pre-defined View and transitionName and automatically use the same properties for the return transition. When you change your focused View in ViewPager, Android doesn't know about that and keep the transition on the previous one on its way back. So you need to inform Android about the changes:

  • 重新映射过渡属性:使用setEnterSharedElementCallbacktransitionNameView更改为新的,然后再从Activity2返回.
  • 等待Activity1完成渲染addOnPreDrawListener.
  • Remap the transition properties: Use setEnterSharedElementCallback to change the transitionName and View to the new one before returning from Activity2.
  • Wait for the Activity1 to finish rendering addOnPreDrawListener.

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

It's a bit complex in the final implementation. But you can look at my sample code https://github.com/tamhuynhit/PhotoGallery. I try to implement the shared-element-transition from many simple to complex sections.Your problem appeared from Level 3 and solved in Level 4.

我正在写一个有关此的教程,但是它不是英语,所以希望代码能对您有所帮助

I am writing a tutorial about this but it's not in English so hope the code can help

更新1:工作流程

这是我在代码中实现它的方式:

Here is how I implement it in my code:

  • 在Activity2中覆盖finishAfterTransition,并调用setEnterSharedElementCallback方法以重新映射ViewPager中的当前选定项.另外,调用setResult将新选择的索引传递回此处的上一个活动.

  • Override finishAfterTransition in Activity2 and call setEnterSharedElementCallback method to re-map the current selected item in ViewPager. Also, call setResult to pass the new selected index back to previous activity here.

@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之前设置回调.

  • Write a custom ShareElementCallback so I can set the callback before knowing which View is going to be used.

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

  • 覆盖Activity1中的onActivityReenter,从结果Intent中获取选定的索引.设置setExitSharedElementCallback以便在过渡开始时重新映射新选择的View.调用supportPostponeEnterTransition会延迟一点时间,因为此时可能无法渲染新的View.使用getViewTreeObserver().addOnPreDrawListener侦听布局更改,通过选定的索引找到正确的View,然后继续过渡supportStartPostponedEnterTransition.

  • Override onActivityReenter in Activity1, get the selected index from the result Intent. Set setExitSharedElementCallback to re-map new selected View when the transition begins.Call supportPostponeEnterTransition to delay a bit because your new View may not be rendered at this point. Use getViewTreeObserver().addOnPreDrawListener to listen for the layout changes, find the right View by the selected index and continue the transition 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中选择视图,请不要使用getChildAt,否则您将获得错误的视图,请使用findViewWithTag

    To get selected View from the ViewPager, don't use getChildAt or you get the wrong View, use findViewWithTag instead

    PagerAdapter.instantiateItem中,将位置用作每个视图的标签:

    In the PagerAdapter.instantiateItem, use position as tag for each View:

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

    收听onPageSelected事件以获取选定的索引:

    Listen to onPageSelected event to get the selected index:

    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以按选定的索引获取当前视图

    Call getSelectedView to get the current view by the selected index

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

    这篇关于共享元素转换未正确退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

    07-25 13:13