Android: การโคลนสิ่งที่วาดได้เพื่อสร้าง StateListDrawable ด้วยตัวกรอง


91

ฉันพยายามที่จะทำให้ฟังก์ชั่นกรอบทั่วไปที่ทำให้ Drawable ใด ๆ กลายเป็นไฮไลต์เมื่อกด / มุ่งเน้น / เลือก / etc

ฟังก์ชั่นของฉันใช้เวลา Drawable และผลตอบแทน StateListDrawable ที่รัฐเริ่มต้นคือ Drawable ตัวเองและของรัฐเพื่อandroid.R.attr.state_pressedเป็น drawable setColorFilterเดียวกันเพียงกับตัวกรองที่นำมาใช้โดยใช้

ปัญหาของฉันคือฉันไม่สามารถโคลนสิ่งที่วาดได้และสร้างอินสแตนซ์แยกต่างหากโดยใช้ตัวกรอง นี่คือสิ่งที่ฉันพยายามจะบรรลุ:

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

ถ้าฉันไม่โคลนตัวกรองจะถูกนำไปใช้กับทั้งสองสถานะอย่างชัดเจน ฉันลองเล่นด้วยmutate()แต่มันไม่ช่วย ..

ความคิดใด ๆ ?

อัปเดต:

คำตอบที่ได้รับการยอมรับคือการลอกแบบที่เบิกได้ มันไม่ได้ช่วยฉันเลยเพราะฟังก์ชันทั่วไปของฉันล้มเหลวในปัญหาอื่น ดูเหมือนว่าเมื่อคุณเพิ่ม drawable ลงใน StateList มันจะสูญเสียตัวกรองทั้งหมด


สวัสดีคุณพบวิธีแก้ปัญหาสำหรับ drawables ที่สูญเสียตัวกรองหรือไม่? ฉันพบปัญหาเดียวกัน :( ฉันลงเอยด้วยการสร้างภาพอื่นจากอิมเมจต้นฉบับโดยการโคลนบิตแมปและใช้ฟิลเตอร์แบบพิกเซลต่อพิกเซลใช่มันไม่มีประสิทธิภาพ แต่ฉันมีการประมวลผลภาพขนาดเล็กเพียงครั้งเดียว
port443

ฉันไม่สามารถแก้ไขได้ด้วย StateListDrawable แต่ถ้าคุณไม่ได้ใช้ StateListDrawable และยังคงสูญเสียตัวกรองของคุณตรวจสอบให้แน่ใจว่าบิตแมปของคุณไม่แน่นอน มีคำถามที่เกี่ยวข้องดี: stackoverflow.com/questions/5499637/…และฉันค้นพบว่า LightingColorFilter ทำงานในสถานที่ที่ PorterDuff ล้มเหลว .. lovin android นี้ :)
talkol

คำตอบที่ดีในลิงค์นี้stackoverflow.com/questions/10889415/…
Alan

มีผลข้างเคียงที่คล้ายกันที่เกิดขึ้นImageView.setImageDrawableซึ่งฉันสามารถแก้ไขได้ด้วยคำตอบที่ยอมรับ
Giulio Piancastelli

ฉันพยายามทำสิ่งเดียวกันและได้ผลตามที่คาดไว้ แต่ ColorFilter ก็ไม่หายไป ... ความแตกต่างคือฉันได้กลายพันธุ์สิ่งที่วาดได้
Henry

คำตอบ:


163

ลองทำดังต่อไปนี้:

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

