Özel görünümlerin en önemli kısmı görünümüdür. Özel çizim uygulamanızın ihtiyaçlarına göre kolay veya karmaşık olabilir. Bu doküman en yaygın işlemlerden bazılarını ele almaktadır.
Daha fazla bilgi için bkz. Çekilebilir öğelere genel bakış
onDraw() öğesini geçersiz kıl
Özel bir görünüm çizmenin en önemli adımı,
onDraw()
.
yöntemidir. onDraw()
parametresi
Canvas
görünümün kendini çizmek için kullanabileceği
nesnedir. Canvas
sınıfı
metin, çizgiler, bit eşlemler ve daha birçok grafik çizme yöntemlerini tanımlar
ilkel maddelerdir. onDraw()
uygulamasında bu yöntemleri kullanarak
özel kullanıcı arayüzü.
Öncelikle
Paint
nesne algılandı.
Sonraki bölümde Paint
daha ayrıntılı olarak ele alınmaktadır.
Çizim nesneleri oluşturma
İlgili içeriği oluşturmak için kullanılan
android.graphics
.
çerçeve, çizimi iki alana ayırır:
- Ne çizilecek?
Canvas
tarafından ele alınır. - Çizim,
Paint
tarafından işlenir.
Örneğin, Canvas
çizgi çizmek için bir yöntem sağlar ve
Paint
, çizginin rengini tanımlamak için kullanılan yöntemler sunar.
Canvas
, dikdörtgen çizmek için bir yönteme sahip ve Paint
o dikdörtgenin bir renkle doldurulup doldurulmayacağını belirler.
Canvas
, ekranda çizebileceğiniz şekilleri tanımlar ve
Paint
, her şeklin rengini, stilini, yazı tipini ve diğer özellikleri tanımlar
anlamına gelir.
Herhangi bir şey çizmeden önce bir veya daha fazla Paint
nesnesi oluşturun. İlgili içeriği oluşturmak için kullanılan
aşağıdaki örnekte bunu init
adlı bir yöntemde yapılmaktadır. Bu yöntem
kurucusu tarafından çağrılmakla birlikte, satır içinde başlatılabilir.
Kotlin.
Kotlin
@ColorInt private var textColor // Obtained from style attributes. @Dimension private var textHeight // Obtained from style attributes. private val textPaint = Paint(ANTI_ALIAS_FLAG).apply { color = textColor if (textHeight == 0f) { textHeight = textSize } else { textSize = textHeight } } private val piePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL textSize = textHeight } private val shadowPaint = Paint(0).apply { color = 0x101010 maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL) }
Java
private Paint textPaint; private Paint piePaint; private Paint shadowPaint; @ColorInt private int textColor; // Obtained from style attributes. @Dimension private float textHeight; // Obtained from style attributes. private void init() { textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(textColor); if (textHeight == 0) { textHeight = textPaint.getTextSize(); } else { textPaint.setTextSize(textHeight); } piePaint = new Paint(Paint.ANTI_ALIAS_FLAG); piePaint.setStyle(Paint.Style.FILL); piePaint.setTextSize(textHeight); shadowPaint = new Paint(0); shadowPaint.setColor(0xff101010); shadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL)); ... }
Nesneleri önceden oluşturmak önemli bir optimizasyondur. Görüntüleme sayısı
tekrar çizilir ve birçok çizim nesnesi pahalı başlatma işlemi gerektirir.
onDraw()
yönteminizde önemli ölçüde çizim nesneleri oluşturma
performansı düşürür ve kullanıcı arayüzünüzü yavaşlatabilir.
Düzen etkinliklerini işleme
Özel görünümünüzü doğru şekilde çizmek için görünümünün ne olduğunu bulun. Karmaşık özel görünümlerin boyutuna bağlı olarak, genellikle birden çok düzen hesaplaması yapması gerekir alanı şekline karar verebilir. Çıkarınızın boyutu hakkında varsayımlarda bulunmayın. ekranda görüntüleyin. Görünümünüzü sadece tek bir uygulama kullansa bile Farklı ekran boyutlarını, birden fazla ekran yoğunluğunu ve çeşitli en boy oranlarını ele almak hem dikey hem de yatay modda çalışır.
View
olmasına rağmen
ölçüm için birçok yöntem sunar. Ancak bunların çoğu,
geçersiz kılınır. Görünümünüz, boyutu üzerinde özel bir denetime ihtiyaç duymuyorsa
yöntemi geçersiz kıl:
onSizeChanged()
onSizeChanged()
, görünümünüze ilk kez bir
herhangi bir nedenle görünümünüzün boyutu değişirse boyutu da görebilirsiniz. Hesaplama
konumları, boyutları ve görüntülemenizin boyutuyla ilgili diğer tüm değerleri
onSizeChanged()
kullanarak her çiziminizde yeniden hesaplamanız gerekmez.
Aşağıdaki örnekte onSizeChanged()
, görünümün
grafiğin sınırlayıcı dikdörtgenini ve
metin etiketi ve diğer görsel öğelerden oluşmalıdır.
Görünümünüze bir boyut atandığında, düzen yöneticisi bu boyutun
görünümün dolgusunu içerir.
görünümünün boyutudur. onSizeChanged()
sitesinde bu programın nasıl çalıştığını gösteren bir snippet
Bunun için:
Kotlin
private val showText // Obtained from styled attributes. private val textWidth // Obtained from styled attributes. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) // Account for padding. var xpad = (paddingLeft + paddingRight).toFloat() val ypad = (paddingTop + paddingBottom).toFloat() // Account for the label. if (showText) xpad += textWidth.toFloat() val ww = w.toFloat() - xpad val hh = h.toFloat() - ypad // Figure out how big you can make the pie. val diameter = Math.min(ww, hh) }
Java
private Boolean showText; // Obtained from styled attributes. private int textWidth; // Obtained from styled attributes. @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Account for padding. float xpad = (float)(getPaddingLeft() + getPaddingRight()); float ypad = (float)(getPaddingTop() + getPaddingBottom()); // Account for the label. if (showText) xpad += textWidth; float ww = (float)w - xpad; float hh = (float)h - ypad; // Figure out how big you can make the pie. float diameter = Math.min(ww, hh); }
Görünümünüzün düzen parametreleri üzerinde daha ayrıntılı denetime ihtiyacınız varsa
onMeasure()
Bu yöntemin parametreleri
View.MeasureSpec
.
ebeveynlerinin görünümünüzün ne kadar büyük olmasını istediğini belirten
sabit bir maksimum değer mi yoksa sadece bir öneri mi olduğunu
kontrol edebilirsiniz. Optimizasyon olarak,
bu değerler, paketlenmiş tam sayılar olarak depolanır ve statik yöntemleri kullanarak
View.MeasureSpec
tuşlarına basın.
Aşağıda, onMeasure()
uygulamasının bir örneği verilmiştir. Burada
alanını, grafiği olabildiğince büyük hale getirecek kadar büyük
kullanın:
Kotlin
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Try for a width based on your minimum. val minw: Int = paddingLeft + paddingRight + suggestedMinimumWidth val w: Int = View.resolveSizeAndState(minw, widthMeasureSpec, 1) // Whatever the width is, ask for a height that lets the pie get as big as // it can. val minh: Int = View.MeasureSpec.getSize(w) - textWidth.toInt() + paddingBottom + paddingTop val h: Int = View.resolveSizeAndState(minh, heightMeasureSpec, 0) setMeasuredDimension(w, h) }
Java
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Try for a width based on your minimum. int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth(); int w = resolveSizeAndState(minw, widthMeasureSpec, 1); // Whatever the width is, ask for a height that lets the pie get as big as it // can. int minh = MeasureSpec.getSize(w) - (int)textWidth + getPaddingBottom() + getPaddingTop(); int h = resolveSizeAndState(minh, heightMeasureSpec, 0); setMeasuredDimension(w, h); }
Bu kodda dikkat edilmesi gereken üç önemli nokta vardır:
- Hesaplamalar, görünümün dolgusunu dikkate alır. Bahsedildiği gibi görünümün sorumluluğundadır.
- Yardımcı yöntem
resolveSizeAndState()
. değeri, nihai genişlik ve yükseklik değerlerini oluşturmak için kullanılır. Bu yardımcı geri döndü uygun birView.MeasureSpec
değeri belirlemek için görünümünün gerekli boyutunuonMeasure()
içine aktarılan değere uygular. onMeasure()
için döndürülen değer yok. Bunun yerine sonuçlarını müşteriye telefon edereksetMeasuredDimension()
Bu yöntemin çağrılması zorunludur. Bu çağrıyı çıkarırsanızView
sınıfı, çalışma zamanı istisnası bildiriyor.
Çiz
Nesne oluşturma ve ölçüm kodunuzu tanımladıktan sonra,
onDraw()
Her görünüm onDraw()
uygulamasını farklı şekilde uygular,
ancak çoğu görüntülemenin ortak olduğu bazı yaygın işlemler vardır:
- Şunu kullanarak metin çizin:
drawText()
Çağrı yaparak yazı karakterini belirtmesetTypeface()
. ve metin rengini çağıraraksetColor()
. - İlkel şekilleri çizmek için:
drawRect()
,drawOval()
, vedrawArc()
. Şekillerin dolgulu, dış çizgili veya her ikisini birden çağırarak şekillerin durumunu değiştirebilirsiniz:setStyle()
- Boşlukları kullanarak daha karmaşık şekiller çizin
Path
. sınıfını kullanır.Path
öğesine çizgi ve eğri ekleyerek bir şekli tanımlayın ardından şeklidrawPath()
. İlkel şekillerde olduğu gibi, yollar dış çizgilerle doldurulabilir, doldurulabilir veya her ikisi birden hizmet:setStyle()
-
Gradyan dolgularını
LinearGradient
oluşturarak tanımlayın nesneler'i tıklayın. Telefonla aramasetShader()
.LinearGradient
efektinizi dolgulu şekillerde kullanın. - Şununla bit eşlemler çizin:
drawBitmap()
Aşağıdaki kod metin, çizgi ve şekillerin bir karışımını çizer:
Kotlin
private val data = mutableListOf<Item>() // A list of items that are displayed. private var shadowBounds = RectF() // Calculated in onSizeChanged. private var pointerRadius: Float = 2f // Obtained from styled attributes. private var pointerX: Float = 0f // Calculated in onSizeChanged. private var pointerY: Float = 0f // Calculated in onSizeChanged. private var textX: Float = 0f // Calculated in onSizeChanged. private var textY: Float = 0f // Calculated in onSizeChanged. private var bounds = RectF() // Calculated in onSizeChanged. private var currentItem: Int = 0 // The index of the currently selected item. override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.apply { // Draw the shadow. drawOval(shadowBounds, shadowPaint) // Draw the label text. drawText(data[currentItem].label, textX, textY, textPaint) // Draw the pie slices. data.forEach {item -> piePaint.shader = item.shader drawArc( bounds, 360 - item.endAngle, item.endAngle - item.startAngle, true, piePaint ) } // Draw the pointer. drawLine(textX, pointerY, pointerX, pointerY, textPaint) drawCircle(pointerX, pointerY, pointerRadius, textPaint) } } // Maintains the state for a data item. private data class Item( var label: String, var value: Float = 0f, @ColorInt var color: Int = 0, // Computed values. var startAngle: Float = 0f, var endAngle: Float = 0f, var shader: Shader )
Java
private List<Item> data = new ArrayList<Item>(); // A list of items that are displayed. private RectF shadowBounds; // Calculated in onSizeChanged. private float pointerRadius; // Obtained from styled attributes. private float pointerX; // Calculated in onSizeChanged. private float pointerY; // Calculated in onSizeChanged. private float textX; // Calculated in onSizeChanged. private float textY; // Calculated in onSizeChanged. private RectF bounds; // Calculated in onSizeChanged. private int currentItem = 0; // The index of the currently selected item. protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the shadow. canvas.drawOval( shadowBounds, shadowPaint ); // Draw the label text. canvas.drawText(data.get(currentItem).label, textX, textY, textPaint); // Draw the pie slices. for (int i = 0; i < data.size(); ++i) { Item it = data.get(i); piePaint.setShader(it.shader); canvas.drawArc( bounds, 360 - it.endAngle, it.endAngle - it.startAngle, true, piePaint ); } // Draw the pointer. canvas.drawLine(textX, pointerY, pointerX, pointerY, textPaint); canvas.drawCircle(pointerX, pointerY, pointerRadius, textPaint); } // Maintains the state for a data item. private class Item { public String label; public float value; @ColorInt public int color; // Computed values. public int startAngle; public int endAngle; public Shader shader; }
Grafik efektleri uygula
Android 12 (API düzeyi 31),
RenderEffect
.
ve renk filtreleri gibi yaygın grafik efektlerinin uygulandığı,
Android gölgelendirici efektleri ve daha fazlası
View
nesne ve
hiyerarşik olarak hazırlanır. Efektleri zincir efektler olarak birleştirebilirsiniz.
bir iç ve dış efektin ya da karma efektlerin. Bu özellik için destek
işlem gücüne bağlı olarak değişir.
Arka plandaki temel öğelere de
Şunun için RenderNode
:
arayarak View
için arama yapın
View.setRenderEffect(RenderEffect)
.
Bir RenderEffect
nesnesini uygulamak için aşağıdakileri yapın:
view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))
Görünümü programatik olarak oluşturabilir veya bir XML düzeninden şişirerek
Bağlamı görüntüle'yi kullanarak dosyayı alın veya
findViewById()
.