32

Tôi muốn thiết lập chuyển đổi phần tử được chia sẻ khi chuyển từ một Hoạt động này sang Hoạt động khác.Chuyển đổi hoạt động phần tử được chia sẻ trên android 5

Hoạt động đầu tiên có RecyclerView với các mục. Khi một mục được nhấp vào, mục đó sẽ tạo hiệu ứng cho hoạt động mới.

Vì vậy, tôi đã đặt android: transitionName = "item" trên cả chế độ xem hoạt động cuối cùng, như được xem như chế độ xem mục của trình xem lại.

Tôi cũng đang sử dụng mã này khi đi vào hoạt động tiếp theo:

this.startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, itemView, "boomrang_item").toBundle()); 

Khi nhấp vào một mục, nó chuyển đúng cách và quan điểm mới được hiển thị. Nó thật sự rất đẹp. Tuy nhiên, khi tôi nhấp vào nút quay lại. Đôi khi nó hoạt động tốt, nhưng hầu hết thời gian hoạt động của tôi gặp sự cố với stacktrace sau:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.ViewGroup.transformMatrixToGlobal(android.graphics.Matrix)' on a null object reference 
      at android.view.GhostView.calculateMatrix(GhostView.java:95) 
      at android.app.ActivityTransitionCoordinator$GhostViewListeners.onPreDraw(ActivityTransitionCoordinator.java:845) 
      at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847) 
      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1956) 
      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054) 
      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779) 
      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) 
      at android.view.Choreographer.doCallbacks(Choreographer.java:580) 
      at android.view.Choreographer.doFrame(Choreographer.java:550) 
      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) 
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5221) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 

Tôi đang làm gì sai? Dường như một lỗi trong android 5

+0

bạn có thể thêm tất cả mã có liên quan để dễ dàng tìm ra nơi bạn đang đi sai? – gaara87

+0

Thử đặt 'transitionName' trên _only_ lượt xem phần tử được chia sẻ. –

+0

Tôi gặp vấn đề tương tự khi mở một hoạt động mới, xoay màn hình và nhấn nút quay lại. Bất kỳ giải pháp? – jclova

Trả lời

3

Nếu bạn đang sử dụng Proguard thì hãy thử thêm lỗi này vào tệp quy tắc của mình. Tôi đã có cùng một vấn đề và nó xuất hiện để làm việc?

-keep public class android.app.ActivityTransitionCoordinator

2

Hãy chắc chắn "itemView" Anh đang đi qua trong quá trình chuyển đổi được quan điểm được nhấp (nhận được trên onClick của bạn() gọi lại)

4

Hãy thử loại bỏ bất kỳ thẻ merge xml mà bạn có thể có trên hoạt động cuối cùng của xem. Tôi đã nhận thấy rằng việc chuyển sang chế độ xem, có chứa thẻ hợp nhất, trong đó phần tử chuyển đổi là con trực tiếp của thẻ hợp nhất, sẽ gây ra lỗi này, nhưng tôi nên thay thế thẻ hợp nhất bằng một vùng chứa khác như CardView, hoạt ảnh hoạt động tốt. Ngoài ra hãy chắc chắn rằng có mối quan hệ 1: 1 giữa transitionNames trong các khung nhìn.

CẬP NHẬT: Tôi gặp phải sự cố này một lần nữa khi thực hiện chuyển đổi hoạt động, nhấp vào nút quay lại để quay lại hoạt động ban đầu và sau đó thử chuyển đổi lần nữa. Tôi đã truy cập vào phụ huynh trực tiếp của 'thành phần chuyển đổi', (A RelativeLayout) theo id, với lệnh findViewById() và sau đó gọi removeAllViews(). Tôi đã kết thúc việc thay đổi mã để gọi 'removeAllViews()' trên một tổ tiên lớn hơn cha mẹ, cũng đã xóa một thẻ khỏi phần tử đã thay thế 'thành phần chuyển đổi' sau khi tải trang. Điều này làm giảm bớt vấn đề của tôi.

