Android: Sao chép một drawable để tạo StateListDrawable với các bộ lọc


91

Tôi đang cố gắng để thực hiện một chức năng khuôn khổ chung mà làm cho bất kỳ drawable trở nên nổi bật khi ép / tập trung / chọn / etc .

Hàm của tôi nhận Drawable và trả về StateListDrawable, trong đó trạng thái mặc định là Drawable chính nó và trạng thái cho android.R.attr.state_pressedcũng có thể vẽ được, chỉ với một bộ lọc được áp dụng setColorFilter.

Vấn đề của tôi là tôi không thể sao chép tệp có thể vẽ và tạo một phiên bản riêng của nó với bộ lọc được áp dụng. Đây là những gì tôi đang cố gắng đạt được:

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

Nếu tôi không sao chép thì rõ ràng bộ lọc được áp dụng cho cả hai trạng thái. Tôi đã thử chơi với mutate()nhưng nó không giúp ích gì ..

Bất kỳ ý tưởng?

Cập nhật:

Câu trả lời được chấp nhận thực sự sao chép một điều có thể rút ra được. Tuy nhiên, nó không giúp được tôi vì chức năng chung của tôi bị lỗi do một vấn đề khác. Có vẻ như khi bạn thêm một drawable vào StateList, nó sẽ mất tất cả các bộ lọc của nó.


Xin chào, bạn đã tìm thấy giải pháp để làm mất bộ lọc có thể kéo được chưa? Tôi đã gặp phải vấn đề tương tự :( Tôi đã kết thúc việc tạo hình ảnh khác từ hình ảnh nguồn bằng cách sao chép Bitmap và áp dụng bộ lọc từng pixel. Vâng, điều này không hiệu quả, nhưng tôi chỉ xử lý một loạt hình ảnh nhỏ một lần.
port443

Tôi không thể giải quyết nó với StateListDrawable, nhưng nếu bạn không sử dụng StateListDrawable và vẫn mất bộ lọc, hãy đảm bảo rằng các bitmap của bạn có thể thay đổi được. Có những câu hỏi tốt liên quan: stackoverflow.com/questions/5499637/... , tôi cũng đã phát hiện ra rằng LightingColorFilter làm việc ở những nơi PorterDuff thất bại .. lovin này android :)
talkol

một câu trả lời tuyệt vời trên liên kết này stackoverflow.com/questions/10889415/…
Alan

Có một tác dụng phụ tương tự gây ra ImageView.setImageDrawable, mà tôi đã có thể giải quyết nhờ câu trả lời được chấp nhận.
Giulio Piancastelli

Tôi đang cố gắng làm điều tương tự và nó hoạt động như mong đợi bằng cách nào đó, ColorFilter không bị mất ... Sự khác biệt là tôi đã biến đổi cái có thể vẽ được.
Henry

Câu trả lời:


162

Hãy thử những cách sau:

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

