Android: Simple understanding and use of shared elements of Android study notes

1. Basic concepts

Andriod 5.0 and later began to support shared element animation. Two Activities or fragments can share certain controls. For example, when Activity A jumps to Activity B, a certain control of A can automatically move to the position of the corresponding control of B, resulting in animation.

insert image description here

2. Basic use

1. Activity to Activity jump implementation

1.1. Steps to use

1. Assign a unique transition name to each shared element view.

2. makeSceneTransitionAnimationAdd the shared element view and the transition name corresponding to the shared element view after switching.

3. Page jump.

1.2. Case Description

1. The first step is to set transitionNamethe attribute of the view in ActivityA and ActivityB. The attribute value is customized, which is a string.

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@mipmap/timg"
        android:id="@+id/share_pic"
        android:transitionName="@string/share_pic_str"
        android:scaleType="fitXY"/>
</LinearLayout>
 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@mipmap/timg"
        android:id="@+id/share_pic"
        android:transitionName="@string/share_pic_str"
        android:scaleType="fitXY" />

transitionNameYou can also set the properties manually

ShareAnimatorActivity

ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
                    MainActivity.this,

                    // Now we provide a list of Pair items which contain the view we can transitioning
                    // from, and the name of the view it is transitioning to, in the launched activity
                    new Pair<>(view.findViewById(R.id.imageview_item),
                            R.string.share_pic_str));

SecondShareAnimActivity

ViewCompat.setTransitionName(mImageView, R.string.share_pic_str);

2. Set the intent jump

Intent intent = new Intent(ShareAnimatorActivity.this,SecondShareAnimActivity.class);

Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(ShareAnimatorActivity.this,shareImg,getString(R.string.share_pic_str)).toBundle();

startActivity(intent,bundle);

makeSceneTransitionAnimation()Parameter explanation:

  • Activity is the Activity that initiates the jump,
  • shareElement is the id of the shared control,
  • sharedElementName is the string defined in the first step.
  • This method only supports sharing a single control.

The details are as follows: The Activity that initiates the jump:

public class ShareAnimatorActivity extends AppCompatActivity {
    
    
    private ImageView shareImg;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_share_animator_main);
        shareImg = (ImageView) findViewById(R.id.share_pic);
        shareImg.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                Log.d(TAG,"onClick");
                Intent intent = new Intent(ShareAnimatorActivity.this,SecondShareAnimActivity.class);
                Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(ShareAnimatorActivity.this,shareImg,getString(R.string.share_pic_str)).toBundle();
                startActivity(intent,bundle);
            }
        });
    }

Activity code to jump to:

public class SecondShareAnimActivity extends AppCompatActivity {
    
    

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

2. Fragment to Fragment jump implementation

2.1. Steps to use

1. Set the transitionName attribute.
The Transition framework needs a method that can associate the View of the current page with the page to be jumped to. The following are two methods for adding a Transition Name to the View:

  • ViewCompat.setTransitionName() can be called directly in the code, or
    setTransitionName() can be called directly in devices with Android Lolipop system version or higher.
  • In the layout XML file, set the android:transitionName attribute directly.

2. Set Fragment Transaction

getSupportFragmentManager()
        .beginTransaction()
        .addSharedElement(sharedElement, transitionName)
        .replace(R.id.container, newFragment)
        .addToBackStack(null)
        .commit();

Just call addSharedElement()to associate the View you want to share with the Fragment.

  • The View passed as a parameter to addSharedElement() is the View in the first Fragment that you want to
    share with the Fragment that is about to jump.
  • The TransitionName required to be filled in by the second parameter of this method is the Transition Name of the shared View in the redirected Fragment
    .
    • For example, if the transitionName of the shared View in the current Fragment is "foo", and the transitionName of the shared View in the Fragment to be jumped
      to is "bar", then the second parameter passed in addSharedElement() should be "bar".

3. Specify the method of adding Transition animation as a shared element:

Call setSharedElementEnterTransition()Specifies how the View transitions from the first Fragment to the View in the jump Fragment.

Call setSharedElementReturnTransition()Specifies how the View returns to the first Fragment from the Jump Fragment after the user clicks the Back button.

Remember, to return to Transition, you need to call the corresponding method in the jump Fragment, otherwise you will not get the effect you want.

setEnterTransitionOf course, you can also set Transition transition animation for any non-shared View, but the calling API has changed: just call (), setExitTransition(), setReturnTransition(), and setReenterTransition() methods in the corresponding Fragment.

2.2. Case Description

1、TestShareActivity

public class TestShareActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.share_test);
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FragmentA.class.getName());
        if (fragment == null) {
    
    
            fragment = FragmentA.newInstance();
            getSupportFragmentManager().beginTransaction().add(R.id.share_test,
                    fragment,
                    FragmentA.class.getName())
                    .commit();
        }
    }
}

