Özel Görünümler Oluşturma

Bu codelab, Advanced Android in Kotlin kursunun bir parçasıdır. Bu kurstan en iyi şekilde yararlanmak için codelab'leri sırayla incelemeniz önerilir ancak bu zorunlu değildir. Kursla ilgili tüm codelab'ler Kotlin'de İleri Düzey Android codelab'leri açılış sayfasında listelenir.

Giriş

Android, View alt sınıflarından oluşan geniş bir set sunar. Örneğin: Button, TextView, EditText, ImageView, CheckBox veya RadioButton. Bu alt sınıfları, kullanıcı etkileşimini sağlayan ve uygulamanızda bilgileri görüntüleyen bir kullanıcı arayüzü oluşturmak için kullanabilirsiniz. View alt sınıflarından hiçbiri ihtiyaçlarınızı karşılamıyorsa özel görünüm olarak bilinen bir View alt sınıfı oluşturabilirsiniz.

Özel görünüm oluşturmak için mevcut bir View alt sınıfını (ör. Button veya EditText) genişletebilir ya da View için kendi alt sınıfınızı oluşturabilirsiniz. View öğesini doğrudan genişleterek, View öğesinin onDraw() yöntemini geçersiz kılarak herhangi bir boyut ve şekilde etkileşimli bir kullanıcı arayüzü öğesi oluşturabilirsiniz.

Özel görünüm oluşturduktan sonra, TextView veya Button ekler gibi etkinliğinize ekleyebilirsiniz.

Bu derste, View öğesini genişleterek sıfırdan özel görünüm oluşturma işlemi açıklanmaktadır.

Bilmeniz gerekenler

  • Etkinlik içeren bir uygulama oluşturma ve Android Studio'yu kullanarak uygulamayı çalıştırma

Neler öğreneceksiniz?

  • Özel görünüm oluşturmak için View nasıl genişletilir?
  • Dairesel şekilde özel görünüm çizme
  • Kullanıcıların özel görünümle etkileşimini yönetmek için dinleyicileri kullanma
  • Düzenlerde özel görünüm kullanma

Yapacaklarınız

  • Özel görünüm oluşturmak için View simgesini genişletin.
  • Özel görünümü çizim ve boyama değerleriyle başlatın.
  • Görünümü çizmek için onDraw() geçersiz kılın.
  • Özel görünümün davranışını sağlamak için dinleyicileri kullanın.
  • Özel görünümü bir düzene ekleyin.

CustomFanController uygulaması, View sınıfını genişleterek özel görünüm alt sınıfı oluşturmayı gösterir. Yeni alt sınıfa DialView adı verilir.

Uygulamada, kapalı (0), düşük (1), orta (2) ve yüksek (3) ayarlarını içeren, fiziksel bir fan kontrolüne benzeyen dairesel bir kullanıcı arayüzü öğesi gösteriliyor. Kullanıcı görünüme dokunduğunda seçim göstergesi bir sonraki konuma (0-1-2-3) ve tekrar 0'a geçer. Ayrıca, seçim 1 veya daha yüksekse görünümün dairesel kısmının arka plan rengi griden yeşile döner (fan gücünün açık olduğunu gösterir).

Görünümler, bir uygulamanın kullanıcı arayüzünün temel yapı taşlarıdır. View sınıfı, tipik bir Android uygulamasının kullanıcı arayüzünün birçok ihtiyacını karşılayan ve UI widget'ları olarak adlandırılan birçok alt sınıf sağlar.

Button ve TextView gibi kullanıcı arayüzü yapı taşları, View sınıfını genişleten alt sınıflardır. Zaman kazanmak ve geliştirme çabalarınızı azaltmak için bu View alt sınıflarından birini genişletebilirsiniz. Özel görünüm, üst öğesinin görünümünü ve davranışını devralır. Değiştirmek istediğiniz davranışı veya görünümün yönünü geçersiz kılabilirsiniz. Örneğin, EditText öğesini genişleterek özel bir görünüm oluşturursanız görünüm, EditText görünümü gibi davranır ancak metin giriş alanındaki metni temizleyen bir X düğmesi gösterecek şekilde de özelleştirilebilir.

Özel bir görünüm elde etmek için View alt sınıflarından herhangi birini (ör. EditText) genişletebilirsiniz. Amacınıza en uygun olanı seçin. Ardından, özel görünümü bir veya daha fazla düzende, özelliklere sahip bir XML öğesi olarak diğer View alt sınıfları gibi kullanabilirsiniz.