1
Cảm ơn! Phương pháp này dường như sao chép thành công một tệp có thể vẽ được. Chức năng tôi đã cố gắng để viết dù không làm việc .. Dường như khi một drawable được đưa vào một StateList nó sẽ mất bộ lọc của nó :(
talkol

3
+1 vì đã giúp tôi sửa một lỗi rất lạ trong MapView trong đó việc sử dụng lại một Drawable từ ItemizedOverlay trong AlertDialog đã làm cho ItemizedOverlay di chuyển khi được kích hoạt. Tạo một phiên bản mới của Drawable đã khắc phục được sự cố.
kskjon

9
Làm để hoạt động chính xác, nếu chúng tôi thử sử dụng phương pháp setAlpha. Trong trường hợp này, cả bitmap thay đổi có thể vẽ được. Sau đó, tôi nhận được đầu tiên có thể vẽ là: getResources (). GetDrawable (), thứ hai là: getResources (). GetDrawable (). Mutate ().
Yura Shinkarev

Cảm ơn rất nhiều, nó đã khắc phục sự cố mà tôi gặp phải khi áp dụng một hàm giới hạn, từ API Mapsforge. Bây giờ tôi có thể sử dụng thành công các ngăn kéo ở khắp mọi nơi!
xarlymg89

18
@Flavio - Tôi đã thử điều này với bộ lọc màu, nhưng nó tô màu cho tất cả các trường hợp có thể vẽ của tôi! Hóa ra có vẻ như bạn phải sử dụng .mutate()(xem câu trả lời của tôi).
Peter Ajtai,

106

Nếu bạn áp dụng bộ lọc / etc cho một drawable được tạo bằng getConstantState().newDrawable()thì tất cả các trường hợp của drawable đó cũng sẽ bị thay đổi, vì drawable sử dụng constantStatelàm bộ nhớ đệm!

Vì vậy, nếu bạn tô màu một vòng tròn bằng cách sử dụng bộ lọc màu và a newDrawable(), bạn sẽ thay đổi màu của tất cả các vòng tròn.

Nếu bạn muốn làm cho bản vẽ này có thể cập nhật được mà không ảnh hưởng đến các trường hợp khác thì bạn phải thay đổi trạng thái hằng số hiện có đó.

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

Để có lời giải thích tốt, hãy xem:

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

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


Trên thực tế, mutate () trả về cùng một phiên bản chính xác, nhưng trạng thái bên trong của nó bị thay đổi, do đó việc áp dụng bộ lọc màu sẽ không ảnh hưởng đến các phiên bản khác. Bạn có thể xem lại và sửa câu trả lời của mình không?
clemp6r

1
@ clemp6r nếu bạn không sử dụng đột biến tất cả các trường hợp của sự thay đổi màu sắc - bạn cần phải gọi đột biến để chỉ thay đổi màu sắc của clone
Peter Ajtai

2
Kiểm tra tham chiếu API ("Làm cho tệp này có thể thay đổi được. - Trả về tệp có thể kéo này") và mã nguồn ("trả lại tệp này"). Việc gọi hàm mutate () là bắt buộc, nhưng phiên bản trả về vẫn như cũ. Thao tác này không tạo ra một bản sao, điều này chỉ thay đổi trạng thái bên trong của đối tượng có thể vẽ để cho phép sửa đổi nó mà không ảnh hưởng đến các đối tượng khác của cùng một đối tượng có thể vẽ được.
clemp6r

Vâng, tôi không biết gì về những câu hỏi, nhưng câu trả lời này không chính xác những điều tôi cần ... TU
Evren Ozturk

1
Đó là những liên kết tốt nhất, những liên kết mà bạn đã đưa ra để tham khảo
Ashok Varma

15

Đây là những gì làm việc cho tôi.

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

YES I don không biết tại sao nhưng chỉ sự kết hợp này newDrawable () và đột biến () làm việc cho tôi bất cứ đột biến nào khác () hoặc đơn newDrawable () không làm việc một cách chính xác đối với tôi
Michał Ziobro

12

Đây là giải pháp của tôi, dựa trên câu hỏi SO này .

Ý tưởng là ImageViewnhận được bộ lọc màu khi người dùng chạm vào nó và bộ lọc màu sẽ bị xóa khi người dùng ngừng chạm vào nó. Trong bộ nhớ chỉ có 1 drawable / bitmap, vì vậy không cần phải lãng phí nó. Nó hoạt động như nó cần.

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

sử dụng:

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

Hoạt động cho tôi cũng! Đó là một giải pháp thú vị, cảm ơn!) PS android tệ quá, API hoạt động không bình thường rất tệ :(
Anton Kizema

Tôi nghĩ rằng đây là giải pháp tốt nhất cho đến nay để giải quyết các lỗi trong (StateListDrawable + BitmapDrawable)!
Xavier.S

1

Tôi đã trả lời một câu hỏi liên quan ở đây

Về cơ bản, có vẻ như StateListDrawables thực sự mất bộ lọc của chúng. Tôi đã tạo một BitmapDrawale mới từ một bản sao đã thay đổi của Bitmap mà tôi muốn sử dụng ban đầu.


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

trong trường hợp getConstantState()trả lại null.


0

Nhận bản sao có thể vẽ bằng cách sử dụng newDrawable()nhưng hãy đảm bảo rằng nó có thể thay đổi nếu không hiệu ứng sao chép của bạn biến mất, tôi đã sử dụng vài dòng mã này và nó đang hoạt động như mong đợi. getConstantState()có thể rỗng như được gợi ý bởi chú thích, vì vậy hãy xử lý RunTimeException này trong khi bạn sao chép có thể vẽ được.

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.