7

Tôi gặp phải sự cố tương tự và nhận thấy sự cố xảy ra nếu phần tử được chia sẻ ban đầu không còn hiển thị trên màn hình trước đó nữa (có thể đây là phần tử cuối cùng trên màn hình theo chiều dọc). không còn nhìn thấy được nữa, và do đó quá trình chuyển đổi không có nơi nào để đưa trở lại phần tử được chia sẻ.

workaround của tôi là để loại bỏ quá trình chuyển đổi trở lại (trong hoạt động thứ 2) nếu màn hình đã được xoay trước khi đi trở lại, nhưng tôi chắc chắn phải có một cách tốt hơn để xử lý này:

@Override 
public void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 
    mOrientationChanged = !mOrientationChanged; 
} 

@Override 
public void supportFinishAfterTransition() { 
    if (mOrientationChanged) { 
     /** 
     * if orientation changed, finishing activity with shared element 
     * transition may cause NPE if the original element is not visible in the returned 
     * activity due to new orientation, we just finish without transition here 
     */ 
     finish(); 
    } else { 
     super.supportFinishAfterTransition(); 
    } 
} 
3

Đảm bảo rằng Chế độ xem bạn đang chuyển sang hoạt động thứ hai không phải là bố cục gốc. Bạn có thể chỉ cần bọc nó trong một FrameLayout với một cửa sổ trong suốtBackground.

+0

Đó là một giải pháp! –

1

Điều tôi nảy ra là tránh chuyển trở lại hoạt động bằng RecyclerView hoặc thay đổi lại quá trình chuyển đổi bằng một thứ khác.

Disable tất cả các hiệu ứng chuyển tiếp trở lại:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
@Override 
public void finishAfterTransition() { 
    finish(); 
} 

Hoặc, nếu bạn muốn vô hiệu hóa các yếu tố chỉ chia sẻ trở chuyển tiếp, và có thể thiết lập chuyển lợi nhuận của riêng bạn:

// Track if finishAfterTransition() was called 
private boolean mFinishingAfterTransition; 

@Override 
protected void onCreate(@Nullable final Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    mFinishingAfterTransition = false; 
} 

public boolean isFinishingAfterTransition() { 
    return mFinishingAfterTransition; 
} 

@Override 
@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
public void finishAfterTransition() { 
    mFinishingAfterTransition = true; 
    super.finishAfterTransition(); 
} 

public void clearSharedElementsOnReturn() { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     TransitionUtilsLollipop.clearSharedElementsOnReturn(this); 
    } 
} 

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
private static final class TransitionUtilsLollipop { 

    private TransitionUtilsLollipop() { 
     throw new UnsupportedOperationException(); 
    } 

    static void clearSharedElementsOnReturn(@NonNull final BaseActivity activity) { 
     activity.setEnterSharedElementCallback(new SharedElementCallback() { 

      @Override 
      public void onMapSharedElements(final List<String> names, 
        final Map<String, View> sharedElements) { 
       super.onMapSharedElements(names, sharedElements); 
       if (activity.isFinishingAfterTransition()) { 
        names.clear(); 
        sharedElements.clear(); 
       } 
      } 
     }); 
    } 

Với điều đó được thực hiện trong hoạt động cơ sở, bạn có thể dễ dàng sử dụng nó trong onCreate()

@Override 
protected void onCreate(@Nullable final Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    clearSharedElementsOnReturn(this); 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
     // set your own transition 
     getWindow().setReturnTransition(new VerticalGateTransition()); 
    } 
} 
1

Tôi đã làm như vậy lỗi, của tôi đã được gây ra bởi cùng một lý do đằng sau câu trả lời của hidro nhưng được gây ra bởi bàn phím ẩn phần tử chia sẻ mà quá trình chuyển đổi sẽ quay lại.

Giải pháp thay thế của tôi là đóng bàn phím ngay lập tức trước khi hoàn thành hoạt động sao cho thành phần được chia sẻ trên hoạt động trước đó không bị che khuất.

