【发布时间】:2020-10-20 14:47:26
【问题描述】:
我尝试创建像WhatsApp 这样的个人资料偏好,其中个人资料照片与个人资料活动共享。我使用了以下在 Android 8 及更高版本中正常工作的代码。但在 Android 7 及更低版本中,共享元素动画不起作用。 This 是 API 21 测试的结果。
不相关的代码已被删除。
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">
<LinearLayout
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?attr/dividerColor"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:singleLine="true"
android:text="Okay, Bye."
android:textAlignment="gravity"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:transitionName="macTransition"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@android:id/title"
app:layout_constraintTop_toBottomOf="@android:id/title" />
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:singleLine="true"
android:text="Robert Downey"
android:textAlignment="gravity"
android:textColor="@android:color/black"
android:textSize="22sp"
android:transitionName="nameTransition"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@android:id/summary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@android:id/icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@android:id/icon"
android:layout_width="67dp"
android:layout_height="67dp"
android:layout_marginStart="14dp"
android:layout_marginLeft="14dp"
android:layout_marginTop="17dp"
android:layout_marginBottom="17dp"
android:src="@drawable/avatar_contact"
android:transitionName="imageTransition"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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() {
if (Build.VERSION.SDK_INT >= 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:
<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">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginTop="24dp"
android:transitionName="imageTransition"
app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appbar"
app:placeholderImage="@drawable/ic_settings_profile"
app:placeholderImageScaleType="centerCrop"
app:roundAsCircle="true" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/setImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
android:layout_marginBottom="2dp"
android:clickable="true"
android:focusable="true"
android:src="@drawable/ic_home_camera"
android:tint="@color/white"
android:visibility="invisible"
app:backgroundTint="@color/colorPrimaryLight"
app:fabCustomSize="48dp"
app:layout_constraintBottom_toBottomOf="@+id/image"
app:layout_constraintEnd_toEndOf="@+id/image"
app:rippleColor="@color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>
syles.xml:
<resources>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="AppTheme" parent="AppTheme.Base">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
styles.xml (v21):
<resources>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowSharedElementEnterTransition">
@transition/change_image_transform
</item>
<item name="android:windowSharedElementExitTransition">
@transition/change_image_transform
</item>
</style>
</resources>
change_image_transition.xml:
<transitionSet>
<changeImageTransform />
<changeBounds />
</transitionSet>
explode.xml (v21):
<explode xmlns:android="http://schemas.android.com/apk/res/android">
<targets>
<target android:excludeId="@android:id/statusBarBackground" />
<target android:excludeId="@android:id/navigationBarBackground" />
<target android:excludeId="@id/appbar" />
</targets>
</explode>
fade.xml (v21):
<fade xmlns:android="http://schemas.android.com/apk/res/android">
<targets>
<target android:excludeId="@android:id/statusBarBackground" />
<target android:excludeId="@android:id/navigationBarBackground" />
<target android:excludeId="@id/appbar" />
</targets>
</fade>
【问题讨论】:
-
Telegram 有类似的过渡动画。也许你可以检查他们的源代码。
标签: java android android-animation android-transitions