Sıfırdan kendi özel görünümünüzü oluşturmak için View sınıfını genişletin. Kodunuz, görünümün görünümünü ve işlevselliğini tanımlamak için View yöntemlerini geçersiz kılar. Kendi özel görünümünüzü oluştururken, herhangi bir boyut ve şekle sahip tüm kullanıcı arayüzü öğesini ekrana çizmekten siz sorumlusunuz. Button gibi mevcut bir görünümü alt sınıfa ayırırsanız bu sınıf, çizimi sizin için işler. (Çizim hakkında daha fazla bilgiyi bu Codelab'in ilerleyen bölümlerinde edineceksiniz.)

Özel görünüm oluşturmak için genel olarak şu adımları izleyin:

  • View sınıfını veya View alt sınıfını (ör. Button veya EditText) genişleten özel bir görünüm sınıfı oluşturun.
  • Mevcut bir View alt sınıfını genişletiyorsanız yalnızca değiştirmek istediğiniz davranışı veya görünüm yönlerini geçersiz kılın.
  • View sınıfını genişletirseniz yeni sınıfta onDraw() ve onMeasure() gibi View yöntemlerini geçersiz kılarak özel görünümün şeklini çizin ve görünümünü kontrol edin.
  • Kullanıcı etkileşimine yanıt vermek için kod ekleyin ve gerekirse özel görünümü yeniden çizin.
  • Özel görünüm sınıfını, etkinliğinizin XML düzeninde bir kullanıcı arayüzü widget'ı olarak kullanın. Görünüm için farklı düzenlerde özelleştirme sağlamak üzere özel özellikler de tanımlayabilirsiniz.

Bu görevde şunları yapacaksınız:

  • Özel görünüm için geçici yer tutucu olarak ImageView simgesini içeren bir uygulama oluşturun.
  • Özel görünümü oluşturmak için View simgesini genişletin.
  • Özel görünümü çizim ve boyama değerleriyle başlatın.

1. adım: ImageView yer tutucusu olan bir uygulama oluşturun

  1. Empty Activity şablonunu kullanarak CustomFanController başlıklı bir Kotlin uygulaması oluşturun. Paket adının com.example.android.customfancontroller olduğundan emin olun.
  2. XML kodunu düzenlemek için Metin sekmesinde activity_main.xml simgesini açın.
  3. Mevcut TextView kodunu bu kodla değiştirin. Bu metin, özel görünüm için etkinlikteki bir etiket görevi görür.
<TextView
       android:id="@+id/customViewLabel"
       android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:padding="16dp"
       android:textColor="@android:color/black"
       android:layout_marginStart="8dp"
       android:layout_marginEnd="8dp"
       android:layout_marginTop="24dp"
       android:text="Fan Control"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"/>
  1. Bu ImageView öğesini düzene ekleyin. Bu, bu codelab'de oluşturacağınız özel görünüm için yer tutucudur.
<ImageView
       android:id="@+id/dialView"
       android:layout_width="200dp"
       android:layout_height="200dp"
       android:background="@android:color/darker_gray"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="8dp"
       android:layout_marginRight="8dp"
       android:layout_marginTop="8dp"/>
  1. Her iki kullanıcı arayüzü öğesinde de dize ve boyut kaynaklarını ayıklayın.
  2. Tasarım sekmesini tıklayın. Düzen aşağıdaki gibi görünmelidir:

2. Adım Özel görünüm sınıfınızı oluşturun

  1. DialView adlı yeni bir Kotlin sınıfı oluşturun.
  2. View değerini uzatmak için sınıf tanımını değiştirin. İstendiğinde android.view.View içe aktarın.
  3. View simgesini ve ardından kırmızı ampulü tıklayın. Add Android View constructors using '@JvmOverloads' (Android View oluşturucularını "@JvmOverloads" kullanarak ekle) seçeneğini belirleyin. Android Studio, View sınıfından oluşturucuyu ekler. @JvmOverloads ek açıklaması, Kotlin derleyicisine bu işlev için varsayılan parametre değerlerinin yerine geçen aşırı yüklemeler oluşturmasını söyler.
class DialView @JvmOverloads constructor(
   context: Context,
   attrs: AttributeSet? = null,
   defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
  1. DialView sınıf tanımının üzerinde, içe aktarma işlemlerinin hemen altında, mevcut vantilatör hızlarını temsil eden üst düzey bir enum ekleyin. Değerler gerçek dizeler değil, dize kaynakları olduğundan bu enum öğesinin Int türünde olduğunu unutmayın. Android Studio, bu değerlerin her birinde eksik dize kaynaklarıyla ilgili hataları gösterir. Bu hataları sonraki bir adımda düzelteceksiniz.
private enum class FanSpeed(val label: Int) {
   OFF(R.string.fan_off),
   LOW(R.string.fan_low),
   MEDIUM(R.string.fan_medium),
   HIGH(R.string.fan_high);
}
  1. enum simgesinin altına şu sabitleri ekleyin. Bunları, gösterge işaretçilerini ve etiketleri çizerken kullanırsınız.
private const val RADIUS_OFFSET_LABEL = 30      
private const val RADIUS_OFFSET_INDICATOR = -35
  1. DialView sınıfında, özel görünümü çizmek için ihtiyacınız olan birkaç değişkeni tanımlayın. İstenirse android.graphics.PointF içe aktarın.
private var radius = 0.0f                   // Radius of the circle.
private var fanSpeed = FanSpeed.OFF         // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)
  • radius, çemberin geçerli yarıçapıdır. Bu değer, görünüm ekranda çizildiğinde ayarlanır.
  • fanSpeed, FanSpeed numaralandırmasındaki değerlerden biri olan fanın mevcut hızıdır. Varsayılan olarak bu değer OFF'dır.
  • Son olarak postPosition, görünümün çeşitli öğelerini ekranda çizmek için kullanılacak bir X,Y noktasıdır.

Bu değerler, görünümün gerçekten çizildiği sırada değil, burada oluşturulup başlatılır. Böylece, asıl çizim adımının mümkün olduğunca hızlı çalışması sağlanır.

  1. Ayrıca DialView sınıf tanımının içinde, Paint nesnesini birkaç temel stil ile başlatın. İstendiğinde android.graphics.Paint ve android.graphics.Typeface öğelerini içe aktarın. Daha önce değişkenlerde olduğu gibi, bu stiller de çizim adımını hızlandırmak için burada başlatılır.
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
   style = Paint.Style.FILL
   textAlign = Paint.Align.CENTER
   textSize = 55.0f
   typeface = Typeface.create( "", Typeface.BOLD)
}
  1. res/values/strings.xml dosyasını açın ve fan hızları için dize kaynaklarını ekleyin:
<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string>

Özel görünüm oluşturduktan sonra bu görünümü çizebilmeniz gerekir. View gibi bir EditText alt sınıfını genişlettiğinizde bu alt sınıf, görünümün görünümünü ve özelliklerini tanımlar ve kendisini ekranda çizer. Bu nedenle, görünümü çizmek için kod yazmanız gerekmez. Bunun yerine, görünümünüzü özelleştirmek için üst öğenin yöntemlerini geçersiz kılabilirsiniz.

Görünümünüzü sıfırdan oluşturuyorsanız (View'yı genişleterek) ekran her yenilendiğinde görünümün tamamını çizmekten ve çizimi işleyen View yöntemlerini geçersiz kılmaktan siz sorumlusunuz. View öğesini genişleten özel bir görünümü düzgün şekilde çizmek için:

  • onSizeChanged() yöntemini geçersiz kılarak görünümün ilk göründüğündeki ve her boyut değişikliğinde görünümün boyutunu hesaplayın.
  • onDraw() yöntemini geçersiz kılarak özel görünümü çizin. Bu işlem için Paint nesnesiyle stilize edilmiş bir Canvas nesnesi kullanın.
  • Görünümün çizilme şeklini değiştiren bir kullanıcı tıklamasına yanıt verirken invalidate() yöntemini çağırarak görünümün tamamını geçersiz kılın. Böylece, görünümü yeniden çizmek için onDraw() yönteminin çağrılması zorunlu kılınır.

onDraw() yöntemi, ekran her yenilendiğinde (saniyede birçok kez olabilir) çağrılır. Performans nedenleriyle ve görsel hataları önlemek için onDraw() içinde mümkün olduğunca az işlem yapmalısınız. Özellikle onDraw() içine yerleştirme yapmayın. Yerleştirmeler, görsel titremeye neden olabilecek bir çöp toplama işlemine yol açabilir.

Canvas ve Paint sınıfları, çizimle ilgili bir dizi kullanışlı kısayol sunar:

  • drawText() kullanarak metin çizin. setTypeface() işlevini çağırarak yazı tipini, setColor() işlevini çağırarak da metin rengini belirtin.
  • drawRect(), drawOval() ve drawArc() kullanarak temel şekiller çizin. setStyle() işlevini çağırarak şekillerin doldurulup doldurulmayacağını, ana hatlarının çizilip çizilmeyeceğini veya her ikisinin de yapılıp yapılmayacağını değiştirin.
  • drawBitmap() kullanarak bit eşlemler çizin.

Canvas ve Paint hakkında daha fazla bilgiyi sonraki bir codelab'de edineceksiniz. Android'in görünümleri nasıl çizdiği hakkında daha fazla bilgi edinmek için Android'in Görünümleri Çizme Şekli başlıklı makaleyi inceleyin.

Bu görevde, onSizeChanged() ve onDraw() yöntemlerini kullanarak fan kontrol cihazının özel görünümünü (kadranın kendisi, mevcut konum göstergesi ve gösterge etiketleri) ekrana çizeceksiniz. Ayrıca, gösterge etiketinin kadran üzerindeki mevcut X,Y konumunu hesaplamak için computeXYForSpeed(), yardımcı yöntemini de oluşturacaksınız.

1. Adım: Konumları hesaplama ve görünümü çizme

  1. DialView sınıfında, başlatmaların altında, özel görünümün kadranının boyutunu hesaplamak için View sınıfındaki onSizeChanged() yöntemini geçersiz kılın. kotlin içe aktarın.math.min istendiğinde.

    onSizeChanged() yöntemi, görünümün boyutu her değiştiğinde (düzen şişirildiğinde ilk çizim dahil) çağrılır. Çizim yaptığınız her seferde yeniden hesaplamak yerine, konumları, boyutları ve özel görünümünüzün boyutuyla ilgili diğer tüm değerleri hesaplamak için onSizeChanged() işlevini geçersiz kılın. Bu durumda, arama çubuğunun daire öğesinin geçerli yarıçapını hesaplamak için onSizeChanged() işlevini kullanırsınız.
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   radius = (min(width, height) / 2.0 * 0.8).toFloat()
}
  1. onSizeChanged() bölümünün altına, PointF sınıfı için bir computeXYForSpeed() uzantı işlevi tanımlamak üzere bu kodu ekleyin. İstendiğinde kotlin.math.cos ve kotlin.math.sin öğelerini içe aktarın. PointF sınıfındaki bu uzantı işlevi, mevcut FanSpeed konumu ve kadranın yarıçapı göz önüne alındığında, metin etiketi ve mevcut gösterge (0, 1, 2 veya 3) için ekrandaki X ve Y koordinatlarını hesaplar. Bunu onDraw(). içinde kullanacaksınız.
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
   // Angles are in radians.
   val startAngle = Math.PI * (9 / 8.0)   
   val angle = startAngle + pos.ordinal * (Math.PI / 4)
   x = (radius * cos(angle)).toFloat() + width / 2
   y = (radius * sin(angle)).toFloat() + height / 2
}
  1. Görünümü ekranda Canvas ve Paint sınıflarıyla oluşturmak için onDraw() yöntemini geçersiz kılın. İstendiğinde android.graphics.Canvas içe aktarın. Bu, iskelet geçersiz kılma işlemidir:
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
   
}
  1. onDraw() içinde, boya rengini fan hızının OFF olup olmamasına veya başka bir değere bağlı olarak gri (Color.GRAY) ya da yeşil (Color.GREEN) olarak ayarlamak için şu satırı ekleyin. İstendiğinde android.graphics.Color içe aktarın.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
  1. Arama için drawCircle() yöntemiyle daire çizmek üzere bu kodu ekleyin. Bu yöntem, dairenin merkezini, dairenin yarıçapını ve mevcut boya rengini bulmak için mevcut görünümün genişliğini ve yüksekliğini kullanır. width ve height özellikleri, View üst sınıfının üyeleridir ve görünümün mevcut boyutlarını gösterir.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
  1. Fan hızı göstergesi işareti için daha küçük bir daire çizmek üzere aşağıdaki kodu drawCircle() yöntemiyle ekleyin. Bu kısımda PointF kullanılır.Gösterge merkezinin X ve Y koordinatlarını mevcut fan hızına göre hesaplamak için computeXYforSpeed() uzantı yöntemi.
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
  1. Son olarak, fan hızı etiketlerini (0, 1, 2, 3) kadranın etrafında uygun konumlara çizin. Yöntemin bu bölümü, her etiketin konumunu almak için PointF.computeXYForSpeed() işlevini tekrar çağırır ve tahsisleri önlemek için pointPosition nesnesini her seferinde yeniden kullanır. Etiketleri çizmek için drawText() simgesini kullanın.
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
   pointPosition.computeXYForSpeed(i, labelRadius)
   val label = resources.getString(i.label)
   canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}

