Android过渡动画在26版本以下的API中无法正常工作。

huangapple go评论74阅读模式
英文:

Android transition animation does not work in API below 26

问题

SettingsActivity.java :

public class SettingsActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);
        setupToolbar();
        getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.pref_content, new MainPreferenceFragment())
            .commit();
    }

    private void setupToolbar() {
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        try {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }
}

MainPreferenceFragment.java :

import android.transition.TransitionInflater;
import androidx.core.app.ActivityOptionsCompat;
import androidx.preference.ListPreference;
import androidx.transition.Explode;
import androidx.transition.Fade;
import androidx.transition.Transition;

public class MainPreferenceFragment extends PreferenceFragmentCompat {
    public static final String KEY_PROFILE = "pref_profile_key";

    private File myPhotoFile;
    private int profileIconSize;
    private Preference profile;

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences, rootKey);
        Context context = getContext();
        initPreferences(context);
        setListeners(context);
    }

    @Override
    public void onResume() {
        super.onResume();
        profile.setTitle(MainBroadcastReceiver.getMyDeviceName());
        if (myPhotoFile.exists()) {
            profile.setIcon(
                new BitmapDrawable(getResources(), ThumbnailUtils.extractThumbnail(
                        BitmapFactory.decodeFile(myPhotoFile.getPath()), profileIconSize, profileIconSize)));
        } else {
            profile.setIcon(R.drawable.avatar_contact);
        }
    }

    private void initPreferences(Context context) {
        profile = findPreference(KEY_PROFILE);
        profile.setSummary(MainBroadcastReceiver.getMyDeviceMacAddress());
        myPhotoFile = FileManager.myPhotoFile(context);
        profileIconSize = ActivityManager.dp2px(context, 67F);
    }

    private void setListeners(Context context) {
        profile.setOnPreferenceClickListener(preference -> {
            AppCompatActivity activity = (AppCompatActivity) getActivity();
            Intent intent = new Intent(activity, ProfileActivity.class);

            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                activity.getWindow().setExitTransition(TransitionInflater.from(activity).inflateTransition(R.transition.fade));
                View profileView = getListView().getChildAt(profile.getOrder());
                ImageView profileImageView = profileView.findViewById(android.R.id.icon);
                ActivityOptionsCompat options = ActivityOptionsCompat
                    .makeSceneTransitionAnimation(activity, profileImageView, profileImageView.getTransitionName());
                startActivity(intent, options.toBundle());
            } else {
                startActivity(intent);
            }
            return true;
        });
    }
}

preference_profile.xml :

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/chat_row_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true">

    <!-- Rest of the XML content (irrelevant for translation) -->

</androidx.constraintlayout.widget.ConstraintLayout>

preferences.xml:

<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.preference.Preference
        android:icon="@drawable/avatar_contact"
        android:key="pref_profile_key"
        android:layout="@layout/preference_profile"
        android:summary="u:n:k:n:o:w:n"
        android:title="@string/unknown" />

</androidx.preference.PreferenceScreen>

ProfileActivity.java :

import android.transition.Transition;
import android.transition.TransitionInflater;

public class ProfileActivity extends BaseActivity {

    private SimpleDraweeView imageDrawee;
    private ImageView setImageImageView;

    private boolean isBackPressed = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
        setupTransition();
    }

    private void setupTransition() {
        // Rest of the setupTransition method (irrelevant for translation)
    }

    @Override
    public void onBackPressed() {
        // Rest of the onBackPressed method (irrelevant for translation)
    }
}

activity_profile.xml :

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Rest of the XML content (irrelevant for translation) -->

</androidx.constraintlayout.widget.ConstraintLayout>

styles.xml :

<resources>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar" />

    <style name="AppTheme" parent="AppTheme.Base">
        <!-- Rest of the style items (irrelevant for translation) -->
    </style>
</resources>

styles.xml (v21) :

<resources>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Rest of the style items (irrelevant for translation) -->
    </style>
</resources>

change_image_transition.xml :

<transitionSet>
    <changeImageTransform />
    <changeBounds />
</transitionSet>

explode.xml (v21) :

<explode xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rest of the explode definition (irrelevant for translation) -->
</explode>

fade.xml (v21) :

<fade xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rest of the fade definition (irrelevant for translation) -->
</fade>

Note: The provided content contains XML layouts, Java code, and resource references that are specific to Android app development. Let me know if there's anything else you need assistance with!

英文:

I tried to create a profile preference like WhatsApp where the profile photo is shared with the profile activity. I used the following code which works properly in Android 8 and above. But in Android 7 and below, the shared element animation does not work. This is the result of a test on API 21.

Irrelevant codes have been removed.

SettingsActivity.java :

