Android: Filtrelerle StateListDrawable yapmak için bir çekilebilir klonlama


92

Herhangi bir Drawable'ın basıldığında / odaklandığında / seçildiğinde / vb . Vurgulanmasını sağlayan genel bir çerçeve işlevi yapmaya çalışıyorum .

Benim fonksiyonum bir Drawable alır ve bir StateListDrawable döndürür, burada varsayılan durum Drawable'ın kendisidir ve durum android.R.attr.state_pressed, yalnızca bir filtre kullanılarak uygulanarak aynı çekilebilirdir setColorFilter.

Benim sorunum, çekilebilir dosyayı klonlayamıyorum ve filtre uygulandığında bunun ayrı bir örneğini yapamıyorum. İşte başarmaya çalıştığım şey:

StateListDrawable makeHighlightable(Drawable drawable)
{
    StateListDrawable res = new StateListDrawable();

    Drawable clone = drawable.clone(); // how do I do this??

    clone.setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
    res.addState(new int[] {android.R.attr.state_pressed}, clone);
    res.addState(new int[] { }, drawable);
    return res;
}

Klonlamazsam, filtre açıkça her iki duruma da uygulanır. Oynamayı denedim mutate()ama yardımcı olmadı ..

Herhangi bir fikir?

Güncelleme:

Kabul edilen cevap gerçekten de bir çekilebilirliği klonluyor. Yine de bana yardımcı olmadı çünkü genel işlevim farklı bir problemde başarısız oldu. Görünüşe göre bir StateList'e bir çekilebilir öğe eklediğinizde, tüm filtrelerini kaybediyor.