Tamamlanan onDraw() yöntemi şu şekilde görünür:

override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
   // Set dial background color to green if selection not off.
   paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
   // Draw the dial.
   canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
   // Draw the indicator circle.
   val markerRadius = radius + RADIUS_OFFSET_INDICATOR
   pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
   paint.color = Color.BLACK
   canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
   // Draw the text labels.
   val labelRadius = radius + RADIUS_OFFSET_LABEL
   for (i in FanSpeed.values()) {
       pointPosition.computeXYForSpeed(i, labelRadius)
       val label = resources.getString(i.label)
       canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
   }
}

2. adım: Görünümü düzene ekleme

Bir uygulamanın kullanıcı arayüzüne özel görünüm eklemek için bunu etkinliğin XML düzeninde bir öğe olarak belirtirsiniz. Görünümünü ve davranışını, diğer tüm kullanıcı arayüzü öğelerinde olduğu gibi XML öğesi özellikleriyle kontrol edin.

  1. activity_main.xml içinde, dialView için ImageView etiketini com.example.android.customfancontroller.DialView olarak değiştirin ve android:background özelliğini silin. Hem DialView hem de orijinal ImageView, View sınıfındaki standart özellikleri devralır. Bu nedenle diğer özelliklerde herhangi bir değişiklik yapmanız gerekmez. Yeni DialView öğesi şu şekilde görünür:
