Bu sayfada, aşağıdakileri yapmak için farklı dokunma teknolojisi API'lerinin nasıl kullanılacağına ilişkin örnekler yer almaktadır. Android uygulamasında özel efektler oluşturabilir. Ne kadar çok bilgi bu sayfa bir titreşim etkinleştiricisinin işleyişiyle ilgili iyi bilgiye dayanır, Titreşim etkinleştirici primini okumanızı öneririz.
Bu sayfada aşağıdaki örnekler yer almaktadır.
- Özel titreşim kalıpları
- Artış kalıbı: Düzgün başlayan bir kalıp.
- Yinelenen kalıp: Sonu olmayan bir kalıp.
- Yedekli desen: Yedek gösteriyoruz.
- Titreşim besteleri
- Direnç: Dinamik yoğunlukta bir sürükleme efekti.
- Genişletme: Yükselişten sonra düşme etkisi.
- Wobble:
SPIN
temel öğesini kullanan sallantılı efekt. - Sıçrama:
THUD
temel öğesi kullanılarak geri dönen efekt.
Daha fazla örnek için Etkinliklere dokunsal geri bildirim ekleme başlıklı makaleyi inceleyin. dokunma teknolojisiyle ilgili tasarım ilkelerine daima uyun.
Cihaz uyumluluğunu yönetmek için yedekleri kullanma
Özel efektler uygularken aşağıdakileri göz önünde bulundurun:
- Efekt için hangi cihaz özellikleri gereklidir?
- Cihaz efekti oynatamıyorsa ne yapmak gerekir?
Android Dokunsal API referansı, dokunma teknolojisinizle ilgili bileşenlere yönelik destek sağlar, böylece uygulamanız ve tutarlı bir genel deneyim sağlar.
Kullanım alanınıza bağlı olarak özel efektleri devre dışı bırakmak veya Farklı potansiyel özelliklere göre alternatif özel efektler sağlar.
Aşağıdaki üst düzey cihaz özelliği sınıflarını göz önünde bulundurun:
Dokunsal temel öğeleri kullanıyorsanız: Bu temel öğeleri destekleyen cihazlar gerektiği anlamına gelir. (Aşağıdaki ayrıntılar için bir sonraki bölüme bakın: temel unsurlardır.)
Genlik kontrolüne sahip cihazlar.
Temel titreşim desteğine (açık/kapalı) sahip cihazlar (yani, cihazlar) genlik kontrolü yoktur.
Uygulamanızın dokunma teknolojisi seçiminde bu kategoriler geçerliyse Dokunsal kullanıcı deneyiminin her cihazda tahmin edilebilir olması gerekir.
Temel dokunsal geri bildirim öğelerinin kullanımı
Android'de hem büyüklük hem de boyut açısından farklılık gösteren çeşitli dokunma teknolojisi temel öğeleri bulunur. sağlar. Tek bir temel öğeyi veya birlikte birden fazla temel öğeyi kullanabilirsiniz zengin dokunma efektleri elde etmek için kullanıldı.
- İkisi arasında fark edilebilir boşluklar için 50 ms veya daha uzun gecikme kullanın ilk elemanları da dikkate alır süre kullanmanızı öneririz.
- 1,4 veya daha yüksek oranda farklılık gösteren ölçekler kullanın.Böylece, yoğunluğu daha iyi algılanır.
0,5, 0,7 ve 1,0’lık ölçekleri kullanarak düşük, orta ve yüksek ilkel öğenin yoğunluk sürümü.
Özel titreşim desenleri oluşturma
Titreşim kalıpları genellikle bildirimler gibi dikkatsel dokunma teknolojisinde kullanılır
ve zil sesleri ekleyin. Vibrator
hizmeti, uygulamanın
zaman içinde titreşim genliğini değiştirin. Bu tür efektlere dalga formları denir.
Dalga formu efektleri kolayca algılanabilir ancak ani uzun titreşimler sessiz bir ortamda oynandığında kullanıcıyı ürkütebilir. Hedef genliğe yükseltme Ayrıca hızlı bir şekilde ayarlamak duyulabilir titremeler de doğurabilir. Şunun için öneri: dalga formu kalıpları tasarlamak, genlik geçişlerini yumuşatmak ve artış ve düşüş efektleri içerir.
Örnek: Artış kalıbı
Dalga formları üç parametreyle VibrationEffect
olarak temsil edilir:
- Zamanlamalar: Her dalga formu için milisaniye cinsinden bir süre dizisi bölümünü seçin.
- Genlik: Belirtilen her süre için istenen titreşim genliği 0 ile 255 arasında bir tam sayı değeriyle temsil edilen, 0 ile gösterilen ilk bağımsız değişken "kapalı" titreşimini temsil eder ve 255, cihazın maksimum değeridir genlik değeri.
- Tekrarlanan dizin: İlk bağımsız değişkende belirtilen dizideki dizin dalga formunu tekrarlamaya başlayın veya kalıbın yalnızca bir kez çalması gerekiyorsa -1 tuşuna basın.
Aşağıda, aralarında 350 ms'lik bir duraklamayla iki kez yanıp sönen bir dalga formu örneği verilmiştir. yanıp söner. İlk nabız, maksimum genliğe ulaşan sorunsuz bir artıştır. ikincisi, maksimum genliği tutmak için kısa bir rampadır. Sonda durdurma tanımlanır negatif tekrar endeksi değerine göre
Kotlin
val timings: LongArray = longArrayOf(50, 50, 50, 50, 50, 100, 350, 25, 25, 25, 25, 200) val amplitudes: IntArray = intArrayOf(33, 51, 75, 113, 170, 255, 0, 38, 62, 100, 160, 255) val repeatIndex = -1 // Do not repeat. vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex))
Java
long[] timings = new long[] { 50, 50, 50, 50, 50, 100, 350, 25, 25, 25, 25, 200 }; int[] amplitudes = new int[] { 33, 51, 75, 113, 170, 255, 0, 38, 62, 100, 160, 255 }; int repeatIndex = -1; // Do not repeat. vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeatIndex));
Örnek: Yinelenen kalıp
Dalga formları, iptal edilene kadar tekrar tekrar oynatılabilir. Bir feed'in negatif olmayan bir "repeat" parametresi ayarlamaktır. Bir tekrarlayan dalga formu varsa titreşim açık bir şekilde iptal edilene kadar hizmet:
Kotlin
void startVibrating() { val timings: LongArray = longArrayOf(50, 50, 100, 50, 50) val amplitudes: IntArray = intArrayOf(64, 128, 255, 128, 64) val repeat = 1 // Repeat from the second entry, index = 1. VibrationEffect repeatingEffect = VibrationEffect.createWaveform(timings, amplitudes, repeat) // repeatingEffect can be used in multiple places. vibrator.vibrate(repeatingEffect) } void stopVibrating() { vibrator.cancel() }
Java
void startVibrating() { long[] timings = new long[] { 50, 50, 100, 50, 50 }; int[] amplitudes = new int[] { 64, 128, 255, 128, 64 }; int repeat = 1; // Repeat from the second entry, index = 1. VibrationEffect repeatingEffect = VibrationEffect.createWaveform(timings, amplitudes, repeat); // repeatingEffect can be used in multiple places. vibrator.vibrate(repeatingEffect); } void stopVibrating() { vibrator.cancel(); }
Bu, kullanıcının şu işlemi gerçekleştirmesini gerektiren aralıklı etkinliklerde çok yararlıdır: kabul etmelisiniz. Bu tür etkinliklere örnek olarak gelen telefon aramaları, etkinleşen alarmlar.
Örnek: Yedekli desen
Bir titreşimin genliğini kontrol etmek, donanıma bağlı özelliklerdir. üzerinde dalga formu bu özelliğe sahip olmayan düşük özellikli cihaz, maksimum düzeyde titreşmesine neden olur genlik dizisindeki her pozitif girişin genliği. Uygulamanızın yerleştirmek istiyorsanız, cihazlarınızın doğru şekilde yayınlandığından emin olmanızı öneririz. bu koşulda oynatıldığında uyarı sesi oluşturmaz veya bunun yerine yedek olarak oynatılabilecek daha basit bir AÇMA/KAPAMA deseni tasarlayın.
Kotlin
if (vibrator.hasAmplitudeControl()) { vibrator.vibrate(VibrationEffect.createWaveform(smoothTimings, amplitudes, smoothRepeatIdx)) } else { vibrator.vibrate(VibrationEffect.createWaveform(onOffTimings, onOffRepeatIdx)) }
Java
if (vibrator.hasAmplitudeControl()) { vibrator.vibrate(VibrationEffect.createWaveform(smoothTimings, amplitudes, smoothRepeatIdx)); } else { vibrator.vibrate(VibrationEffect.createWaveform(onOffTimings, onOffRepeatIdx)); }
Titreşim bileşimleri oluşturma
Bu bölümde, bu metinleri oluştururken kullanabileceğiniz yöntemler daha uzun ve karmaşık özel efektler içerir. Ayrıca, zengin özellikleri keşfetmek için dokunma teknolojisinden yararlanabilirsiniz. Veri analizini artırmak için Daha karmaşık dokunma efektleri oluşturmak için genlik ve frekansı değiştiren efektler daha yüksek frekans bant genişliğine sahip dokunma teknolojisi aktüatörlerine sahip cihazlarda kullanılabilir.
Özel titreşim oluşturma işlemi kalıpları görebilirsiniz. Sesin yumuşak efektleri oluşturmak için titreşim genliğinin nasıl kontrol zaman harcıyor. Zengin dokunma teknolojisi bu kavramı geliştirerek daha geniş bir frekans aralığı kullanarak efekti daha da yumuşak hale getirir. Bu dalga formları özellikle crescendo veya diminuendo oluşturmada etkilidir. etkisi.
Bu sayfanın önceki bölümlerinde açıklanan beste temel öğeleri, girin. Net, kısa ve hoş bir titreşim sağlar dokunma teknolojisi ilkelerine uygun olarak tasarlanmıştır. Daha fazla hakkında ayrıntılı bilgi için Titreşim aktüatörleri ve öğrenin.
Android, desteklenmeyen besteler için yedek sağlamaz ilkel maddelerdir. Aşağıdaki adımları uygulamanızı öneririz:
Gelişmiş dokunma teknolojisinizi etkinleştirmeden önce belirli bir cihazın ve kullandığınız tüm temel bileşenleri ele alalım.
Yalnızca bazı efektler içerir. Chrome Web Mağazası'ndaki cihazın desteği aşağıdaki gibi gösterilir.
VibrationEffect.Composition
ile oluşturulan titreşim efektleri oluşturabilirsiniz.
Aşağıda, yavaşça yükselen bir efektin ardından keskin bir tıklama efekti örneği verilmiştir:
Kotlin
vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_SLOW_RISE ).addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK ).compose() )
Java
vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) .compose());
Sırayla oynatılacak temel öğeler eklenerek beste oluşturulur. Her biri ilkel aynı zamanda ölçeklenebilir, böylece titreşimin genliğini kontrol edebilirsiniz üretilmesini sağlar. Ölçek, 0 ile 1 arasında bir değer olarak tanımlanır. Burada 0 değeri, bu temel öğenin bulunabileceği minimum genlikle eşleşir. (neredeyse) hissettim.
Aynı temel öğenin zayıf ve güçlü bir versiyonunu oluşturmak isterseniz 1,4 veya daha yüksek bir oranda farklılık göstermesi önerilir.Dolayısıyla, kolayca algılanabilir. Üçten fazla oluşturmaya çalışmayın algılanamadığından, aynı ilkelin yoğunluk seviyeleri farklı olmalıdır. Örneğin, 0,5, 0,7 ve 1,0 ölçeğini kullanarak düşük, orta, bir temel bileşenin yüksek yoğunluklu versiyonunu ele alacağız.
Beste, ardışık olmayanlar arasında eklenecek gecikmeleri de belirtebilir ilkel maddelerdir. Bu gecikme, önceki temel öğedir. Genel olarak, iki temel öğe arasındaki 5-10 ms'lik bir boşluk çok fazladır. kısa olmasına dikkat edin. 50 ms veya daha uzun aralıklarla bir boşluk kullanmayı düşünün kullanarak iki temel öğe arasında fark edilebilir bir boşluk oluşturabilirsiniz. Burada gecikmeli bir beste örneği:
Kotlin
val delayMs = 100 vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f ).addPrimitive( VibrationEffect.Composition.PRIMITIVE_SPIN, 0.6f ).addPrimitive( VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, delayMs ).compose() )
Java
int delayMs = 100; vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.6f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, delayMs) .compose());
Belirli API'ler için cihaz desteğini doğrulamak amacıyla aşağıdaki API'ler temel öğeler:
Kotlin
val primitive = VibrationEffect.Composition.PRIMITIVE_LOW_TICK if (vibrator.areAllPrimitivesSupported(primitive)) { vibrator.vibrate(VibrationEffect.startComposition().addPrimitive(primitive).compose()) } else { // Play a predefined effect or custom pattern as a fallback. }
Java
int primitive = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; if (vibrator.areAllPrimitivesSupported(primitive)) { vibrator.vibrate(VibrationEffect.startComposition().addPrimitive(primitive).compose()); } else { // Play a predefined effect or custom pattern as a fallback. }
Ayrıca, birden fazla temel öğeyi kontrol edip hangilerinin uygulanacağına karar vermek de mümkündür. cihaz destek düzeyine göre oluşturma:
Kotlin
val effects: IntArray = intArrayOf( VibrationEffect.Composition.PRIMITIVE_LOW_TICK, VibrationEffect.Composition.PRIMITIVE_TICK, VibrationEffect.Composition.PRIMITIVE_CLICK ) val supported: BooleanArray = vibrator.arePrimitivesSupported(primitives);
Java
int[] primitives = new int[] { VibrationEffect.Composition.PRIMITIVE_LOW_TICK, VibrationEffect.Composition.PRIMITIVE_TICK, VibrationEffect.Composition.PRIMITIVE_CLICK }; boolean[] supported = vibrator.arePrimitivesSupported(effects);
Örnek: Direnç gösterin (alçak değer çizgileriyle)
İlkel titreşimin genliğini kontrol ederek devam eden bir eylem için yararlı geri bildirim sağlar. Yakın aralıklı ölçek değerleri ilkel öğenin yumuşak yükseliş efektini oluşturmak için kullanılır. Gecikme süresi, ardışık temel öğeleri de kullanıcıya göre dinamik olarak ayarlanabilir. bahsedeceğim. Bu, aşağıdaki görüntüleme animasyonu örneğinde gösterilmektedir Sürükleme hareketiyle kontrol edilir ve dokunma teknolojisiyle geliştirilir.
![Aşağı sürüklenen bir dairenin animasyonu](https://developer.android.com/static/develop/ui/views/haptics/images/resist-demo.gif?authuser=9&hl=tr)
Kotlin
@Composable fun ResistScreen() { // Control variables for the dragging of the indicator. var isDragging by remember { mutableStateOf(false) } var dragOffset by remember { mutableStateOf(0f) } // Only vibrates while the user is dragging if (isDragging) { LaunchedEffect(Unit) { // Continuously run the effect for vibration to occur even when the view // is not being drawn, when user stops dragging midway through gesture. while (true) { // Calculate the interval inversely proportional to the drag offset. val vibrationInterval = calculateVibrationInterval(dragOffset) // Calculate the scale directly proportional to the drag offset. val vibrationScale = calculateVibrationScale(dragOffset) delay(vibrationInterval) vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_LOW_TICK, vibrationScale ).compose() ) } } } Screen() { Column( Modifier .draggable( orientation = Orientation.Vertical, onDragStarted = { isDragging = true }, onDragStopped = { isDragging = false }, state = rememberDraggableState { delta -> dragOffset += delta } ) ) { // Build the indicator UI based on how much the user has dragged it. ResistIndicator(dragOffset) } } }
Java
class DragListener implements View.OnTouchListener { // Control variables for the dragging of the indicator. private int startY; private int vibrationInterval; private float vibrationScale; @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startY = event.getRawY(); vibrationInterval = calculateVibrationInterval(0); vibrationScale = calculateVibrationScale(0); startVibration(); break; case MotionEvent.ACTION_MOVE: float dragOffset = event.getRawY() - startY; // Calculate the interval inversely proportional to the drag offset. vibrationInterval = calculateVibrationInterval(dragOffset); // Calculate the scale directly proportional to the drag offset. vibrationScale = calculateVibrationScale(dragOffset); // Build the indicator UI based on how much the user has dragged it. updateIndicator(dragOffset); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // Only vibrates while the user is dragging cancelVibration(); break; } return true; } private void startVibration() { vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, vibrationScale) .compose()); // Continuously run the effect for vibration to occur even when the view // is not being drawn, when user stops dragging midway through gesture. handler.postDelayed(this::startVibration, vibrationInterval); } private void cancelVibration() { handler.removeCallbacksAndMessages(null); } }
Örnek: Genişletme (artış ve düşüşle)
Algılanan titreşim yoğunluğunu artırmak için iki temel öğe vardır:
PRIMITIVE_QUICK_RISE
ve
PRIMITIVE_SLOW_RISE
.
Her ikisi de aynı hedefe farklı sürelerle ulaşır. Yalnızca bir tane
ilkel unsurlar ise
PRIMITIVE_QUICK_FALL
.
Bu temel bileşenler birlikte daha iyi çalışarak şu alanlarda büyüyen bir dalga formu segmenti oluşturur:
şiddetine bağlı olarak değişir. Ölçeklendirilmiş temel öğeleri, aniden oluşabilecek ani
olarak değişir. Bu, genel olarak genişletmek için iyi sonuç veren bir araçtır.
efekt süresidir. Farkında olarak, kullanıcılar artan bilgiyi her zaman
Bu nedenle, yükselen kısmı
düşüşten daha kısa tutmak için
vurguyu düşüşe kaydırmak için kullanılır.
Burada, genişletme ve genişletme için bu bestenin bir örneğini bir daire daraltılır. Yükseliş efekti, uygulama sırasında genişleme hissini tıklayın. Yükseliş ve düşüş efektlerinin birlikte kullanımı animasyonun sonunda daralması gerekir.
![Genişleyen bir daire animasyonu](https://developer.android.com/static/develop/ui/views/haptics/images/expand-demo.gif?authuser=9&hl=tr)
Kotlin
enum class ExpandShapeState { Collapsed, Expanded } @Composable fun ExpandScreen() { // Control variable for the state of the indicator. var currentState by remember { mutableStateOf(ExpandShapeState.Collapsed) } // Animation between expanded and collapsed states. val transitionData = updateTransitionData(currentState) Screen() { Column( Modifier .clickable( { if (currentState == ExpandShapeState.Collapsed) { currentState = ExpandShapeState.Expanded vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.3f ).addPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.3f ).compose() ) } else { currentState = ExpandShapeState.Collapsed vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_SLOW_RISE ).compose() ) } ) ) { // Build the indicator UI based on the current state. ExpandIndicator(transitionData) } } }
Java
class ClickListener implements View.OnClickListener { private final Animation expandAnimation; private final Animation collapseAnimation; private boolean isExpanded; ClickListener(Context context) { expandAnimation = AnimationUtils.loadAnimation(context, R.anim.expand); expandAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.3f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.3f) .compose()); } }); collapseAnimation = AnimationUtils.loadAnimation(context, R.anim.collapse); collapseAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE) .compose()); } }); } @Override public void onClick(View view) { view.startAnimation(isExpanded ? collapseAnimation : expandAnimation); isExpanded = !isExpanded; } }
Örnek: Wobble (çevirilerle)
Temel dokunma teknolojisi ilkelerinden biri, kullanıcıları memnun etmektir. Eğlenceli bir yol
hoş bir beklenmedik titreşim efekti eklemek için
PRIMITIVE_SPIN
.
Bu temel öğe, birden fazla kez çağrıldığında en etkili olur. Birden çok
dönen dairesel hareketler titreme ve dengesiz bir efekt yaratabilir. Bu da,
her temel öğeye biraz rastgele bir ölçeklendirme uygulayarak bunu daha da geliştirebiliriz. Siz
ayrıca ardışık döndürme temel öğeleri arasındaki boşlukla ilgili denemeler yapabilirsiniz. İki döndürme
(arada 0 ms) boşluk olmamasına rağmen videonun sık sık duyulduğu bir video hissi verir. Artan
10 ile 50 ms. arasında dönüşler arası boşluk, daha gevşek bir dönüş hissine neden olur ve
bir videonun veya animasyonun süresini eşleştirmek için kullanılabilir.
Öncekinden 100 ms'den uzun bir boşluk kullanmanız önerilmez dönüşler düzgün bir şekilde bütünleşmiyor ve bireysel efektler hissi vermeye başlıyor.
Aşağıda, sürüklendikten sonra geri dönen esnek şekil örneği verilmiştir ve ardından serbest bırakıldı. Animasyon, sihirli bir şekilde gizlenen, sıçrama yer değiştirmesiyle orantılı olarak değişen yoğunluklara sahiptir.
![Zıplayan esnek şekil animasyonu](https://developer.android.com/static/develop/ui/views/haptics/images/wobble-demo.gif?authuser=9&hl=tr)
Kotlin
@Composable fun WobbleScreen() { // Control variables for the dragging and animating state of the elastic. var dragDistance by remember { mutableStateOf(0f) } var isWobbling by remember { mutableStateOf(false) } // Use drag distance to create an animated float value behaving like a spring. val dragDistanceAnimated by animateFloatAsState( targetValue = if (dragDistance > 0f) dragDistance else 0f, animationSpec = spring( dampingRatio = Spring.DampingRatioHighBouncy, stiffness = Spring.StiffnessMedium ), ) if (isWobbling) { LaunchedEffect(Unit) { while (true) { val displacement = dragDistanceAnimated / MAX_DRAG_DISTANCE // Use some sort of minimum displacement so the final few frames // of animation don't generate a vibration. if (displacement > SPIN_MIN_DISPLACEMENT) { vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_SPIN, nextSpinScale(displacement) ).addPrimitive( VibrationEffect.Composition.PRIMITIVE_SPIN, nextSpinScale(displacement) ).compose() ) } // Delay the next check for a sufficient duration until the current // composition finishes. Note that you can use // Vibrator.getPrimitiveDurations API to calculcate the delay. delay(VIBRATION_DURATION) } } } Box( Modifier .fillMaxSize() .draggable( onDragStopped = { isWobbling = true dragDistance = 0f }, orientation = Orientation.Vertical, state = rememberDraggableState { delta -> isWobbling = false dragDistance += delta } ) ) { // Draw the wobbling shape using the animated spring-like value. WobbleShape(dragDistanceAnimated) } } // Calculate a random scale for each spin to vary the full effect. fun nextSpinScale(displacement: Float): Float { // Generate a random offset in [-0.1,+0.1] to be added to the vibration // scale so the spin effects have slightly different values. val randomOffset: Float = Random.Default.nextFloat() * 0.2f - 0.1f return (displacement + randomOffset).absoluteValue.coerceIn(0f, 1f) }
Java
class AnimationListener implements DynamicAnimation.OnAnimationUpdateListener { private final Random vibrationRandom = new Random(seed); private final long lastVibrationUptime; @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { // Delay the next check for a sufficient duration until the current // composition finishes. Note that you can use // Vibrator.getPrimitiveDurations API to calculcate the delay. if (SystemClock.uptimeMillis() - lastVibrationUptime < VIBRATION_DURATION) { return; } float displacement = calculateRelativeDisplacement(value); // Use some sort of minimum displacement so the final few frames // of animation don't generate a vibration. if (displacement < SPIN_MIN_DISPLACEMENT) { return; } lastVibrationUptime = SystemClock.uptimeMillis(); vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, nextSpinScale(displacement)) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, nextSpinScale(displacement)) .compose()); } // Calculate a random scale for each spin to vary the full effect. float nextSpinScale(float displacement) { // Generate a random offset in [-0.1,+0.1] to be added to the vibration // scale so the spin effects have slightly different values. float randomOffset = vibrationRandom.nextFloat() * 0.2f - 0.1f return MathUtils.clamp(displacement + randomOffset, 0f, 1f) } }
Örnek: Sıçrama (tıkırtılarla)
Titreşim efektlerinin bir diğer gelişmiş uygulaması da fiziksel
simülasyondur.
etkileşimleridir. İlgili içeriği oluşturmak için kullanılan
PRIMITIVE_THUD
güçlü ve yankı uyandıran bir efekt oluşturabilir. Bunun için
(ör. bir videoda veya animasyonda) etkisinin görselleştirilmesi
yardımcı olabilir.
Aşağıda, gürültü efektiyle geliştirilmiş basit bir top düşürme animasyonu örneği verilmiştir top ekranın altından her zıpladığında oynanır:
![Ekranın altından zıplayan bir topun animasyonu](https://developer.android.com/static/develop/ui/views/haptics/images/bounce-demo.gif?authuser=9&hl=tr)
Kotlin
enum class BallPosition { Start, End } @Composable fun BounceScreen() { // Control variable for the state of the ball. var ballPosition by remember { mutableStateOf(BallPosition.Start) } var bounceCount by remember { mutableStateOf(0) } // Animation for the bouncing ball. var transitionData = updateTransitionData(ballPosition) val collisionData = updateCollisionData(transitionData) // Ball is about to contact floor, only vibrating once per collision. var hasVibratedForBallContact by remember { mutableStateOf(false) } if (collisionData.collisionWithFloor) { if (!hasVibratedForBallContact) { val vibrationScale = 0.7.pow(bounceCount++).toFloat() vibrator.vibrate( VibrationEffect.startComposition().addPrimitive( VibrationEffect.Composition.PRIMITIVE_THUD, vibrationScale ).compose() ) hasVibratedForBallContact = true } } else { // Reset for next contact with floor. hasVibratedForBallContact = false } Screen() { Box( Modifier .fillMaxSize() .clickable { if (transitionData.isAtStart) { ballPosition = BallPosition.End } else { ballPosition = BallPosition.Start bounceCount = 0 } }, ) { // Build the ball UI based on the current state. BouncingBall(transitionData) } } }
Java
class ClickListener implements View.OnClickListener { @Override public void onClick(View view) { view.animate() .translationY(targetY) .setDuration(3000) .setInterpolator(new BounceInterpolator()) .setUpdateListener(new AnimatorUpdateListener() { boolean hasVibratedForBallContact = false; int bounceCount = 0; @Override public void onAnimationUpdate(ValueAnimator animator) { boolean valueBeyondThreshold = (float) animator.getAnimatedValue() > 0.98; if (valueBeyondThreshold) { if (!hasVibratedForBallContact) { float vibrationScale = (float) Math.pow(0.7, bounceCount++); vibrator.vibrate( VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, vibrationScale) .compose()); hasVibratedForBallContact = true; } } else { // Reset for next contact with floor. hasVibratedForBallContact = false; } } }); } }