Android:フィルターを使用してStateListDrawableを作成するために、Drawableを複製する


91

私は、任意のDrawableが押された/フォーカスされた/選択されたなどのときに強調表示されるようにする一般的なフレームワーク関数を作成しようとしています。

私の関数は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()が、役に立ちません。

何か案は?

更新:

受け入れられた答えは確かにドローアブルを複製します。私の一般的な機能が別の問題で失敗するので、それは私を助けませんでした。ドローアブルをStateListに追加すると、すべてのフィルターが失われるようです。


こんにちは、ドローアブルを失うフィルターの解決策を見つけましたか?私は同じ問題に遭遇しました:(私はビットマップをクローニングし、ピクセルごとのフィルタを適用することにより、元の画像から他の画像を生成することになったはい、これは非効率的であるが、私は一度に処理小さな画像のちょうど束を持っている。。
port443を

StateListDrawableでは解決できませんでしたが、StateListDrawableを使用していないにもかかわらずフィルターが失われる場合は、ビットマップが変更可能であることを確認してください。関連する良い質問があります:stackoverflow.com/questions/5499637/…、また、PorterDuffが失敗する場所でLightingColorFilterが機能することを発見しました.. lovin this android :)
talkol

このリンク上の偉大な答えstackoverflow.com/questions/10889415/...
アラン・

によって引き起こされる同様の副作用がありますがImageView.setImageDrawable、受け入れられた回答のおかげで回避できました。
Giulio Piancastelli

私は同じことをやろうとしていますが、それは何とか期待どおりに機能します。ColorFilterは失われませんでした...違いは、ドローアブルを変更したことです。
ヘンリー

回答:


162

以下を試してください:

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

1
ありがとう!このメソッドは、ドローアブルを正常に複製するようです。私が書こうとした関数は機能しません。..ドローアブルがStateListに挿入されると、フィルターを失うようです:(
talkol

3
AlertViewでItemizedOverlayからDrawableを再利用すると、トリガーされたときにItemizedOverlayが移動するというMapViewの非常に奇妙なエラーを修正するのに役立つ+1。Drawableの新しいインスタンスを作成すると、問題が修正されました。
kskjon

9
setAlphaメソッドを使用する場合は、正しく機能するようにしてください。この場合、両方の描画可能な変更ビットマップ。次に、最初のドローアブルをgetResources()。getDrawable()として取得し、2番目のドローアブルをgetResources()。getDrawable()。mutate()として取得します。
Yura

おかげで、API Mapsforgeから境界関数を適用したときに発生していた問題が修正されました。これで、どこでもドローアブルを正常に使用できます!
xarlymg89

18
@Flavio-カラーフィルターを使用してこれを試しましたが、ドローアブルのすべてのインスタンスに色が付きました あなたが使用しなければならないように見えます.mutate()(私の答えを参照してください)。
Peter Ajtai 2013

106

で作成されたドローアブルにフィルターなどを適用すると、ドローアブル はキャッシュとしてgetConstantState().newDrawable()使用されるため、そのドローアブルのすべてのインスタンスも変更されますconstantState

したがって、カラーフィルターとを使用して円に色を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 2014年

1
@ clemp6r-色の変更のすべてのインスタンスを使用しない場合-クローンの色のみを変更するには、mutateを呼び出す必要があります
Peter Ajtai

2
チェックAPIの参照 - (「この描画可能を返します。この描画可能可変にします」)と、ソースコードを(「本を返します」)。mutate()の呼び出しは必須ですが、返されるインスタンスは同じです。これはクローンを作成しません。これはドローアブルインスタンスの内部状態を変更するだけで、同じドローアブルの他のインスタンスに影響を与えることなくそれを変更できます。
clemp6r 2014年

さて、質問についてはわかりませんが、この答えは私が必要とする正確なことを行います... tU
Evren Ozturk

1
これらはあなたが参照のためにあなたが与えたものである最高のリンクです
Ashok Varma

15

これが私にとってうまくいきます。

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

YES、私は私の作品しかし、なぜこれだけの組み合わせnewDrawable()とのmutate()任意の他の単一の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 2016

1

ここで関連する質問に答えました

基本的には、StateListDrawablesが実際にフィルターを失うようです。最初に使用したかったビットマップの変更されたコピーから新しいBitmapDrawaleを作成しました。



0

を使用して描画可能なクローンを取得しnewDrawable()ますが、変更可能であることを確認してください。getConstantState()アノテーションで提案されているようにnullになる可能性があるため、ドローアブルのクローンを作成するときにこの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.