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
Viewnası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
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:
Viewsınıfını veyaViewalt sınıfını (ör.ButtonveyaEditText) genişleten özel bir görünüm sınıfı oluşturun.- Mevcut bir
Viewalt 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. Viewsınıfını genişletirseniz yeni sınıftaonDraw()veonMeasure()gibiViewyö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
ImageViewsimgesini içeren bir uygulama oluşturun. - Özel görünümü oluşturmak için
Viewsimgesini 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
- Empty Activity şablonunu kullanarak
CustomFanControllerbaşlıklı bir Kotlin uygulaması oluşturun. Paket adınıncom.example.android.customfancontrollerolduğundan emin olun. - XML kodunu düzenlemek için Metin sekmesinde
activity_main.xmlsimgesini açın. - Mevcut
TextViewkodunu 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"/>- 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"/>- Her iki kullanıcı arayüzü öğesinde de dize ve boyut kaynaklarını ayıklayın.
- 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
DialViewadlı yeni bir Kotlin sınıfı oluşturun.Viewdeğerini uzatmak için sınıf tanımını değiştirin. İstendiğindeandroid.view.Viewiçe aktarın.Viewsimgesini 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,Viewsınıfından oluşturucuyu ekler.@JvmOverloadsek 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) {DialViewsı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 birenumekleyin. Değerler gerçek dizeler değil, dize kaynakları olduğundan buenumöğesininInttü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);
}enumsimgesinin 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 = -35DialViewsınıfında, özel görünümü çizmek için ihtiyacınız olan birkaç değişkeni tanımlayın. İstenirseandroid.graphics.PointFiç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,FanSpeednumaralandırmasındaki değerlerden biri olan fanın mevcut hızıdır. Varsayılan olarak bu değerOFF'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.
- Ayrıca
DialViewsınıf tanımının içinde,Paintnesnesini birkaç temel stil ile başlatın. İstendiğindeandroid.graphics.Paintveandroid.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)
}res/values/strings.xmldosyası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çinPaintnesnesiyle stilize edilmiş birCanvasnesnesi 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çinonDraw()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()vedrawArc()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
DialViewsınıfında, başlatmaların altında, özel görünümün kadranının boyutunu hesaplamak içinViewsınıfındakionSizeChanged()yöntemini geçersiz kılın.kotliniçe aktarın.math.ministendiğ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çinonSizeChanged()işlevini geçersiz kılın. Bu durumda, arama çubuğunun daire öğesinin geçerli yarıçapını hesaplamak içinonSizeChanged()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()
}onSizeChanged()bölümünün altına,PointFsınıfı için bircomputeXYForSpeed()uzantı işlevi tanımlamak üzere bu kodu ekleyin. İstendiğindekotlin.math.cosvekotlin.math.sinöğelerini içe aktarın.PointFsınıfındaki bu uzantı işlevi, mevcutFanSpeedkonumu 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. BunuonDraw().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
}- Görünümü ekranda
CanvasvePaintsınıflarıyla oluşturmak içinonDraw()yöntemini geçersiz kılın. İstendiğindeandroid.graphics.Canvasiçe aktarın. Bu, iskelet geçersiz kılma işlemidir:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}onDraw()içinde, boya rengini fan hızınınOFFolup 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ğindeandroid.graphics.Coloriçe aktarın.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN- 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.widthveheightö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)- 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ımdaPointFkullanılır.Gösterge merkezinin X ve Y koordinatlarını mevcut fan hızına göre hesaplamak içincomputeXYforSpeed()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)- 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çinpointPositionnesnesini her seferinde yeniden kullanır. Etiketleri çizmek içindrawText()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.
activity_main.xmliçinde,dialViewiçinImageViewetiketinicom.example.android.customfancontroller.DialViewolarak değiştirin veandroid:backgroundözelliğini silin. HemDialViewhem de orijinalImageView,Viewsınıfındaki standart özellikleri devralır. Bu nedenle diğer özelliklerde herhangi bir değişiklik yapmanız gerekmez. YeniDialViewöğ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" />- 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ğinitrueolarak 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
ViewsınıfınınperformClick()yöntemini uygulayın. invalidate()yöntemini çağırın. Bu, Android sistemine görünümü yeniden çizmek içinonDraw()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.
DialView.ktiçinde,FanSpeednumaralandırmasında, mevcut fan hızını listedeki bir sonraki hıza (OFF'danLOW,MEDIUMveHIGH'ya, ardından tekrarOFF'a) değiştiren bir uzantı işlevinext()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
}
}DialViewsınıfında,onSizeChanged()yönteminden hemen önce birinit()bloğu ekleyin. GörünümünisClickableözelliğini true olarak ayarlamak, görünümün kullanıcı girişini kabul etmesini sağlar.
init {
isClickable = true
}init(),altındaperformClick()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().
- 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.
res/values/attrs.xmloluşturun ve açın.<resources>öğesinin içine bir<declare-styleable>kaynak öğesi ekleyin.<declare-styleable>Kaynak öğesinin içine, her özellik için bir tane olmak üzere üçattröğesi ekleyin. Bu öğelernameveformatözelliklerini içermelidir.format, bir tür gibidir ve bu örnektecolor'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>activity_main.xmldüzen dosyasını açın.DialViewbölümündefanColor1,fanColor2vefanColor3özelliklerini ekleyin ve değerlerini aşağıda gösterilen renkler olarak ayarlayın. Özel özelliklerinizandroidad alanı yerineschemas.android.com/apk/res/your_app_package_namead alanına ait olduğundan, özel özelliğin önsözü olarakandroid:yerineapp:(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.
DialView.ktsınıf dosyasını açın.DialViewiçinde, özellik değerlerini önbelleğe almak için değişkenler bildirin.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0initbloğuna,withStyledAttributesuzantı 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.withStyledAttributesiçe aktarılırken doğrugetColor()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)
}- 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.GRAYelseColor.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- 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.
- Android cihazda veya emülatörde Ayarlar > Erişilebilirlik > TalkBack'e gidin.
- TalkBack'i açmak için Açık/Kapalı açma/kapatma düğmesine dokunun.
- İzinleri onaylamak için Tamam'a dokunun.
- İ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.)
- 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.
CustomFanControlleruygulaması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 metnininTextView("Fan Kontrolü") okunduğunu fark edin. AncakDialViewgö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.
DialViewsınıfının en altında, bağımsız değişkeni veya dönüş türü olmayan bir işlevupdateContentDescription()tanımlayın.
fun updateContentDescription() {
}updateContentDescription()içinde, özel görünümüncontentDescriptionözelliğini mevcut fan hızıyla (kapalı, 1, 2 veya 3) ilişkili dize kaynağıyla değiştirin. Bunlar, ekranda kadran çizilirkenonDraw()içinde kullanılan etiketlerle aynıdır.
fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}init()bloğuna gidin ve bu bloğun sonunaupdateContentDescription()ç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()
}updateContentDescription()yönteminde,invalidate()'den hemen önceperformClick()'ye başka bir çağrı ekleyin.
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}- 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.
DialView.ktiçinde,initbloğunda, görünümde yeni birAccessibilityDelegateCompatnesnesi olarak erişilebilirlik temsilcisi ayarlayın. İstendiğindeandroidx.core.view.ViewCompatveandroidx.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() {
})AccessibilityDelegateCompatnesnesinin içinde,onInitializeAccessibilityNodeInfo()işleviniAccessibilityNodeInfoCompatnesnesiyle geçersiz kılın ve üst sınıfın yöntemini çağırın. İstendiğindeandroidx.core.view.accessibility.AccessibilityNodeInfoCompatiç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.
onInitializeAccessibilityNodeInfo()içinde yeni birAccessibilityNodeInfoCompat.AccessibilityActionCompatnesnesi oluşturun ve bunucustomClickdeğişkenine atayın. OluşturucuyaAccessibilityNodeInfo.ACTION_CLICKsabitini ve bir yer tutucu dizesini iletin. İstendiğindeAccessibilityNodeInfoiç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.
"placeholder"dizesini, dize kaynağını almak içincontext.getString()çağrısıyla değiştirin. Belirli bir kaynak için mevcut fan hızını test edin. Hız şu andaFanSpeed.HIGHise 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)
)
}
})customClicktanımının kapanış parantezinden sonra, yeni erişilebilirlik işlemini düğüm bilgisi nesnesine eklemek içinaddAction()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)
}
})res/values/strings.xmliç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>- 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.
Viewalt 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
Viewsı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()gibiViewyö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() yerineperformClick()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ştiricilerinonClickListener()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.xmlklasöründevaluesdosyası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:
- Özel Görünümler Oluşturma
@JvmOverloads- Özel Bileşenler
- Android'de Görünümler Nasıl Çizilir?
onMeasure()onSizeChanged()onDraw()CanvasPaintdrawText()setTypeface()setColor()drawRect()drawOval()drawArc()drawBitmap()setStyle()invalidate()- Görünüm
- Giriş Etkinlikleri
- Paint
- Kotlin uzantı kitaplığı android-ktx
withStyledAttributes- Android KTX belgeleri
- Android KTX ilk duyurusu blogu
- Özel görünümleri daha erişilebilir hale getirme
AccessibilityDelegateCompatAccessibilityNodeInfoCompatAccessibilityNodeInfoCompat.AccessibilityActionCompat
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.