share_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <FrameLayout
        android:id="@+id/share_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

2、ShareElementsFragment1

public class FragmentA extends Fragment {
    
    
    public static final String TAG = FragmentA.class.getSimpleName();
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
    
        return inflater.inflate(R.layout.fragment_a, container, false);
    }

    public static FragmentA newInstance() {
    
    
        return new FragmentA();
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    
    
        super.onActivityCreated(savedInstanceState);
        final ImageView imageView = (ImageView) getView().findViewById(R.id.imageView);
        getActivity().findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                Fragment fragmentB = getFragmentManager().findFragmentByTag(TAG);
                if (fragmentB == null) fragmentB = FragmentB.newInstance();
                getFragmentManager()
                        .beginTransaction()
                        .addSharedElement(imageView,
                                ViewCompat.getTransitionName(imageView))
                        .addToBackStack(TAG)
                        .replace(R.id.share_test, fragmentB)
                        .commit();
            }
        });
    }
}

fragment_a.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:transitionName="simple transition name"
        android:background="@color/color_green" />

    <Button
        android:id="@+id/btn_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        android:textSize="16sp" />

</LinearLayout>

3、ShareElementsFragment2

public class FragmentB extends Fragment {
    
    

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
    
        return inflater.inflate(R.layout.fragment_b, container, false);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    
    
            setSharedElementEnterTransition(
                    TransitionInflater.from(getContext())
                            .inflateTransition(android.R.transition.move));
        }
    }

    public static FragmentB newInstance() {
    
    
        return new FragmentB();
    }

}

fragment_b.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:transitionName="simple transition name"
        android:background="@color/color_green"
        android:layout_gravity="center_horizontal"/>

</LinearLayout>

3. Navigation + shared elements + recyclerview jump implementation

3.1. Steps to use

  • 1. Set transitionNameproperties
  • 2. Define FragmentNavigatorExtrasparameters
  • 3. findNavController().navigatePage jump
// 第一步
holder.item.findViewById<TextView>(R.id.user_name_text).transitionName = myDataset[position]

holder.item.findViewById<ImageView>(R.id.user_avatar_image).transitionName =
            (position % listOfAvatars.size).toString()
//第二步
val extras = FragmentNavigatorExtras(
                holder.item.findViewById<ImageView>(R.id.user_avatar_image)
                        to (position % listOfAvatars.size).toString(),
                holder.item.findViewById<TextView>(R.id.user_name_text)
                        to myDataset[position]
            )
            holder.item.findNavController().navigate(
                    R.id.action_leaderboard_to_userProfile,
                bundle,
                null,
                extras
            )
//第三步
holder.item.findNavController().navigate(
                    R.id.action_leaderboard_to_userProfile,
                bundle,
                null,
                extras
            )

3.2. Case Description

Leaderboard

class Leaderboard : Fragment() {
    
    

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
    
    
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_leaderboard, container, false)

        val viewAdapter = MyAdapter(Array(6) {
    
     "Person ${it + 1}" })

        view.findViewById<RecyclerView>(R.id.leaderboard_list).run {
    
    
            // use this setting to improve performance if you know that changes
            // in content do not change the layout size of the RecyclerView
            setHasFixedSize(true)

            // specify an viewAdapter (see also next example)
            adapter = viewAdapter

        }
        return view
    }
}

class MyAdapter(private val myDataset: Array<String>) :
    RecyclerView.Adapter<MyAdapter.ViewHolder>() {
    
    
    
    class ViewHolder(val item: View) : RecyclerView.ViewHolder(item)

    // Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(parent: ViewGroup,
                                    viewType: Int): ViewHolder {
    
    
        // create a new view
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.list_view_item, parent, false)

        return ViewHolder(itemView)
    }

    // Replace the contents of a view (invoked by the layout manager)
    @SuppressLint("CutPasteId")
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    
    
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.item.findViewById<TextView>(R.id.user_name_text).text = myDataset[position]
        holder.item.findViewById<TextView>(R.id.user_name_text).transitionName = myDataset[position]

        holder.item.findViewById<ImageView>(R.id.user_avatar_image)
                .setImageResource(listOfAvatars[position % listOfAvatars.size])
        holder.item.findViewById<ImageView>(R.id.user_avatar_image).transitionName =
            (position % listOfAvatars.size).toString()
        holder.item.setOnClickListener {
    
    
            val bundle = Bundle()
            bundle.apply {
    
    
                putString(USERNAME_KEY, myDataset[position])
                putInt(USER_AVATAR_KEY, position % listOfAvatars.size)
            }
            val extras = FragmentNavigatorExtras(
                holder.item.findViewById<ImageView>(R.id.user_avatar_image)
                        to (position % listOfAvatars.size).toString(),
                holder.item.findViewById<TextView>(R.id.user_name_text)
                        to myDataset[position]
            )
            holder.item.findNavController().navigate(
                    R.id.action_leaderboard_to_userProfile,
                bundle,
                null,
                extras
            )
    }

    // Return the size of your dataset (invoked by the layout manager)
    override fun getItemCount() = myDataset.size

    companion object {
    
    
        const val USERNAME_KEY = "userName"
        const val USER_AVATAR_KEY = "userAvatar"

    }
}

