Kullanıcı arayüz tasarımında küçük dokunuşlar ile müşterinin dikkatini çeken görsellikler sağlayabilirsiniz. Mobil tasarım da Material Design yapısını kullanarak yazılımcıyı yormadan etkileyici tasarımlar çıkartabilirsiniz.
Bu makale serisinde, Android uygulama da UI öğeleri arasındaki geçişleri farklı animasyon görünümleri ile sağlayan Material motion özelliklerini örnekleyeceğim. İlk serimizde Material motion’ın Shared axis özelliğini anlatacağım.
Shared axis
Kullanıcının uygulamadaki UI öğeleri arasındaki geçişlerinde, öğe görünümü soluklaştırmasıyla bir geçiş animasyonu sağlar. Shared axis X,Y ve Z eksenlerinde 3 farklı yönde animasyonlu geçiş sağlamaya olanak tanır.
Shared axis özelliğini X ekseninde kullanarak örneklediğim projenin görüntüsü;
Projemin kodlarına github linkinden hızlıca ulaşabilirsiniz.
Bu örneği uygulayabilmek için işlemleri adım adım yapalım.
1-Gerekli Kütüphanelerin Yüklenmesi
Projemin app dizinin altındaki build.gradle dosyasını açıyoruz. Dependencies kod bloklarının arasına aşağıdaki kodları yerleştirerek navigation ve material kütüphanelerini yüklüyoruz.
1 2 3 |
implementation('androidx.navigation:navigation-fragment-ktx:2.3.1') implementation('androidx.navigation:navigation-ui-ktx:2.3.1') implementation('com.google.android.material:material:1.3.0-alpha03') |
2- Kotlin kodları
İlk önce ana sınıfımız olan MainActivity sınıfından bahsetmek istiyorum. Bu sınıfta, slide’larda kullanılan resim ve yazıları arraylist yapılarına ekledik ve oluşturduğumuz WalkthroughFragment sınıfına bu değerleri atadık. Aynı zamanda SharedAxis özelliğinin hangi eksende ve slide geçiş sürelerinin ne kadar sürede olacağını hazırlayan metodu hazırladım. Son olarak next ve back butonlarına işlevsellik verildi. Daha detaylı açıklama kodlardan erişebilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.fragment.app.commit import androidx.lifecycle.* import com.google.android.material.button.MaterialButton import com.google.android.material.tabs.TabLayout import com.google.android.material.transition.MaterialSharedAxis private const val COUNT = 3 class MainActivity : AppCompatActivity() { lateinit var tabLayout: TabLayout lateinit var backButton: MaterialButton lateinit var nextButton: MaterialButton private var selected = MutableLiveData<Int>().apply { value = 0 } //slide'daki başlık yazılarını arraylist atadık private val titles by lazy { arrayListOf( getString(R.string.upload_your_photos), getString(R.string.share), getString(R.string.invite_friends) ) } //slide'daki açıklama yazılarını arraylist atadık private val bodies by lazy { arrayListOf( getString(R.string.upload_artistic_photos), getString(R.string.share_work_social_networks), getString(R.string.enjoy_rewards) ) } //slide resimlerini arraylist atadık private val images = arrayListOf( R.drawable.undraw_drag, R.drawable.undraw_social_sharing, R.drawable.undraw_winners ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.walkthrough_activity) //arayüz elamanlarının tanımlanması tabLayout = findViewById(R.id.tab_layout) backButton=findViewById(R.id.back_button) nextButton=findViewById(R.id.next_button) //slide'daki gösterilecek öğelerin değerlerini WalkthroughFragment sınıfına gönderiyoruz selected.value?.let { val fragment = WalkthroughFragment.newInstance( titles[it], bodies[it], images[it] ) //WalkthroughFragment sınıfı FragmentContainerView yükleniyor supportFragmentManager.commit { add(R.id.fragment_container, fragment, FRAGMENT_TAG) } } setDotsTabLayout() setClickListeners() setSelectedObserver() } //slide gösterimleri için COUNT değeri kadar tab eklenmesi private fun setDotsTabLayout() { repeat(COUNT) { tabLayout.addTab(tabLayout.newTab()) } tabLayout.touchables.forEach { it.isEnabled = false } } //slide'lar arasında önceki ve sonraki slide'lara geçiş yapmanız için //seçim değerinde değişiklik sağlama private fun setClickListeners() { backButton.setOnClickListener { selected.value?.let { selected.value = it - 1 } selectFragment(forward = false) } nextButton.setOnClickListener { selected.value?.let { selected.value = it + 1 } selectFragment(forward = true) } } private fun setSelectedObserver() { selected.observe( this, Observer { nextButton.isEnabled = it < COUNT - 1 backButton.isEnabled = it > 0 tabLayout.apply { selectTab(getTabAt(it)) } } ) } //SharedAxis özelliğinin hangi eksende ve slide geçiş sürelerinin ne kadar sürede olacağını hazırlayan metod private fun buildTransition(forward: Boolean) = MaterialSharedAxis(MaterialSharedAxis.X, forward).apply { duration = 500 } private fun selectFragment(forward: Boolean) { selected.value?.let { val previousFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) //buildTransition metodunun açıklaması üstte bulunmaktadır previousFragment?.exitTransition = buildTransition(forward) val fragment = WalkthroughFragment.newInstance( titles[it], bodies[it], images[it] ) fragment.enterTransition = buildTransition(forward) supportFragmentManager.commit { replace(R.id.fragment_container, fragment, FRAGMENT_TAG) } } } companion object { private const val FRAGMENT_TAG = "WALKTHROUGH_FRAGMENT" } } |
WalkthroughFragment sınıfımız ise basit bir fragment’dır. MainActivity sınıfından gelen başlık, resim gibi değerleri newInstance metodu aracılığıyla bu sınıfa aktardım. Sonrasında ImageView ve MaterialTextView arayüz elemanlarına bu değerleri atadım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
import android.os.Bundle import android.view.* import androidx.annotation.DrawableRes import androidx.fragment.app.Fragment import com.smality.materialmotionsharedaxis.databinding.WalkthroughFragmentBinding private const val ARG_TITLE = "title" private const val ARG_BODY = "body" private const val ARG_DRAWABLE_RES = "drawableRes" class WalkthroughFragment : Fragment() { private var title: String? = null private var body: String? = null private var drawableRes: Int? = null private var _binding: WalkthroughFragmentBinding? = null private val binding get() = _binding!! companion object { /** * MainActivity sınıfından gelen başlık, resim gibi değerleri newInstance * aracılığı ile alınıyor * @param body Parameter 1. * @param imageRes Parameter 2. * @return A new instance of fragment WalkthroughFragment. */ @JvmStatic fun newInstance(title: String, body: String, @DrawableRes imageRes: Int) = WalkthroughFragment().apply { arguments = Bundle().apply { putString(ARG_TITLE, title) putString(ARG_BODY, body) putInt(ARG_DRAWABLE_RES, imageRes) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { title = it.getString(ARG_TITLE) body = it.getString(ARG_BODY) drawableRes = it.getInt(ARG_DRAWABLE_RES) } } //LayoutInflater ile arayüz layout çağrılıyor override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = WalkthroughFragmentBinding.inflate(inflater, container, false) return binding.root } //Parametreler arayüz nesnelerine yükleniyor override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.titleTextView.text = title binding.bodyTextView.text = body drawableRes?.let { binding.imageView.setImageResource(it) } } override fun onDestroyView() { super.onDestroyView() _binding = null } } |
Diğer tasaırm kodlarını makaleyi sade bırakmak için yazmadım. Dilerseniz projemin kodlarına github linkinden hızlıca ulaşabilirsiniz.