نحوه استفاده از Shared Element در اندروید

Shared Elementشاید تو یک سری از اپلیکشین های اندرویدی مثل نسخه قبلی گوگل پلی، با shared element برخورد کرده باشید. Shared Elementفک کنم از تصویر بالا کلیات رو فهمیده باشید. Shared Element در واقع یعنی انتقال یک شیء از یک اکتیویتی به یک اکتیویتی دیگر با انیمیشن، در صورتی که اون شیء بین هر دو اکتیویتی مشترک باشه. عنصر مشترک یا Shared Element در واقع در اندروید ۵ معرفی شد. و فقط گوشی های هوشمند با اندروید ۵ به بالا از اون پشتیبانی می‌کنند.در ادامه یک پروژه کوچیک با هم‌دیگه انجام می‌دیم که بهتر متوجه بشید.عنصر مشترکامیدوارم با کلیات توسعه اندروید آشنا باشید. چون نمی‌خوام زیاد وارد جزئیات بشم. تو این پروژه ما یک اسپلش اسکرین یا صفحه معرفی و یک صفحه اصلی داریم. داخل صفحه معرفی، لوگو برنامه هست که بعد از سه ثانیه صفحه معرفی تموم میشه و صفحه اصلی باز می‌شه. صفحه اصلی شامل لوگو برنامه و یک متن هست. بیاید شروع کنیم.یک پروژه جدید با یک اکتیویتی خالی در اندروید استدیو ایجاد کنید. اسمشم بزارید Shared Element یا هر چی که دوست داشتید.حالا توی پروژه یک اکتیویتی دیگه به اسم SplashScreenActivity ایجاد کنید.توی layout کلاس SplashScreenActivity یک امیج ویو بزارید. مثله کد زیر:<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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" android:background="@android:color/white" tools:context=".SplashScreenActivity"> <ImageView android:id="@+id/logo" android:layout_width="80dp" android:layout_height="80dp" android:tint="@color/colorAccent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" android:src="@drawable/ic_shop" android:transitionName="logo" <!-- here is the transitionName for shared Element--> /> </android.support.constraint.ConstraintLayout>می‌رسیم به قسمت مهم ماجرا. شما باید به غیر از تعریف ایدی برای ابجکت یا المنت‌تون یک ایدی هم به عنوان اسم عنصر مشترک اختصاص بدید. این ایدی با transitioneName تعریف می‌شه. همون قسمتی که با توضیح مشخص کردم. من transitioneName رو برابر "logo" قرار دادم. شما می‌تونید هر چی دلتون خواست بزارید. خروجی مورد انتظار چیزی شبیه به تصویر پایین باید باشه:activity_splash_screen.xmlحالا در layout اکتیویتی اول یعنی کلاس MainActivity یک تکست‌ویو و یک امیج‌ویو برای نمایش لوگو و متن ایجاد کنید. شبیه به کد زیر:<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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" tools:context=".MainActivity"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/logo" android:layout_width="80dp" android:layout_height="80dp" android:layout_marginTop="16dp" android:src="@drawable/ic_shop" android:tint="@color/colorAccent" android:transitionName="logo" /> <!--here is the transition name. --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is a project for learn how to use Shared Element." android:textAlignment="center" android:textColor="@android:color/background_dark" android:textSize="18dp" /> </LinearLayout> </android.support.constraint.ConstraintLayout>اینجا هم قسمت مهم همون transitionName هست. در اینجا باید همون اسمی رو اختصاص بدید که در layout کلاس SplashScreenActivity به امیج ویو اختصاص داده بودید. خروجی باید چیزی شبیه به تصویر پایین باشه:activity_main.xmlخوب کارمون با قسمت طراحی تموم شد. برگردیم سراغ اکتیویتی ها. کلاس SplashScreenActivity رو باز کنید. در اینجا می‌خوایم یک تایمر بزاریم که بعد از دو ثانیه اکتیویتی اصلی اجرا بشه.من از ترید(thread) استفاده کردم. ولی شما می‌تونید فقط یک تایمر بزارید. طبق کد زیر پیش برید:Thread splash = new Thread() { @Override public void run() { try { sleep(2000); runOnUiThread(new Runnable() { @Override public void run() { } }); } catch (Exception e) { } } };امیج ویویی که لوگو داخل اون قرار داره رو شناسایی کنید:ImageView imageViewLogo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash_screen); imageViewLogo=findViewById(R.id.logo); }حالا داخل متد run کدهای زیر رو وارد کنید. این کدها برای اجرای اکتیویتی اصلی هستن:Intent i = new Intent(SplashScreenActivity.this, MainActivity.class); ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(SplashScreenActivity.this, imageViewLogo, "logo"); startActivity(i, optionsCompat.toBundle()); finish();چیزی که در اینجا مهمه این خط از کد هست: ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(SplashScreenActivity.this, imageViewLogo, "logo"); در اینجا ما به اینتنت می‌فهمونیم هم قراره یک اکتیویتی رو اجرا کنه و هم اینکه یک چیزی به اون ارسال کنه. اون چیز در واقع makeSceneTransitionAnimation هست. این تابع در اندروید ۵ معرفی شد. با این تابع هست که امکان Shared Element مهیا می‌شه. البته استفاده های دیگه‌ای هم داره که من دقیقا نمی‌دونم.قسمت اول این تابع اسم اون اکتیویتی هست که الان داخلش هستیم. قسمت دوم اسم اون المنتی هست که ما می‌خوایم به عنوان عنصر مشترک استفاده کنیم. که در اینجا امیج ویویی هست که لوگوی برنامه رو نشون میده. قسمت سوم اسم transitionName هست که قبلا به هر دو عنصر اختصاص دادیم. در اینجا من از "logo" استفاده کرده بودم. شما اگر ایدی دیگه‌ای نوشتید همون رو وارد کنید.حالا تایمر یا تردی که نوشتید رو تو قسمت oncreate اکتیوتی اجرا کنید. کدهای کلاس SplashScreenActivity باید چیزی شبیه به تصویر پایین باشه:SplashScreenActivity.javaحالا طبق تصویر زیر فایل styles.xml رو باز کنید:styles.xmlو کد زیر رو در داخل بلوک AppTheme وارد کنید:<item name="android:windowContentTransitions">true</item>این خط از کد به هر دو اکتیویتی میفهمونه که دارن از shared element استفاده می‌کنند.حالا برنامه رو اجرا کنید:عه! پس چرا اینجوری شد؟!!!!!در واقع هر اکتیویتی بعد از تموم شدن دارای یک انیمیشن هست. حالا این انیمیشن با انیمیشنی که ما درست کردیم تداخل پیدا کرده. (آخرسر اندروید با این دردسرهاش مارو می‌کشه)برای حل این مشکل یک راه حل ساده داریم. شما باید سه تا کار انجام بدید:۱. اول اینکه خط کد زیر رو به قسمت onCreate در کلاس SplashScreenActivity اضافه کنید:getWindow().setExitTransition(null);فک کنم کد واضح باشه. این خط از کد انیمیشن خروج اکتیویتی رو حذف می‌کنه.۲. خط کد زیر رو هم به قسمت onCreate در کلاس MainActivity اضافه کنید:getWindow().setEnterTransition(null);این کد هم انیمیشن ورود رو حذف می‌کنه.۳. به جای اینکه بلافاصله بعد از شروع اکتیویتی جدید، اکتیویتی قبلی رو تموم یا finish کنیم، بیایم تو تابع آن_ستاپ اونو finish کنیم. امیدوارم فهمیده باشید. بیاید یک نگاه به تصویر پایین بندازیم:SplashScreenActivity,javaدر کلاس SplashScreenActivity من اومدم تابع آن_ستاپ رو override کردم و در اون اکتیویتی رو finish کردم. یعنی وقتی چرخه اکتیویتی رفت روی آن_ستاپ، بعد از اون بیاد اکتیویتی رو تموم کنه. اینجوری مشکل تداخل هم حل میشه. البته این یک راه حل بود که خودم پیدا کردم. شاید شما راه حل بهتری داشته باشید.حالا برنامه رو اجرا کنید:مشکل حل شد. امیدوارم که قابل فهم بوده باشه. تا جایی که تونستم سعی کردم ساده توضیح بدم. سورس پروژه رو هم می‌تونید از طریق لیک زیر از گیت هاب دانلود و استفاده کنید. https://github.com/shadmanadman/SharedElement/