<com.example.android.customfancontroller.DialView
       android:id="@+id/dialView"
       android:layout_width="@dimen/fan_dimen"
       android:layout_height="@dimen/fan_dimen"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="@dimen/default_margin"
       android:layout_marginRight="@dimen/default_margin"
       android:layout_marginTop="@dimen/default_margin" />
  1. Uygulamayı çalıştırın. Etkinlikte fan kontrolü görünümünüz gösterilir.

Son görev, kullanıcının görünüme dokunduğunda bir işlem gerçekleştirmesi için özel görünümünüzü etkinleştirmektir. Her dokunuşta seçim göstergesi bir sonraki konuma (kapalı-1-2-3) ve tekrar kapalı konumuna geçer. Ayrıca, seçim 1 veya daha yüksekse arka planı griden yeşile çevirerek fan gücünün açık olduğunu gösterin.

Özel görünümünüzün tıklanabilir olmasını sağlamak için:

  • Görünümün isClickable özelliğini true olarak ayarlayın. Bu, özel görünümünüzün tıklamalara yanıt vermesini sağlar.
  • Görünüm tıklandığında işlemler gerçekleştirmek için View sınıfının performClick() yöntemini uygulayın.
  • invalidate() yöntemini çağırın. Bu, Android sistemine görünümü yeniden çizmek için onDraw() yöntemini çağırmasını söyler.

