Android: Menggandakan drawable untuk membuat StateListDrawable dengan filter


91

Saya mencoba membuat fungsi kerangka umum yang membuat semua Drawable menjadi disorot saat ditekan / difokuskan / dipilih / dll .

Fungsi saya mengambil Drawable dan mengembalikan StateListDrawable, di mana status defaultnya adalah Drawable itu sendiri, dan status untuk android.R.attr.state_presseddrawable yang sama, hanya dengan filter yang diterapkan menggunakan setColorFilter.

Masalah saya adalah saya tidak dapat mengkloning drawable dan membuat instance terpisah dengan filter yang diterapkan. Inilah yang saya coba capai:

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;
}

Jika saya tidak mengkloning, maka filter diterapkan secara jelas ke kedua status. Saya mencoba bermain dengan mutate()tetapi tidak membantu ..

Ada ide?

Memperbarui:

Jawaban yang diterima memang mengkloning drawable. Itu tidak membantu saya karena fungsi umum saya gagal pada masalah yang berbeda. Tampaknya saat Anda menambahkan drawable ke StateList, semua filternya akan hilang.


Hai, apakah Anda menemukan solusi untuk menghilangkan filter yang dapat digambar? Saya mengalami masalah yang sama :( Saya akhirnya menghasilkan gambar lain dari gambar sumber dengan mengklon Bitmap dan menerapkan filter piksel demi piksel. Ya, ini tidak efisien, tetapi saya hanya memiliki banyak gambar kecil yang diproses sekali.
port443

Saya tidak bisa menyelesaikannya dengan StateListDrawable, tetapi jika Anda tidak menggunakan StateListDrawable dan masih kehilangan filter, pastikan bitmap Anda bisa berubah. Ada pertanyaan terkait yang bagus: stackoverflow.com/questions/5499637/… , juga saya telah menemukan bahwa LightingColorFilter berfungsi di tempat-tempat di mana PorterDuff gagal .. suka android ini :)
talkol

jawaban yang bagus di tautan ini stackoverflow.com/questions/10889415/…
Alan

Ada efek samping serupa yang dipicu ImageView.setImageDrawable, yang bisa saya atasi berkat jawaban yang diterima.
Giulio Piancastelli

Saya mencoba melakukan hal yang sama dan entah bagaimana berfungsi seperti yang diharapkan, ColorFilter tidak hilang ... Perbedaannya adalah saya memutasi drawable.
Henry

Jawaban:


163

Coba yang berikut ini:

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

1
Terima kasih! Metode ini tampaknya berhasil menggandakan drawable. Fungsi yang saya coba tulis meskipun tidak berfungsi .. Tampaknya ketika drawable dimasukkan ke dalam StateList, ia kehilangan
filternya

3
+1 untuk membantu saya memperbaiki kesalahan yang sangat aneh di MapView di mana menggunakan kembali Drawable dari ItemizedOverlay di AlertDialog membuat perpindahan ItemizedOverlay saat dipicu. Membuat instance baru dari Drawable memperbaiki masalah tersebut.
kskjon

9
Lakukan agar berfungsi dengan benar, jika kita mencoba menggunakan metode setAlpha. Dalam hal ini keduanya mengubah bitmap drawable. Kemudian saya mendapatkan drawable pertama sebagai: getResources (). GetDrawable (), kedua sebagai: getResources (). GetDrawable (). Mutate ().
Yura Shinkarev

Terima kasih banyak, ini memperbaiki masalah yang saya hadapi ketika saya menerapkan fungsi pembatas, dari API Mapsforge. Sekarang saya berhasil menggunakan drawable di mana saja!
xarlymg89

18
@ Flavio - Saya mencoba ini dengan filter warna, tetapi itu mewarnai semua contoh drawable saya! Ternyata sepertinya Anda harus menggunakan .mutate()(lihat jawaban saya).
Peter Ajtai

106

Jika Anda menerapkan filter / etc ke drawable yang dibuat dengan getConstantState().newDrawable()semua instance drawable tersebut akan diubah juga, karena drawable menggunakan constantStatesebagai cache!

Jadi jika Anda mewarnai lingkaran menggunakan filter warna dan a newDrawable(), Anda akan mengubah warna semua lingkaran.

Jika Anda ingin membuat drawable ini dapat diperbarui tanpa memengaruhi instance lain, Anda harus mengubah status konstanta yang ada itu.

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

Untuk penjelasan yang bagus lihat:

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

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


Faktanya mutate () mengembalikan instance yang sama persis, tetapi status internalnya berubah sehingga menerapkan filter warna tidak akan memengaruhi instance lain. Bisakah Anda meninjau dan memperbaiki jawaban Anda?
clemp6r

1
@ clemp6r jika Anda tidak menggunakan mutasi semua contoh perubahan warna - Anda perlu memanggil mutasi untuk hanya mengubah warna klon
Peter Ajtai

2
Periksa referensi API ("Make this drawable mutable. - Returns this drawable") dan kode sumbernya ("return this"). Memanggil mutate () diperlukan, tetapi instance yang dikembalikan sama. Ini tidak membuat penggandaan, ini hanya mengubah status internal instance drawable untuk memungkinkan pengubahan tanpa memengaruhi instance lain dari drawable yang sama.
clemp6r

Saya tidak tahu tentang pertanyaannya, tetapi jawaban ini melakukan hal yang saya butuhkan ... tU
Evren Ozturk

1
Itu adalah Tautan terbaik, yang Anda berikan sebagai referensi
Ashok Varma

16

Inilah yang berhasil untuk saya.

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

YA, saya tidak tahu MENGAPA tetapi hanya kombinasi ini newDrawable () dan mutate () yang berfungsi untuk saya, setiap mutate () atau single newDrawable () lainnya tidak berfungsi dengan benar untuk saya
Michał Ziobro

12

Ini adalah solusi saya, berdasarkan pertanyaan SO ini .

Idenya adalah ImageViewmendapatkan filter warna saat pengguna menyentuhnya, dan filter warna dihapus saat pengguna berhenti menyentuhnya. Hanya 1 drawable / bitmap yang ada di memori, jadi tidak perlu menyia-nyiakannya. Ini bekerja sebagaimana mestinya.

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;
    }
}

pemakaian:

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

Bekerja untuk saya juga! Itu solusi yang menarik, terima kasih!) PS android menyebalkan, API yang tidak berfungsi dengan baik :(
Anton Kizema

Saya pikir ini adalah solusi terbaik sejauh ini untuk menyelesaikan bug di (StateListDrawable + BitmapDrawable)!
Xavier.S

1

Saya menjawab pertanyaan terkait di sini

Pada dasarnya, sepertinya StateListDrawables memang kehilangan filternya. Saya membuat BitmapDrawale baru dari salinan Bitmap yang telah diubah yang awalnya ingin saya gunakan.


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

dalam kasus getConstantState()pengembalian null.


0

Dapatkan clone drawable menggunakan newDrawable()tetapi pastikan itu bisa berubah jika tidak efek klon Anda hilang, saya menggunakan beberapa baris kode ini dan berfungsi seperti yang diharapkan. getConstantState()mungkin null seperti yang disarankan oleh anotasi, jadi tangani RunTimeException ini saat Anda menggandakan drawable.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.