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.

talkol
sumber
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();
Flavio
sumber
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 ()

Peter Ajtai
sumber
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();
Yanru Bi
sumber
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));
Malachiasz
sumber
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.

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

dalam kasus getConstantState()pengembalian null.

Martin Wang
sumber
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();
}
Kishan Donga
sumber