Normalde, standart bir Android görünümünde, kullanıcı bu görünümü tıkladığında bir işlem gerçekleştirmek için OnClickListener() öğesini uygularsınız. Özel bir görünüm için bunun yerine View sınıfının performClick() yöntemini uygular ve super yöntemini çağırırsınız.performClick(). Varsayılan performClick() yöntemi de onClickListener() yöntemini çağırır. Bu nedenle, işlemlerinizi performClick() yöntemine ekleyebilir ve onClickListener() yöntemini, özel görünümünüzü kullanabilecek diğer geliştiriciler veya sizin tarafınızdan daha fazla özelleştirilebilecek şekilde bırakabilirsiniz.

  1. DialView.kt içinde, FanSpeed numaralandırmasında, mevcut fan hızını listedeki bir sonraki hıza (OFF'dan LOW, MEDIUM ve HIGH'ya, ardından tekrar OFF'a) değiştiren bir uzantı işlevi next() ekleyin. Numaralandırmanın tamamı artık şu şekilde görünüyor:
private enum class FanSpeed(val label: Int) {
   OFF(R.string.fan_off),
   LOW(R.string.fan_low),
   MEDIUM(R.string.fan_medium),
   HIGH(R.string.fan_high);

   fun next() = when (this) {
       OFF -> LOW
       LOW -> MEDIUM
       MEDIUM -> HIGH
       HIGH -> OFF
   }
}
  1. DialView sınıfında, onSizeChanged() yönteminden hemen önce bir init() bloğu ekleyin. Görünümün isClickable özelliğini true olarak ayarlamak, görünümün kullanıcı girişini kabul etmesini sağlar.
init {
   isClickable = true
}
  1. init(), altında performClick() yöntemini aşağıdaki kodla geçersiz kılın.
override fun performClick(): Boolean {
   if (super.performClick()) return true

   fanSpeed = fanSpeed.next()
   contentDescription = resources.getString(fanSpeed.label)
  
   invalidate()
   return true
}

super numaralı telefona yapılan arama.performClick() öncelikle gerçekleşmelidir. Bu işlem, erişilebilirlik etkinliklerinin yanı sıra aramaları da etkinleştirir onClickListener().

Sonraki iki satır, next() yöntemiyle vantilatörün hızını artırır ve görünümün içerik açıklamasını mevcut hızı (kapalı, 1, 2 veya 3) temsil eden dize kaynağına ayarlar.

Son olarak, invalidate() yöntemi görünümün tamamını geçersiz kılar ve görünümü yeniden çizmek için onDraw() çağrısı yapmaya zorlar. Özel görünümünüzdeki bir öğe, kullanıcı etkileşimi de dahil olmak üzere herhangi bir nedenle değişirse ve bu değişikliğin gösterilmesi gerekirse invalidate().

  1. Uygulamayı çalıştırın. Göstergeyi kapalı konumundan 1 konumuna getirmek için DialView öğesine dokunun. Çevirme düğmesi yeşile döner. Her dokunuşta gösterge bir sonraki konuma geçmelidir. Gösterge tekrar kapalı duruma geldiğinde kadran tekrar gri renge döner.

Bu örnekte, özel özelliklerin özel görünümünüzle birlikte kullanılmasının temel mekanizmaları gösterilmektedir. DialView sınıfı için her fan kadranı konumu farklı bir renkle özel özellikler tanımlarsınız.

  1. res/values/attrs.xml oluşturun ve açın.
  2. <resources> öğesinin içine bir <declare-styleable> kaynak öğesi ekleyin.
  3. <declare-styleable> Kaynak öğesinin içine, her özellik için bir tane olmak üzere üç attr öğesi ekleyin. Bu öğeler name ve format özelliklerini içermelidir. format, bir tür gibidir ve bu örnekte color'dir.
<?xml version="1.0" encoding="utf-8"?>
<resources>
       <declare-styleable name="DialView">
           <attr name="fanColor1" format="color" />
           <attr name="fanColor2" format="color" />
           <attr name="fanColor3" format="color" />
       </declare-styleable>
</resources>
  1. activity_main.xml düzen dosyasını açın.
  2. DialView bölümünde fanColor1, fanColor2 ve fanColor3 özelliklerini ekleyin ve değerlerini aşağıda gösterilen renkler olarak ayarlayın. Özel özellikleriniz android ad alanı yerine schemas.android.com/apk/res/your_app_package_name ad alanına ait olduğundan, özel özelliğin önsözü olarak android: yerine app: (app:fanColor1 örneğinde olduğu gibi) kullanın.
app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"

DialView sınıfınızdaki özellikleri kullanmak için bunları almanız gerekir. Bu dosyalar, varsa oluşturulduktan sonra sınıfınıza teslim edilen bir AttributeSet içinde saklanır. Özellikleri init içinde alıp özellik değerlerini önbelleğe alma için yerel değişkenlere atarsınız.

  1. DialView.kt sınıf dosyasını açın.
  2. DialView içinde, özellik değerlerini önbelleğe almak için değişkenler bildirin.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0
  1. init bloğuna, withStyledAttributes uzantı işlevini kullanarak aşağıdaki kodu ekleyin. Özellikleri ve görünümü sağlarsınız, yerel değişkenlerinizi ayarlarsınız. withStyledAttributes içe aktarılırken doğru getColor() işlevi de içe aktarılır.
context.withStyledAttributes(attrs, R.styleable.DialView) {
   fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
   fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
   fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}
  1. Arama rengini mevcut fan hızına göre ayarlamak için onDraw() içindeki yerel değişkenleri kullanın. Boya renginin ayarlandığı satırı (paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN) aşağıdaki kodla değiştirin.
paint.color = when (fanSpeed) {
   FanSpeed.OFF -> Color.GRAY
   FanSpeed.LOW -> fanSpeedLowColor
   FanSpeed.MEDIUM -> fanSpeedMediumColor
   FanSpeed.HIGH -> fanSeedMaxColor
} as Int
  1. Uygulamanızı çalıştırın, kadranı tıklayın. Aşağıda gösterildiği gibi, renk ayarı her konum için farklı olmalıdır.

Özel görünüm özellikleri hakkında daha fazla bilgi edinmek için Görünüm sınıfı oluşturma başlıklı makaleyi inceleyin.

Erişilebilirlik, uygulamanızın engelli bireyler de dahil olmak üzere herkes tarafından kullanılabilmesini sağlayan bir dizi tasarım, uygulama ve test tekniğidir.

Kişinin bir Android cihaz kullanmasını etkileyebilecek yaygın engellilikler arasında körlük, az görme, renk körlüğü, sağırlık veya işitme kaybı ve kısıtlı motor beceriler yer alır. Uygulamalarınızı erişilebilirliği göz önünde bulundurarak geliştirdiğinizde, kullanıcı deneyimini yalnızca bu engellere sahip kullanıcılar için değil, diğer tüm kullanıcılarınız için de iyileştirmiş olursunuz.

Android, TextView ve Button gibi standart kullanıcı arayüzü görünümlerinde varsayılan olarak çeşitli erişilebilirlik özellikleri sunar. Ancak özel bir görünüm oluşturduğunuzda, bu özel görünümün ekrandaki içeriğin sesli açıklamaları gibi erişilebilir özellikler sunma şeklini göz önünde bulundurmanız gerekir.

Bu görevde, Android'in ekran okuyucusu TalkBack hakkında bilgi edinecek ve uygulamanızı, DialView özel görünümü için okunabilir ipuçları ve açıklamalar içerecek şekilde değiştireceksiniz.

1. Adım TalkBack'i keşfedin

TalkBack, Android'in yerleşik ekran okuyucusudur. TalkBack etkinleştirildiğinde Android, ekran öğelerini sesli olarak tanımladığından kullanıcı, ekranı görmeden Android cihazıyla etkileşimde bulunabilir. Görme engelli kullanıcılar, uygulamanızı kullanmak için TalkBack'e güvenebilir.

Bu görevde, ekran okuyucuların nasıl çalıştığını ve uygulamalarda nasıl gezineceğinizi anlamak için TalkBack'i etkinleştirirsiniz.

  1. Android cihazda veya emülatörde Ayarlar > Erişilebilirlik > TalkBack'e gidin.
  2. TalkBack'i açmak için Açık/Kapalı açma/kapatma düğmesine dokunun.
  3. İzinleri onaylamak için Tamam'a dokunun.
  4. İstenirse cihaz şifrenizi onaylayın. TalkBack'i ilk kez çalıştırıyorsanız bir eğitim başlatılır. (Eğitim, eski cihazlarda kullanılamayabilir.)
  5. Eğitimde gözleriniz kapalıyken gezinmeniz faydalı olabilir. Eğitimi gelecekte tekrar açmak için Ayarlar > Erişilebilirlik > TalkBack > Ayarlar > TalkBack eğitimini başlat'a gidin.
  6. CustomFanController uygulamasını derleyip çalıştırın veya cihazınızdaki Genel Bakış ya da Son Kullanılanlar düğmesiyle açın. TalkBack açıkken uygulamanın adının ve etiket metninin TextView ("Fan Kontrolü") okunduğunu fark edin. Ancak DialView görünümüne dokunursanız görünümün durumu (çevirme düğmesinin mevcut ayarı) veya görünüm etkinleştirilmek üzere dokunulduğunda gerçekleşecek işlem hakkında herhangi bir bilgi okunmaz.

2. adım: Çevirme etiketleri için içerik açıklamaları ekleme

İçerik açıklamaları, uygulamanızdaki görünümlerin anlamını ve amacını açıklar. Bu etiketler, Android'in TalkBack özelliği gibi ekran okuyucuların her öğenin işlevini doğru bir şekilde açıklamasını sağlar. ImageView gibi statik görünümler için contentDescription özelliğini kullanarak içerik açıklamasını düzen dosyasındaki görünüme ekleyebilirsiniz. Metin görünümleri (TextView ve EditText), görünümdeki metni otomatik olarak içerik açıklaması olarak kullanır.

Özel fan kontrolü görünümünde, mevcut fan ayarını belirtmek için görünüm her tıklandığında içerik açıklamasını dinamik olarak güncellemeniz gerekir.

  1. DialView sınıfının en altında, bağımsız değişkeni veya dönüş türü olmayan bir işlev updateContentDescription() tanımlayın.
fun updateContentDescription() {
}
  1. updateContentDescription() içinde, özel görünümün contentDescription özelliğini mevcut fan hızıyla (kapalı, 1, 2 veya 3) ilişkili dize kaynağıyla değiştirin. Bunlar, ekranda kadran çizilirken onDraw() içinde kullanılan etiketlerle aynıdır.
fun updateContentDescription() {
   contentDescription = resources.getString(fanSpeed.label)
}
  1. init() bloğuna gidin ve bu bloğun sonuna updateContentDescription() çağrısı ekleyin. Bu, görünüm başlatıldığında içerik açıklamasını başlatır.
init {
   isClickable = true
   // ...

   updateContentDescription()
}
  1. updateContentDescription() yönteminde, invalidate()'den hemen önce performClick()'ye başka bir çağrı ekleyin.
override fun performClick(): Boolean {
   if (super.performClick()) return true
   fanSpeed = fanSpeed.next()
   updateContentDescription()
   invalidate()
   return true
}
  1. Uygulamayı derleyip çalıştırın ve TalkBack'in etkinleştirildiğinden emin olun. Çevirme görünümü ayarını değiştirmek için dokunun. TalkBack'in artık geçerli etiketi (kapalı, 1, 2, 3) ve "Etkinleştirmek için iki kez dokunun" ifadesini duyurduğunu fark edin.

3. Adım: Tıklama işlemi için daha fazla bilgi ekleme

Bu noktada durabilirsiniz. Görünümünüz TalkBack'te kullanılabilir. Ancak görünümünüzün yalnızca etkinleştirilebileceğini ("Etkinleştirmek için çift dokunun") değil, etkinleştirildiğinde ne olacağını da ("Değiştirmek için çift dokunun" veya "Sıfırlamak için çift dokunun") belirtmesi faydalı olur.

Bunu yapmak için, erişilebilirlik temsilcisi aracılığıyla, görünümün işlemiyle (burada tıklama veya dokunma işlemi) ilgili bilgileri bir AccessibilityNodeInfo nesnesine eklersiniz. Erişilebilirlik temsilcisi, uygulamanızın erişilebilirlikle ilgili özelliklerini kompozisyon (kalıtım yerine) aracılığıyla özelleştirmenize olanak tanır.

Bu görevde, geriye dönük uyumluluğu sağlamak için Android Jetpack kitaplıklarındaki erişilebilirlik sınıflarını (androidx.*) kullanacaksınız.

  1. DialView.kt içinde, init bloğunda, görünümde yeni bir AccessibilityDelegateCompat nesnesi olarak erişilebilirlik temsilcisi ayarlayın. İstendiğinde androidx.core.view.ViewCompat ve androidx.core.view.AccessibilityDelegateCompat öğelerini içe aktarın. Bu strateji, uygulamanızda en fazla geriye dönük uyumluluğu sağlar.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   
})
  1. AccessibilityDelegateCompat nesnesinin içinde, onInitializeAccessibilityNodeInfo() işlevini AccessibilityNodeInfoCompat nesnesiyle geçersiz kılın ve üst sınıfın yöntemini çağırın. İstendiğinde androidx.core.view.accessibility.AccessibilityNodeInfoCompat içe aktarın.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)

   }  
})