View view = this.getCurrentFocus(); 
if (view != null) { 
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
} 
supportFinishAfterTransition(); 
2

Tôi đã gặp vấn đề tương tự, đối với tôi nó đã được gây ra bởi các bản cập nhật thực thi recyclerview sau/trong quá trình chuyển đổi thoát đầu tiên. Tôi cho rằng khung nhìn phần tử chia sẻ sau đó đôi khi được tái chế, có nghĩa là nó sẽ không còn có sẵn cho hoạt ảnh chuyển tiếp, do đó sự cố (thường là trên sự chuyển đổi trở lại nhưng đôi khi trong quá trình chuyển đổi thoát). Tôi đã giải quyết nó bằng cách chặn cập nhật nếu hoạt động bị tạm dừng (sử dụng cờ isRunning) - lưu ý rằng nó đã tạm dừng nhưng không dừng lại vì nó vẫn hiển thị trong nền. Ngoài ra, tôi đã chặn quá trình cập nhật nếu quá trình chuyển đổi đang chạy. Tôi tìm thấy nó đủ để nghe gọi lại này:

Transition sharedElementExitTransition = getWindow().getSharedElementExitTransition(); 
     if (sharedElementExitTransition != null) { 
      sharedElementExitTransition.addListener(.....); 
     } 

Như một biện pháp cuối cùng, mặc dù tôi không chắc chắn nếu điều này làm nên sự khác biệt, tôi cũng đã làm recyclerView.setLayoutFrozen(true)/recyclerView.setLayoutFrozen(false) trong onTransitionStart/onTransitionEnd.

1

Khi @Fabio Rocha cho biết, hãy đảm bảo rằng itemView được truy xuất từ ​​ViewHolder.

Bạn có thể lấy ViewHolder bởi vị trí qua

mRecyclerView.findViewHolderForAdapterPosition(position); 
0

Lý do cho điều này là thực sự khá đơn giản: Khi bạn hướng trở lại Hoạt động phụ huynh hoặc Fragment, các View là không có được nêu (có thể vì nhiều lý do).
Vì vậy, những gì bạn muốn làm là để trì hoãn quá trình chuyển đổi cho đến khi Chế độ xem khả dụng.

Công việc của tôi xung quanh là để gọi hàm sau trong onCreate() trong Fragment của tôi (còn hoạt động trong Hoạt động quá):

private void checkBeforeTransition() { 
    // Postpone the transition until the window's decor view has 
    // finished its layout. 
    getActivity().supportPostponeEnterTransition(); 

    final View decor = getActivity().getWindow().getDecorView(); 
    decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
     @Override 
     public boolean onPreDraw() { 
      decor.getViewTreeObserver().removeOnPreDrawListener(this); 
      getActivity().supportStartPostponedEnterTransition(); 
      return true; 
     } 
    }); 
} 
0

tôi đã phải đối mặt với cùng một vấn đề, thực sự tôi đã sử dụng căn cứ hỏa lực và Tôi có danh sách thông tin và khi người dùng chạm vào nó sẽ gọi detailActivity với sharedAnimation trong hoạt động này tôi đã cập nhật nó như đã thấy bằng firebase để sự kiện firebase cập nhật mục danh sách như đã thấy, trong trường hợp này vấn đề này được gọi vì chế độ xem của trình thu thập màn hình bố cục đã được thực hiện.

và nó gọi một ngoại lệ vì id chuyển đổi mà chúng tôi đã vượt qua nó không còn nữa, vì vậy tôi giải quyết vấn đề này bằng cách sử dụng phương pháp này.

onPause() Tôi đã cố định bố cục và onResume() đặt thành false;

@Override 
public void onPause() { 
    super.onPause(); 
    mRecycler.setLayoutFrozen(true); 
} 

@Override 
public void onResume() { 
    super.onResume(); 
    mRecycler.setLayoutFrozen(false); 
} 

Và nó hoạt động.

Các vấn đề liên quan