public val listOfAvatars = listOf(
    R.drawable.avatar_1_raster,
    R.drawable.avatar_2_raster,
    R.drawable.avatar_3_raster,
    R.drawable.avatar_4_raster,
    R.drawable.avatar_5_raster,
    R.drawable.avatar_6_raster
)

fragment_leaderboard.xml

<?xml version="1.0" encoding="utf-8"?>

<androidx.recyclerview.widget.RecyclerView
    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/leaderboard_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:context="com.example.android.navigationadvancedsample.listscreen.Leaderboard"
    tools:listitem="@layout/list_view_item"/>

insert image description here

list_view_item.xml

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:background="@android:color/background_light"
    tools:layout_editor_absoluteY="81dp">


    <ImageView
        android:id="@+id/user_avatar_image"
        android:layout_width="69dp"
        android:layout_height="69dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="@drawable/avatar_5_raster"
        app:srcCompat="@drawable/circle"
        tools:background="@tools:sample/avatars"
        tools:srcCompat="@drawable/circle"
        android:contentDescription="@string/profile_image"/>


    <TextView
        android:id="@+id/user_name_text"
        android:layout_width="228dp"
        android:layout_height="28dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text=""
        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
        android:textSize="22sp"
        app:layout_constraintBottom_toTopOf="@+id/user_points_text"
        app:layout_constraintStart_toEndOf="@+id/user_avatar_image"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="@tools:sample/full_names"
        android:layout_marginLeft="8dp" />

    <TextView
        android:id="@+id/user_points_text"
        android:layout_width="228dp"
        android:layout_height="21dp"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="8dp"
        android:text="@string/user_points"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/user_avatar_image"
        app:layout_constraintTop_toBottomOf="@+id/user_name_text"
        tools:text="10,000 pts"
        android:layout_marginLeft="8dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

insert image description here

UserProfile

class UserProfile : Fragment() {
    
    

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
    
    


        val view = inflater.inflate(R.layout.fragment_user_profile, container, false)

        val name = arguments?.getString(USERNAME_KEY) ?: "Ali Connors"
        val avatarPosition = arguments?.getInt(USER_AVATAR_KEY) ?: 0

        view.findViewById<TextView>(R.id.profile_user_name).transitionName = name
        view.findViewById<TextView>(R.id.profile_user_name).text = name

        view.findViewById<ImageView>(R.id.profile_pic).transitionName = avatarPosition.toString()
        view.findViewById<ImageView>(R.id.profile_pic)
            .setImageResource(listOfAvatars[avatarPosition])
        return view
    }

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        sharedElementEnterTransition = TransitionInflater.from(requireContext())
            .inflateTransition(android.R.transition.move)
    }
}

fragment_user_profile.xml

<?xml version="1.0" encoding="utf-8"?>

<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/profiler_constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent"
    tools:context="com.example.android.navigationadvancedsample.listscreen.UserProfile"
    tools:layout_editor_absoluteY="81dp">

    <ImageView
        android:id="@+id/profile_pic"
        android:layout_width="240dp"
        android:layout_height="240dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="64dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="@drawable/avatar_6_raster"
        app:srcCompat="@drawable/purple_frame"
        tools:background="@tools:sample/avatars[6]"
        tools:src="@drawable/purple_frame" />

    <View
            android:id="@+id/user_data_card"
            android:layout_width="0dp"
            android:layout_height="141dp"
            android:layout_marginTop="64dp"
            android:background="@drawable/rounded_rect"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/profile_pic"
            app:layout_constraintVertical_bias="1.0"/>

    <TextView
        android:id="@+id/profile_user_name"
        android:gravity="center_horizontal"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        android:text="@string/profile_name_1"
        android:textAlignment="center"
        android:textColor="@color/colorAccent"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/user_data_card" />

    <include
        layout="@layout/user_card"
        android:layout_width="0dp"
        android:layout_height="120dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="24dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/profile_user_name"/>

</androidx.constraintlayout.widget.ConstraintLayout>

insert image description here

reference

1. Add animation transition effects between destinations
2. Android high-level transition animation-ShareElement complete strategy

Guess you like

Origin blog.csdn.net/JMW1407/article/details/125736468
Recommended