Her görünümde, görünümün gerçek düzen bileşenlerine karşılık gelebilen veya gelmeyebilen bir erişilebilirlik düğümleri ağacı bulunur. Android'in erişilebilirlik hizmetleri, görünümle ilgili bilgileri (ör. okunabilir içerik açıklamaları veya görünümde yapılabilecek olası işlemler) öğrenmek için bu düğümlerde gezinir. Özel görünüm oluşturduğunuzda, erişilebilirlik için özel bilgiler sağlamak üzere düğüm bilgilerini de geçersiz kılmanız gerekebilir. Bu durumda, görünümün işlemi için özel bilgiler olduğunu belirtmek üzere düğüm bilgilerini geçersiz kılarsınız.

  1. onInitializeAccessibilityNodeInfo() içinde yeni bir AccessibilityNodeInfoCompat.AccessibilityActionCompat nesnesi oluşturun ve bunu customClick değişkenine atayın. Oluşturucuya AccessibilityNodeInfo.ACTION_CLICK sabitini ve bir yer tutucu dizesini iletin. İstendiğinde AccessibilityNodeInfo içe aktarın.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)
      val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
         AccessibilityNodeInfo.ACTION_CLICK,
        "placeholder"
      )
   }  
})

AccessibilityActionCompat sınıfı, erişilebilirlik amacıyla bir görünümdeki işlemi temsil eder. Burada kullandığınız gibi, tipik bir işlem tıklama veya dokunmadır ancak diğer işlemler arasında odağı kazanma veya kaybetme, pano işlemi (kesme/kopyalama/yapıştırma) ya da görünüm içinde kaydırma yer alabilir. Bu sınıfın oluşturucusu için bir işlem sabiti (burada AccessibilityNodeInfo.ACTION_CLICK) ve TalkBack tarafından işlemin ne olduğunu belirtmek için kullanılan bir dize gerekir.

  1. "placeholder" dizesini, dize kaynağını almak için context.getString() çağrısıyla değiştirin. Belirli bir kaynak için mevcut fan hızını test edin. Hız şu anda FanSpeed.HIGH ise dize "Reset" olur. Fan hızı başka bir değerse dize "Change." olur. Bu dize kaynaklarını daha sonraki bir adımda oluşturacaksınız.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)
      val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
         AccessibilityNodeInfo.ACTION_CLICK,
        context.getString(if (fanSpeed !=  FanSpeed.HIGH) R.string.change else R.string.reset)
      )
   }  
})
  1. customClick tanımının kapanış parantezinden sonra, yeni erişilebilirlik işlemini düğüm bilgisi nesnesine eklemek için addAction() yöntemini kullanın.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
       super.onInitializeAccessibilityNodeInfo(host, info)
       val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
           AccessibilityNodeInfo.ACTION_CLICK,
           context.getString(if (fanSpeed !=  FanSpeed.HIGH) 
                                 R.string.change else R.string.reset)
       )
       info.addAction(customClick)
   }
})
  1. res/values/strings.xml içinde "Change" (Değiştir) ve "Reset" (Sıfırla) için dize kaynaklarını ekleyin.