public class SettingsActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
setupToolbar();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.pref_content, new MainPreferenceFragment())
.commit();
}
private void setupToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
try {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
} catch (NullPointerException e) {
e.printStackTrace();
}
}}

MainPreferenceFragment.java :

import android.transition.TransitionInflater;
import androidx.core.app.ActivityOptionsCompat;
import androidx.preference.ListPreference;
import androidx.transition.Explode;
import androidx.transition.Fade;
import androidx.transition.Transition;
public class MainPreferenceFragment extends PreferenceFragmentCompat {
public static final String KEY_PROFILE = &quot;pref_profile_key&quot;;
private File myPhotoFile;
private int profileIconSize;
private Preference profile;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
Context context = getContext();
initPreferences(context);
setListeners(context);
}
@Override
public void onResume() {
super.onResume();
profile.setTitle(MainBroadcastReceiver.getMyDeviceName());
if (myPhotoFile.exists()) {
profile.setIcon(
new BitmapDrawable(getResources(), ThumbnailUtils.extractThumbnail(
BitmapFactory.decodeFile(myPhotoFile.getPath()), profileIconSize, profileIconSize)));
} else {
profile.setIcon(R.drawable.avatar_contact);
}
}
private void initPreferences(Context context) {
profile = findPreference(KEY_PROFILE);
profile.setSummary(MainBroadcastReceiver.getMyDeviceMacAddress());
myPhotoFile = FileManager.myPhotoFile(context);
profileIconSize = ActivityManager.dp2px(context, 67F);
}
private void setListeners(Context context) {
profile.setOnPreferenceClickListener(preference -&gt; {
AppCompatActivity activity = (AppCompatActivity) getActivity();
Intent intent = new Intent(activity, ProfileActivity.class);
if (android.os.Build.VERSION.SDK_INT &gt;= android.os.Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setExitTransition(TransitionInflater.from(activity).inflateTransition(R.transition.fade));
View profileView = getListView().getChildAt(profile.getOrder());
ImageView profileImageView = profileView.findViewById(android.R.id.icon);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(activity
, profileImageView, profileImageView.getTransitionName());
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
}
return true;
});
}}

preference_profile.xml :

&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;
android:id=&quot;@+id/chat_row_container&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;wrap_content&quot;
android:background=&quot;?android:attr/selectableItemBackground&quot;
android:clickable=&quot;true&quot;
android:focusable=&quot;true&quot;&gt;
&lt;LinearLayout
android:layout_width=&quot;0dp&quot;
android:layout_height=&quot;1dp&quot;
android:background=&quot;?attr/dividerColor&quot;
android:orientation=&quot;horizontal&quot;
app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
app:layout_constraintEnd_toEndOf=&quot;parent&quot;
app:layout_constraintStart_toStartOf=&quot;parent&quot; /&gt;
&lt;TextView
android:id=&quot;@android:id/summary&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_marginEnd=&quot;12dp&quot;
android:layout_marginRight=&quot;12dp&quot;
android:ellipsize=&quot;end&quot;
android:gravity=&quot;start&quot;
android:maxLines=&quot;1&quot;
android:singleLine=&quot;true&quot;
android:text=&quot;Okay, Bye.&quot;
android:textAlignment=&quot;gravity&quot;
android:textColor=&quot;?android:attr/textColorSecondary&quot;
android:textSize=&quot;14sp&quot;
android:transitionName=&quot;macTransition&quot;
app:layout_constrainedWidth=&quot;true&quot;
app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
app:layout_constraintEnd_toEndOf=&quot;parent&quot;
app:layout_constraintHorizontal_bias=&quot;0.0&quot;
app:layout_constraintStart_toStartOf=&quot;@android:id/title&quot;
app:layout_constraintTop_toBottomOf=&quot;@android:id/title&quot; /&gt;
&lt;TextView
android:id=&quot;@android:id/title&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_marginStart=&quot;15dp&quot;
android:layout_marginLeft=&quot;15dp&quot;
android:ellipsize=&quot;end&quot;
android:gravity=&quot;start&quot;
android:maxLines=&quot;1&quot;
android:singleLine=&quot;true&quot;
android:text=&quot;Robert Downey&quot;
android:textAlignment=&quot;gravity&quot;
android:textColor=&quot;@android:color/black&quot;
android:textSize=&quot;22sp&quot;
android:transitionName=&quot;nameTransition&quot;
app:layout_constrainedWidth=&quot;true&quot;
app:layout_constraintBottom_toTopOf=&quot;@android:id/summary&quot;
app:layout_constraintEnd_toEndOf=&quot;parent&quot;
app:layout_constraintHorizontal_bias=&quot;0.0&quot;
app:layout_constraintHorizontal_chainStyle=&quot;spread_inside&quot;
app:layout_constraintStart_toEndOf=&quot;@android:id/icon&quot;
app:layout_constraintTop_toTopOf=&quot;parent&quot;
app:layout_constraintVertical_chainStyle=&quot;packed&quot; /&gt;
&lt;de.hdodenhof.circleimageview.CircleImageView
android:id=&quot;@android:id/icon&quot;
android:layout_width=&quot;67dp&quot;
android:layout_height=&quot;67dp&quot;
android:layout_marginStart=&quot;14dp&quot;
android:layout_marginLeft=&quot;14dp&quot;
android:layout_marginTop=&quot;17dp&quot;
android:layout_marginBottom=&quot;17dp&quot;
android:src=&quot;@drawable/avatar_contact&quot;
android:transitionName=&quot;imageTransition&quot;
app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
app:layout_constraintStart_toStartOf=&quot;parent&quot;
app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

