android-如何从标记位图开始共享元素过渡?
Posted
技术标签:
【中文标题】android-如何从标记位图开始共享元素过渡?【英文标题】:android-how to start a shared element transition from marker bitmap? 【发布时间】:2021-06-03 00:02:54 【问题描述】:我使用自定义位图作为地图上标记的图标。当用户单击任何标记时,我希望有一个与单击的位图相对应的视图并将其作为共享元素添加到片段转换中。但我看不到任何从标记中检索位图的方法。那么如何从标记开始共享元素过渡呢?
【问题讨论】:
【参考方案1】:简而言之:使用setTag()/getTag()
方法在标记对象中保存/恢复位图,并使用此位图作为共享元素的附加ImageView
用于“地图”和“细节”片段之间的转换:
TLDR;
如果您“使用自定义位图作为标记的图标”,您可以使用setTag()
方法将它们存储在Marker
对象中,然后在onMarkerClick(Marker marker)
方法中检索标记的图标位图。但是位图对于shared element transitions 来说是不够的,因为需要View
类的对象来执行它。因此,您需要创建额外的View
(例如ImageView
)并将其用作“地图”和“详细信息”片段之间转换的共享元素。
一般来说,您应该:
应用程序启动时(MainActivity
):
-
使用
ImageView
创建“map”片段以执行共享元素转换;
使用对应的ImageView
创建“详细信息”片段以执行共享元素转换;
创建共享元素过渡动画;
-
只需将位图保存在标记对象中即可。
在“地图”片段中:
-
从标记对象中检索保存的位图;
将检索到的位图设置为共享
ImageView
;
调整ImageView
的大小并将其移动到正好位于标记图标上;
隐藏标记图标并使用标记图标而不是标记显示ImageView
;
通过参数将标记位图(和例如描述)放入“详细信息”片段;
创建并启动FragmentTransaction
;
在“细节”片段中:
-
从参数中获取标记位图和描述,并在相应的
ImageView
和TextView
上显示它们;
-
等待过渡动画结束并显示标记/隐藏
ImageView
。
其中最具挑战性的部分是在“地图”片段内创建共享视图,因为SupportMapFragment
没有“来自盒子”的此类元素。因此,您需要创建自定义 CustomSupportMapFragment
以扩展 SupportMapFragment
并为共享元素转换添加额外的 ImageView
:
public class CustomSupportMapFragment extends SupportMapFragment
private Bitmap mBitmap;
private float mY;
private float mX;
private RelativeLayout mRelativeLayout;
private ImageView mSharedImageView;
private Marker mMarker;
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
View root = super.onCreateView(inflater, container, savedInstanceState);
mRelativeLayout = new RelativeLayout(root.getContext());
mRelativeLayout.addView(root, new RelativeLayout.LayoutParams(-1, -1));
mSharedImageView = new ImageView(root.getContext());
mSharedImageView.setId(View.generateViewId());
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
mSharedImageView.setLayoutParams(layoutParams);
mSharedImageView.setTransitionName("sharedImageView");
mRelativeLayout.addView(mSharedImageView);
return mRelativeLayout;
@Override
public void onStart()
super.onStart();
if (mBitmap != null)
mSharedImageView.setImageBitmap(mBitmap);
mSharedImageView.setX(mX);
mSharedImageView.setY(mY);
@Override
public void onStop()
super.onStop();
mBitmap = ((BitmapDrawable)mSharedImageView.getDrawable()).getBitmap();
mX = mSharedImageView.getX();
mY = mSharedImageView.getY();
public void setSharedMarker(Marker marker)
mMarker = marker;
public void setSharedViewInitialPosition(float x, float y)
mSharedImageView.setX(x);
mSharedImageView.setY(y);
public void setSharedBitmap(Bitmap bitmap)
mSharedImageView.setImageBitmap(bitmap);
public ImageView getSharedView()
return mSharedImageView;
public void showMarker()
if (mMarker != null) mMarker.setVisible(true);
“详细信息”片段可以是这样的典型:
public class DetailsFragment extends Fragment
private ImageView mImageView;
private TextView mTextView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
View view = inflater.inflate(R.layout.fragment_details, container, false);
mImageView = view.findViewById(R.id.picture_iv);
mTextView = view.findViewById(R.id.details_tv);
Bundle bundle = getArguments();
if (bundle != null)
Bitmap bitmap = getArguments().getParcelable("image");
mImageView.setImageBitmap(bitmap);
String description = getArguments().getString("description");
mTextView.setText(description);
return view;
布局如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_ android:layout_>
<ImageView
android:id="@+id/picture_iv"
android:layout_
android:layout_
android:transitionName="sharedImageView"
android:layout_centerInParent="true"/>
<TextView
android:id="@+id/details_tv"
android:layout_below="@+id/picture_iv"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
android:textSize="28dp"
android:text="Description"/>
</RelativeLayout>
注意!在所有“地图”和“详细信息”片段视图中都需要相同的 "sharedImageView"
转换名称。
而MainActivity
应该实现标记点击处理逻辑:
public class MainActivity extends AppCompatActivity
static final LatLng KYIV = new LatLng(50.450311, 30.523730);
static final LatLng DNIPRO = new LatLng(48.466111, 35.025278);
private GoogleMap mGoogleMap;
private CustomSupportMapFragment mapFragment;
private DetailsFragment detailsFragment;
public class DetailsEnterTransition extends TransitionSet
public DetailsEnterTransition()
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
public class DetailsExitTransition extends TransitionSet
public DetailsExitTransition(final CustomSupportMapFragment mapFragment)
setOrdering(ORDERING_TOGETHER);
addTransition(new ChangeBounds()).
addTransition(new ChangeTransform()).
addTransition(new ChangeImageTransform());
addListener(new TransitionListener()
@Override
public void onTransitionStart(Transition transition)
@Override
public void onTransitionEnd(Transition transition)
if (mapFragment != null)
mapFragment.showMarker();
mapFragment.setSharedBitmap(null);
@Override
public void onTransitionCancel(Transition transition)
@Override
public void onTransitionPause(Transition transition)
@Override
public void onTransitionResume(Transition transition)
);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// create "map" fragment
mapFragment = new CustomSupportMapFragment();
// create "details" fragment and transitions animations
detailsFragment = new DetailsFragment();
detailsFragment.setSharedElementEnterTransition(new DetailsEnterTransition());
detailsFragment.setSharedElementReturnTransition(new DetailsExitTransition(mapFragment));
// show "map" fragment
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, mapFragment, "map")
.commit();
// get GoogleMap object
mapFragment.getMapAsync(new OnMapReadyCallback()
@Override
public void onMapReady(GoogleMap googleMap)
mGoogleMap = googleMap;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_kyiv);
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, 200, 250, false);
Marker marker = mGoogleMap.addMarker(new MarkerOptions()
.position(KYIV)
.icon(BitmapDescriptorFactory.fromBitmap(resizedBitmap))
.title("Kyiv"));
marker.setTag(resizedBitmap); // save bitmap1 as tag of marker object
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLng(KYIV));
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_dnipro);
resizedBitmap = Bitmap.createScaledBitmap(bitmap, 200, 250, false);
marker = mGoogleMap.addMarker(new MarkerOptions()
.position(DNIPRO)
.icon(BitmapDescriptorFactory.fromBitmap(resizedBitmap))
.title("Dnipro"));
marker.setTag(resizedBitmap); // save bitmap2 as tag of marker object
mGoogleMap.animateCamera(CameraUpdateFactory.newLatLng(KYIV));
mGoogleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener()
@Override
public boolean onMarkerClick(Marker marker)
// retrieve bitmap for marker
Bitmap sharedBitmap = (Bitmap)marker.getTag();
// determine position of marker and shared element on screen
Projection projection = mGoogleMap.getProjection();
Point viewPosition = projection.toScreenLocation(marker.getPosition());
final float x = viewPosition.x - sharedBitmap.getWidth() / 2.0f;
final float y = viewPosition.y - sharedBitmap.getHeight();
// show shared ImageView and hide marker
mapFragment.setSharedMarker(marker);
mapFragment.setSharedBitmap(sharedBitmap);
mapFragment.setSharedViewInitialPosition(x, y);
mapFragment.getSharedView().setVisibility(View.VISIBLE);
mapFragment.getSharedView().invalidate();
marker.setVisible(false);
// prepare data for "details" fragment
Bundle bundle = new Bundle();
bundle.putParcelable("image", sharedBitmap);
bundle.putString("description", marker.getTitle());
detailsFragment.setArguments(bundle);
// create and start shared element transition animation
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.addSharedElement(mapFragment.getSharedView(), mapFragment.getSharedView().getTransitionName());
ft.replace(R.id.container, detailsFragment, "details");
ft.addToBackStack("details");
ft.commit();
return true; // prevent centring map on marker
);
);
就是这样。注意:这不是功能齐全的商业代码,只是说明。
【讨论】:
以上是关于android-如何从标记位图开始共享元素过渡?的主要内容,如果未能解决你的问题,请参考以下文章