<string name="change">Change</string>
<string name="reset">Reset</string>
  1. Uygulamayı derleyip çalıştırın ve TalkBack'in etkinleştirildiğinden emin olun. "Etkinleştirmek için iki kez dokunun" ifadesinin artık "Değiştirmek için iki kez dokunun" (vantilatör hızı yüksekten veya 3'ten düşükse) ya da "Sıfırlamak için iki kez dokunun" (vantilatör hızı zaten yüksek veya 3'teyse) olarak değiştiğini fark edeceksiniz. "... için iki kez dokunun" isteminin TalkBack hizmeti tarafından sağlandığını unutmayın.

Tamamlanmış codelab'in kodunu indirin.

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-custom-views


Alternatif olarak, depoyu Zip dosyası olarak indirebilir, dosyayı açıp Android Studio'da açabilirsiniz.

Zip dosyasını indir

  • View alt sınıfının görünümünü ve davranışını devralan özel bir görünüm oluşturmak için (ör. EditText) bu alt sınıfı genişleten yeni bir sınıf ekleyin ve alt sınıfın bazı yöntemlerini geçersiz kılarak ayarlamalar yapın.
  • Herhangi bir boyut ve şekilde özel bir görünüm oluşturmak için View sınıfını genişleten yeni bir sınıf ekleyin.
  • Görünümün şeklini ve temel görünümünü tanımlamak için onDraw() gibi View yöntemlerini geçersiz kılın.
  • Görünümün çizilmesini veya yeniden çizilmesini zorlamak için invalidate() simgesini kullanın.
  • Performansı optimize etmek için değişkenleri ayırın ve onDraw() içinde kullanmadan önce çizim ve boyama için gereken değerleri atayın (ör. üye değişkenlerinin başlatılması).
  • Görünümün etkileşimli davranışını sağlamak için özel görünümde OnClickListener() yerine performClick() işlevini geçersiz kılın. Bu, sizin veya özel görünüm sınıfınızı kullanabilecek diğer Android geliştiricilerin onClickListener() kullanarak daha fazla davranış sağlamasına olanak tanır.
  • Özel görünümü, diğer kullanıcı arayüzü öğelerinde olduğu gibi görünümünü tanımlayan özelliklerle birlikte bir XML düzen dosyasına ekleyin.
  • Özel özellikleri tanımlamak için attrs.xml klasöründe values dosyasını oluşturun. Daha sonra, XML düzen dosyasında özel görünüm için özel özellikleri kullanabilirsiniz.

Udacity kursu:

Android geliştirici belgeleri:

Videolar:

Bu bölümde, bir eğitmenin yönettiği kurs kapsamında bu codelab'i tamamlayan öğrenciler için olası ödevler listelenmektedir. Eğitmen, aşağıdakileri yapmalıdır:

  • Gerekirse ödev atayın.
  • Öğrencilere ev ödevi ödevlerini nasıl göndereceklerini bildirin.
  • Ödevlere not verin.

Eğitmenler bu önerileri istedikleri kadar kullanabilir ve uygun olduğunu düşündükleri diğer ödevleri verebilirler.

Bu codelab'i kendi başınıza tamamlıyorsanız bilginizi test etmek için bu ödevleri kullanabilirsiniz.

1. Soru

Özel görünüm ilk kez bir boyuta atandığında konumları, boyutları ve diğer değerleri hesaplamak için hangi yöntemi geçersiz kılarsınız?

onMeasure()

onSizeChanged()

invalidate()

onDraw()

2. Soru

Görünümünüzün onDraw() ile yeniden çizilmesini istediğinizi belirtmek için bir özellik değeri değiştiğinde kullanıcı arayüzü iş parçacığından hangi yöntemi çağırırsınız?

▢ onMeasure()

▢ onSizeChanged()

▢ invalidate()

▢ getVisibility()

3. Soru

Özel görünümünüze etkileşim eklemek için hangi View yöntemini geçersiz kılmalısınız?

▢ setOnClickListener()

▢ onSizeChanged()

▢ isClickable()

▢ performClick()

Bu kurstaki diğer codelab'lerin bağlantıları için Advanced Android in Kotlin codelab'lerinin açılış sayfasına bakın.