Merhaba, çekmecelerin filtreleri kaybetmesine bir çözüm buldunuz mu? Aynı sorunla karşılaştım :( Bitmap'i klonlayarak ve piksel piksel filtre uygulayarak kaynak görüntüden başka bir görüntü oluşturmaya başladım. Evet, bu verimsiz, ancak bir kez işlenmiş sadece bir sürü küçük
resmim var

StateListDrawable ile çözemedim, ancak StateListDrawable'ı kullanmıyorsanız ve hala filtrelerinizi kaybediyorsanız, bitmaplerinizin değiştirilebilir olduğundan emin olun. Bununla ilgili iyi sorular var: stackoverflow.com/questions/5499637/… , ayrıca LightingColorFilter'ın PorterDuff'ın başarısız olduğu yerlerde çalıştığını keşfettim .. bu android'i
seviyorum

bu bağlantıya harika bir cevap stackoverflow.com/questions/10889415/…
Alan

ImageView.setImageDrawableKabul edilen cevap sayesinde etrafta çalışabildiğim benzer bir yan etki tetikledi .
Giulio Piancastelli

Ben de aynı şeyi yapmaya çalışıyorum ve bir şekilde beklendiği gibi çalışıyor, ColorFilter kaybolmadı ... Aradaki fark, çekilebilir olanı değiştirmiş olmam.
Henry

Yanıtlar:


164

Takip etmeyi dene:

Drawable clone = drawable.getConstantState().newDrawable();

1
Teşekkürler! Bu yöntem, bir çekilebilirliği başarıyla klonlamış görünüyor. Yazmaya çalıştığım işlev işe yaramıyor .. Görünüşe göre bir StateList'e bir çekilebilir dosya eklendiğinde filtrelerini kaybediyor :(
talkol

3
Bir AlertDialog'da ItemizedOverlay'den bir Drawable'ı yeniden kullanmanın, tetiklendiğinde ItemizedOverlay'i hareket ettirdiği MapView'daki çok garip bir hatayı düzeltmeme yardımcı olan +1. Drawable'ın yeni bir örneğini oluşturmak sorunu çözdü.
kskjon

9
SetAlpha yöntemini kullanmayı denersek, doğru şekilde çalışsın. Bu durumda her iki çekilebilir bit eşlemi değiştirir. Sonra ilk çekilebilirliği getResources (). GetDrawable (), ikincisi ise getResources (). GetDrawable (). Mutate () olarak alıyorum.
Yura Shinkarev

Çok teşekkürler, API Mapsforge'dan bir sınırlayıcı işlevi uyguladığımda yaşadığım sorunu çözdü. Artık çekmeceleri her yerde başarıyla kullanabiliyorum!
xarlymg89

18
@Flavio - Bunu bir renk filtresiyle denedim, ancak çekilebilirliğimin tüm örneklerini renklendirdi! Görünüşe göre kullanmak zorundasın .mutate()( cevabıma bakın).
Peter Ajtai

106

Oluşturulan bir çekilebilir dosyaya bir filtre / vb uygularsanız getConstantState().newDrawable(), çekilebilir öğeler constantStateönbellek olarak kullanıldığından, bu çekilebilir dosyanın tüm örnekleri de değiştirilecektir !

Dolayısıyla, bir renk filtresi ve a kullanarak bir daireyi newDrawable()renklendirirseniz, tüm dairelerin rengini değiştirirsiniz.

Bu çekilebilirliği, diğer örnekleri etkilemeden güncellenebilir yapmak istiyorsanız, o zaman mevcut sabit durumu değiştirmelisiniz.

// To make a drawable use a separate constant state
drawable.mutate()

İyi bir açıklama için bkz:

http://www.curious-creature.org/2009/05/02/drawable-mutations/

http://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate ()


Aslında mutate () aynı örneği döndürür, ancak dahili durumu değiştirilir, bu nedenle bir renk filtresi uygulamak diğer örnekleri etkilemez. Cevabınızı gözden geçirip düzeltebilir misiniz?
clemp6r

1
@ clemp6r, renk değişikliğinin tüm örneklerini mutate kullanmazsanız - yalnızca klonun rengini değiştirmek için
mutate'i aramanız gerekir

2
Kontrol API ref ve - ( "İade Bu çekilebilir bu çekilebilir kesilebilir yapmak") kaynak kodunu ( "bu dönüş"). Mutate () çağrısı gereklidir, ancak döndürülen örnek aynıdır. Bu bir klon oluşturmaz, bu yalnızca aynı çekilebilir örneğin diğer örneklerini etkilemeden değiştirilmesine izin vermek için çekilebilir örneğin iç durumunu değiştirir.
clemp6r

Peki soruyu bilmiyorum ama bu cevap tam da ihtiyacım olan şeyi yapıyor ... tU
Evren Öztürk

1
Bunlar referans için verdiğiniz en iyi Bağlantılar
Ashok Varma

16

İşte benim işime yarayan bu.

Drawable clone = drawable.getConstantState().newDrawable().mutate();

EVET NEDEN bilmiyorum ama sadece bu newDrawable () ve mutate () kombinasyonu benim için çalışıyor, başka herhangi bir tek mutat () veya tek bir newDrawable () benim için düzgün çalışmıyor
Michał Ziobro

12

Bu SO sorusuna dayanan çözümüm bu .

Buradaki fikir, ImageViewkullanıcı dokunduğunda renk filtresi alması ve kullanıcı dokunmayı bıraktığında renk filtresinin kaldırılmasıdır. Hafızada yalnızca 1 adet çekilebilir / bitmap olduğundan boşa harcamaya gerek yoktur. Olması gerektiği gibi çalışıyor.

class PressedEffectStateListDrawable extends StateListDrawable {

    private int selectionColor;

    public PressedEffectStateListDrawable(Drawable drawable, int selectionColor) {
        super();
        this.selectionColor = selectionColor;
        addState(new int[] { android.R.attr.state_pressed }, drawable);
        addState(new int[] {}, drawable);
    }

    @Override
    protected boolean onStateChange(int[] states) {
        boolean isStatePressedInArray = false;
        for (int state : states) {
            if (state == android.R.attr.state_pressed) {
                isStatePressedInArray = true;
            }
        }
        if (isStatePressedInArray) {
            super.setColorFilter(selectionColor, PorterDuff.Mode.MULTIPLY);
        } else {
            super.clearColorFilter();
        }
        return super.onStateChange(states);
    }

    @Override
    public boolean isStateful() {
        return true;
    }
}

kullanım:

Drawable drawable = new FastBitmapDrawable(bm);
imageView.setImageDrawable(new PressedEffectStateListDrawable(drawable, 0xFF33b5e5));

Benim için de çalışıyor! Bu ilginç bir çözüm, teşekkürler!) PS android berbat, çok kötü düzgün çalışmıyor API :(
Anton Kizema

Bunun (StateListDrawable + BitmapDrawable) içindeki hataları çözmek için en iyi çözüm olduğunu düşünüyorum!
Xavier. S

1

Burada ilgili bir soruyu cevapladım

Temel olarak StateListDrawables gerçekten filtrelerini kaybetmiş gibi görünüyor. Başlangıçta kullanmak istediğim Bitmap'in değiştirilmiş bir kopyasından yeni bir BitmapDrawale oluşturdum.


0
Drawable clone = drawable.mutate().getConstantState().newDrawable().mutate();

getConstantState()iade durumunda null.


0

Kullanarak klon çekilebilir alın newDrawable()ancak değiştirilebilir olduğundan emin olun, aksi takdirde klon efektiniz gitti, bu birkaç satır kodu kullandım ve beklendiği gibi çalışıyor. getConstantState()ek açıklamada önerildiği gibi boş olabilir, bu nedenle bu RunTimeException öğesini çizilebilir klonlarken işleyin.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.