Переход общего элемента не завершается должным образом - PullRequest
0 голосов
/ 05 мая 2018

У меня есть фрагмент, из которого я запускаю действие с переходом общего элемента, в котором есть viewpager, переход enter работает нормально, но когда я прокручиваю просмотр пейджера и заканчиваю переход, разделяемое изображение приходит с левой стороны, которая ему не нужна должен переместить себя туда, где он был запущен, вот мой код:

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

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

Я обновляю представление и его имя в действии, которое содержит видоискатель по завершении действия, но оно мигает:

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

Ответы [ 2 ]

0 голосов
/ 05 мая 2018

По сути, Android запускает переход с предварительно заданными View и transitionName и автоматически использует те же свойства для обратного перехода. Когда вы изменяете свой сфокусированный View в ViewPager, Android не знает об этом и сохраняет переход предыдущего на обратном пути. Так что вам нужно сообщить Android об изменениях:

  • Переопределить свойства перехода: используйте setEnterSharedElementCallback, чтобы изменить transitionName и View на новые, прежде чем вернуться из Activity2.
  • Подождите, пока Activity1 завершит рендеринг addOnPreDrawListener.

Это немного сложно в финальной реализации. Но вы можете посмотреть на мой пример кода https://github.com/tamhuynhit/PhotoGallery. Я пытаюсь реализовать переход от общего элемента от множества простых к сложным разделам. Ваша проблема появилась с Level 3 и решена в Level 4.

Я пишу учебник по этому вопросу, но он не на английском, поэтому надеюсь, что код поможет

ОБНОВЛЕНИЕ 1: рабочий процесс

Вот как я реализую это в своем коде:

  • Переопределите finishAfterTransition в Activity2 и вызовите метод setEnterSharedElementCallback, чтобы повторно отобразить текущий выбранный элемент в ViewPager. Также, позвоните setResult, чтобы передать новый выбранный индекс назад к предыдущему действию здесь.

    @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, не используйте getChildAt, иначе вы получите неправильный вид, используйте findViewWithTag вместо

В PagerAdapter.instantiateItem используйте позицию в качестве тега для каждого просмотра:

@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, чтобы получить текущий вид по выбранному индексу

private View getSelectedView() {
    try {
        return mPhotoViewPager.findViewWithTag(mSelectedIndex);
    } catch (IndexOutOfBoundsException | NullPointerException ex) {
        return null;
    }
}
0 голосов
/ 05 мая 2018

Это на самом деле поведение по умолчанию, я много боролся с SharedElementTransitions, но у меня есть вложенные фрагменты. Я получил свое решение из статьи (очень недавняя статья), она показывает реализацию с RecyclerView, которая, как я полагаю, у вас есть. Короче говоря, решение состоит в том, чтобы переопределить onLayoutChange:

recyclerView.addOnLayoutChangeListener(
new OnLayoutChangeListener() {
  @Override
  public void onLayoutChange(View view,
            int left, 
            int top, 
            int right, 
            int bottom, 
            int oldLeft, 
            int oldTop, 
            int oldRight, 
            int oldBottom) {
     recyclerView.removeOnLayoutChangeListener(this);
     final RecyclerView.LayoutManager layoutManager =
        recyclerView.getLayoutManager();
     View viewAtPosition = 
        layoutManager.findViewByPosition(MainActivity.currentPosition);
     // Scroll to position if the view for the current position is null (not   
     // currently part of layout manager children), or it's not completely
     // visible.
     if (viewAtPosition == null 
         || layoutManager.isViewPartiallyVisible(viewAtPosition, false, true)){
        recyclerView.post(() 
           -> layoutManager.scrollToPosition(MainActivity.currentPosition));
     }
 }
});

Вот статья , и вы также найдете проект на GitHub .

...