1
ขอบคุณ! วิธีนี้ดูเหมือนว่าจะโคลนสิ่งที่วาดได้สำเร็จ ฟังก์ชั่นที่ฉันพยายามเขียนแม้ว่าจะไม่ทำงาน .. ดูเหมือนว่าเมื่อใส่ drawable ลงใน StateList มันจะสูญเสียตัวกรอง :(
talkol

3
+1 เพื่อช่วยฉันแก้ไขข้อผิดพลาดที่แปลกมากใน MapView ซึ่งการใช้ Drawable ซ้ำจาก ItemizedOverlay ใน AlertDialog ทำให้การย้าย ItemizedOverlay เมื่อถูกทริกเกอร์ การสร้างอินสแตนซ์ใหม่ของ Drawable ช่วยแก้ปัญหาได้
kskjon

9
ทำเพื่อให้ทำงานได้อย่างถูกต้องถ้าเราลองใช้เมธอด setAlpha ในกรณีนี้ทั้งบิตแมปการเปลี่ยนแปลงที่วาดได้ จากนั้นฉันจะได้รับการวาดครั้งแรกเป็น: getResources (). getDrawable (), วินาทีเป็น: getResources (). getDrawable (). mutate ()
Yura Shinkarev

ขอบคุณมากมันแก้ไขปัญหาที่ฉันพบเมื่อฉันใช้ฟังก์ชันการกำหนดขอบเขตจาก API Mapsforge ตอนนี้ฉันสามารถใช้ drawables ได้ทุกที่!
xarlymg89

18
@Flavio - ฉันลองใช้ฟิลเตอร์สีแล้ว แต่มันทำให้อินสแตนซ์ของฉันวาดได้ทั้งหมด! ดูเหมือนว่าคุณต้องใช้.mutate()(ดูคำตอบของฉัน)
Peter Ajtai

106

หากคุณใช้ตัวกรอง / etc กับ drawable ที่สร้างขึ้นด้วย getConstantState().newDrawable()อินสแตนซ์ทั้งหมดของ drawable นั้นก็จะเปลี่ยนไปเช่นกันเนื่องจาก drawables ใช้constantStateเป็น cache!

ดังนั้นหากคุณระบายสีวงกลมโดยใช้ฟิลเตอร์สีและ a newDrawable()คุณจะเปลี่ยนสีของวงกลมทั้งหมด

หากคุณต้องการทำให้การอัปเดตที่วาดได้นี้โดยไม่ส่งผลกระทบต่ออินสแตนซ์อื่น ๆ คุณต้องกลายพันธุ์สถานะคงที่ที่มีอยู่นั้น

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

สำหรับคำอธิบายที่ดีโปรดดู:

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

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


ในความเป็นจริง mutate () จะส่งคืนอินสแตนซ์เดียวกันทุกประการ แต่สถานะภายในมีการเปลี่ยนแปลงดังนั้นการใช้ฟิลเตอร์สีจะไม่ส่งผลกระทบต่ออินสแตนซ์อื่น ๆ คุณสามารถตรวจสอบและแก้ไขคำตอบของคุณได้หรือไม่?
clemp6r

1
@ clemp6r ถ้าคุณไม่ใช้ mutate ทุกอินสแตนซ์ของการเปลี่ยนสี - คุณต้องเรียก mutate เพื่อเปลี่ยนสีของโคลนเท่านั้น
Peter Ajtai

2
ตรวจสอบการอ้างอิง API ("Make this drawable mutable. - Returns this drawable") และซอร์สโค้ด ("return this") จำเป็นต้องเรียก mutate () แต่อินสแตนซ์ที่ส่งคืนจะเหมือนกัน สิ่งนี้ไม่ได้สร้างโคลน แต่จะเปลี่ยนสถานะภายในของอินสแตนซ์ที่ดึงได้เท่านั้นเพื่อให้สามารถแก้ไขได้โดยไม่ส่งผลกระทบต่ออินสแตนซ์อื่น ๆ
clemp6r

ฉันไม่รู้เกี่ยวกับคำถาม แต่คำตอบนี้ตรงกับสิ่งที่ฉันต้องการ ... tU
Evren Ozturk

1
นี่คือลิงค์ที่ดีที่สุดซึ่งคุณให้ไว้สำหรับการอ้างอิง
Ashok Varma

16

นี่คือสิ่งที่เหมาะกับฉัน

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

ใช่ฉันไม่รู้ว่าทำไม แต่มีเพียงชุดค่าผสม newDrawable () และ mutate () เท่านั้นที่ใช้ได้กับฉันการกลายพันธุ์เดี่ยวอื่น ๆ () หรือ newDrawable () เดียวทำงานไม่ถูกต้องสำหรับฉัน
Michał Ziobro

12

นี่คือวิธีแก้ปัญหาของฉันตามคำถาม SOนี้

แนวคิดคือImageViewได้รับฟิลเตอร์สีเมื่อผู้ใช้สัมผัสและฟิลเตอร์สีจะถูกลบออกเมื่อผู้ใช้หยุดสัมผัส มีเพียง 1 บิตแมปที่วาดได้ / บิตแมปเท่านั้นที่อยู่ในหน่วยความจำดังนั้นไม่จำเป็นต้องเสียมันไป มันทำงานได้ตามที่ควร

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

การใช้งาน:

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

ใช้ได้ผลกับฉันด้วย! นั่นเป็นทางออกที่น่าสนใจขอบคุณ!) PS android แย่มาก API ทำงานไม่ถูกต้อง :(
Anton Kizema

ฉันคิดว่านี่เป็นทางออกที่ดีที่สุดในการแก้บั๊กใน (StateListDrawable + BitmapDrawable)!
Xavier.S

1

ฉันตอบคำถามที่เกี่ยวข้องที่นี่

โดยทั่วไปดูเหมือนว่า StateListDrawables จะสูญเสียตัวกรอง ฉันสร้าง BitmapDrawale ใหม่จากสำเนา Bitmap ที่ฉันต้องการใช้ในตอนแรก



0

รับโคลนที่วาดได้โดยใช้newDrawable()แต่ตรวจสอบให้แน่ใจว่ามันไม่แน่นอนมิฉะนั้นเอฟเฟกต์การโคลนของคุณจะหายไปฉันใช้โค้ดสองสามบรรทัดเหล่านี้และใช้งานได้ตามที่คาดไว้ getConstantState()อาจเป็นค่าว่างตามที่แนะนำโดยคำอธิบายประกอบดังนั้นให้จัดการ RunTimeException นี้ในขณะที่คุณโคลนที่วาดได้

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.