preferences.xml:

&lt;androidx.preference.PreferenceScreen xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;&gt;
&lt;androidx.preference.Preference
android:icon=&quot;@drawable/avatar_contact&quot;
android:key=&quot;pref_profile_key&quot;
android:layout=&quot;@layout/preference_profile&quot;
android:summary=&quot;u:n:k:n:o:w:n&quot;
android:title=&quot;@string/unknown&quot; /&gt;
&lt;/androidx.preference.PreferenceScreen&gt;

ProfileActivity.java :

import android.transition.Transition;
import android.transition.TransitionInflater;
public class ProfileActivity extends BaseActivity {
private SimpleDraweeView imageDrawee;
private ImageView setImageImageView;
private boolean isBackPressed = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
setupTransition();
}
private void setupTransition() {
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setAllowEnterTransitionOverlap(true);
Transition fade = TransitionInflater.from(this).inflateTransition(R.transition.fade);
fade.excludeTarget(R.id.setImage, true);
getWindow().setEnterTransition(fade);
getWindow().getSharedElementEnterTransition().addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
if (!isBackPressed) {
setImageImageView.setVisibility(View.VISIBLE);
ScaleAnimation scaleUp = new ScaleAnimation(0f, 1f, 0f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleUp.setDuration(150);
scaleUp.setFillAfter(true);
setImageImageView.startAnimation(scaleUp);
}
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
} else {
setImageImageView.setVisibility(View.VISIBLE);
ScaleAnimation scaleUp = new ScaleAnimation(0f, 1f, 0f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleUp.setDuration(150);
scaleUp.setFillAfter(true);
setImageImageView.startAnimation(scaleUp);
}
}
@Override
public void onBackPressed() {
isBackPressed = true;
ScaleAnimation scaleUp = new ScaleAnimation(1f, 0f, 1f, 0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleUp.setDuration(150);
scaleUp.setFillAfter(true);
scaleUp.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
setImageImageView.setVisibility(View.INVISIBLE);
ProfileActivity.super.onBackPressed();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
setImageImageView.startAnimation(scaleUp);
}}

activity_profile.xml :

&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
xmlns:tools=&quot;http://schemas.android.com/tools&quot;
android:layout_width=&quot;match_parent&quot;
android:layout_height=&quot;match_parent&quot;&gt;
&lt;com.facebook.drawee.view.SimpleDraweeView
android:id=&quot;@+id/image&quot;
android:layout_width=&quot;160dp&quot;
android:layout_height=&quot;160dp&quot;
android:layout_marginTop=&quot;24dp&quot;
android:transitionName=&quot;imageTransition&quot;
app:actualImageScaleType=&quot;centerCrop&quot;
app:layout_constraintEnd_toEndOf=&quot;parent&quot;
app:layout_constraintHorizontal_bias=&quot;0.5&quot;
app:layout_constraintStart_toStartOf=&quot;parent&quot;
app:layout_constraintTop_toBottomOf=&quot;@+id/appbar&quot;
app:placeholderImage=&quot;@drawable/ic_settings_profile&quot;
app:placeholderImageScaleType=&quot;centerCrop&quot;
app:roundAsCircle=&quot;true&quot; /&gt;
&lt;com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=&quot;@+id/setImage&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_marginEnd=&quot;2dp&quot;
android:layout_marginRight=&quot;2dp&quot;
android:layout_marginBottom=&quot;2dp&quot;
android:clickable=&quot;true&quot;
android:focusable=&quot;true&quot;
android:src=&quot;@drawable/ic_home_camera&quot;
android:tint=&quot;@color/white&quot;
android:visibility=&quot;invisible&quot;
app:backgroundTint=&quot;@color/colorPrimaryLight&quot;
app:fabCustomSize=&quot;48dp&quot;
app:layout_constraintBottom_toBottomOf=&quot;@+id/image&quot;
app:layout_constraintEnd_toEndOf=&quot;@+id/image&quot;
app:rippleColor=&quot;@color/white&quot; /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

syles.xml :

&lt;resources&gt;
&lt;style name=&quot;AppTheme.Base&quot; parent=&quot;Theme.AppCompat.Light.NoActionBar&quot; /&gt;
&lt;style name=&quot;AppTheme&quot; parent=&quot;AppTheme.Base&quot;&gt;
&lt;item name=&quot;colorPrimary&quot;&gt;@color/colorPrimary&lt;/item&gt;
&lt;item name=&quot;colorPrimaryDark&quot;&gt;@color/colorPrimaryDark&lt;/item&gt;
&lt;item name=&quot;colorAccent&quot;&gt;@color/colorAccent&lt;/item&gt;
&lt;/style&gt;
&lt;/resources&gt;

styles.xml (v21) :

&lt;resources&gt;
&lt;style name=&quot;AppTheme.Base&quot; parent=&quot;Theme.AppCompat.Light.NoActionBar&quot;&gt;
&lt;item name=&quot;android:windowActivityTransitions&quot;&gt;true&lt;/item&gt;
&lt;item name=&quot;android:windowEnterTransition&quot;&gt;@transition/explode&lt;/item&gt;
&lt;item name=&quot;android:windowExitTransition&quot;&gt;@transition/explode&lt;/item&gt;
&lt;item name=&quot;android:windowSharedElementEnterTransition&quot;&gt;
@transition/change_image_transform
&lt;/item&gt;
&lt;item name=&quot;android:windowSharedElementExitTransition&quot;&gt;
@transition/change_image_transform
&lt;/item&gt;
&lt;/style&gt;
&lt;/resources&gt;

change_image_transition.xml :

&lt;transitionSet&gt;
&lt;changeImageTransform /&gt;
&lt;changeBounds /&gt;
&lt;/transitionSet&gt;

explode.xml (v21) :

&lt;explode xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
&lt;targets&gt;
&lt;target android:excludeId=&quot;@android:id/statusBarBackground&quot; /&gt;
&lt;target android:excludeId=&quot;@android:id/navigationBarBackground&quot; /&gt;
&lt;target android:excludeId=&quot;@id/appbar&quot; /&gt;
&lt;/targets&gt;
&lt;/explode&gt;

fade.xml (v21) :

&lt;fade xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
&lt;targets&gt;
&lt;target android:excludeId=&quot;@android:id/statusBarBackground&quot; /&gt;
&lt;target android:excludeId=&quot;@android:id/navigationBarBackground&quot; /&gt;
&lt;target android:excludeId=&quot;@id/appbar&quot; /&gt;
&lt;/targets&gt;
&lt;/fade&gt;

答案1

得分: 1

API 26 及以下的版本中有一些不同的内容,因此你可能想考虑将你的动画在 API 26 及以下的版本中替换为另一个(还有其他一些内容,但也许不包括在这个应用程序中,一般情况下你可以查阅安卓官方网站来进行检查)。
因此,简单地说,你的动画可能在 API 26 及以下的大多数设备上都不起作用。
还要考虑在实际设备上进行测试,以确保准确性。

在这里进行操作,并确保在左上角的 API 下拉菜单中选择 26。
https://developer.android.com/reference/android/view/animation/package-summary

祝你好运

英文:

APIs below 26 simple have different stuff so you might want to consider replacing your animation with another in api 26 and below ( and other stuff but maybe not in this application in general you can google and go to andoid official website to check that.
So simply your might not most of the devices which have api 26 and below.
Consider testing on physical devices as well to be sure.

go here and make sure to chose 26 in api drop menu top left.
https://developer.android.com/reference/android/view/animation/package-summary

good luck

答案2

得分: 0

我找到了这个问题的原因和解决方案。问题并不在我在问题中编写的代码上,而是在我在BaseActivity中使用动态语言方面。我实现的动态语言在 Android 7 及以下版本中会在每次onResume方法上重新创建活动。而每次重新创建活动时,过渡动画都不会起作用。为了解决这个问题,我在ProfileActivity中禁用了动态语言。

英文:

I found the cause of this problem and the solution. The problem was not with the code I wrote in the question, but with the use of dynamic language in my BaseActivity. The dynamic language I implemented, in Android 7 and below recreated activity each time on the onResume method. And every time an activity was recreated, the transition animation did not work. To solve this problem, I disabled the dynamic language for my ProfileActivity.

huangapple
  • 本文由 发表于 2020年10月20日 22:47:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/64447634.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定