简体   繁体   English

Android:如何以编程方式创建 StateListDrawable

[英]Android: How to create a StateListDrawable programmatically

I have a GridView to display some objects, and visually each of the objects will have an image icon and a text label.我有一个 GridView 来显示一些对象,并且在视觉上每个对象都会有一个图像图标和一个文本 label。 I also want the image icon to have some "push and pop" effect when clicked, that is, when pressed, the image will move a small distance to the bottom right direction, and when released get back to its original position.我还希望图像图标在单击时具有一些“推送和弹出”效果,即按下时,图像将向右下方向移动一小段距离,释放时返回其原始 position。

The objects (and their image icons) are from some dynamic sources.对象(及其图像图标)来自一些动态来源。 My intuition is to create a StateListDrawable for each item, which will have two states: pressed or not.我的直觉是为每个项目创建一个 StateListDrawable,它将有两种状态:按下或不按下。 For GridView item view, I would use a Button, which can accomodate a Drawable and a label, that perfectly satisfies my requirment.对于 GridView 项目视图,我会使用一个按钮,它可以容纳一个 Drawable 和一个 label,完全满足我的要求。

I defined an item class to wrap up the original object:我定义了一个项目 class 来包装原始 object:

public class GridItem<T> {

    public static final int ICON_OFFSET = 4;

    private StateListDrawable mIcon;
    private String mLabel;
    private T mObject;

    public Drawable getIcon() {
        return mIcon;
    }

    public void setIcon(Drawable d) {
        if (null == d) {
            mIcon = null;
        }else if(d instanceof StateListDrawable) {
            mIcon = (StateListDrawable) d;
        } else {
            InsetDrawable d1 = new InsetDrawable(d, 0, 0, ICON_OFFSET, ICON_OFFSET);
            InsetDrawable d2 = new InsetDrawable(d, ICON_OFFSET, ICON_OFFSET, 0, 0);
            mIcon = new StateListDrawable();
            mIcon.addState(new int[] { android.R.attr.state_pressed }, d2);
            mIcon.addState(StateSet.WILD_CARD, d1);
            //This won't help either: mIcon.addState(new int[]{}, d1);
        }
    }

    public String getLabel() {
        return mLabel;
    }

    public void setLabel(String l) {
        mLabel = l;
    }

    public T getObject() {
        return mObject;
    }

    public void setObject(T o) {
        mObject = o;
    }

}

Now the problem is, when I touch a grid item, the icon "moves" quite as I have expected, but it won't restore its original position when my finger lifts up leaving the item.现在的问题是,当我触摸一个网格项目时,图标会像我预期的那样“移动”,但是当我的手指抬起离开该项目时,它不会恢复其原始 position。

My question is: how to programmatically create a StateListDrawable equivalent to one inflated from an XML resource like我的问题是:如何以编程方式创建一个 StateListDrawable 等效于从 XML 资源膨胀的一个

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
          android:drawable="@drawable/image_pressed" />  
    <item android:drawable="@drawable/image_normal" />
</selector>

? ?

if your drawables are just bitmaps, you could draw them programmatically, for now it should help, however I wonder what is the problem with InsetDrawable usage here, basically use prepared BitmapDrawables that are drawn programatically, you would need to modify your method to accept bitmaps b如果您的drawables只是位图,您可以以编程方式绘制它们,现在它应该有所帮助,但是我想知道这里使用InsetDrawable有什么问题,基本上使用以编程方式绘制的准备好的BitmapDrawables ,您需要修改您的方法以接受位图b

        Bitmap bc1 = Bitmap.createBitmap(b.getWidth() + ICON_OFFSET, b.getHeight() + ICON_OFFSET, Bitmap.Config.ARGB_8888);
        Canvas c1 = new Canvas(bc1);
        c1.drawBitmap(b, 0, 0, null);
        Bitmap bc2 = Bitmap.createBitmap(b.getWidth() + ICON_OFFSET, b.getHeight() + ICON_OFFSET, Bitmap.Config.ARGB_8888);
        Canvas c2 = new Canvas(bc2);
        c2.drawBitmap(b, ICON_OFFSET, ICON_OFFSET, null);

        mIcon = new StateListDrawable();
        mIcon.addState(new int[] { android.R.attr.state_pressed },  new BitmapDrawable(bc2));
        mIcon.addState(StateSet.WILD_CARD, new BitmapDrawable(bc1));

I can see the answer is already accepted.我可以看到答案已被接受。 I am sharing if you want to assign dynamically colors of buttons from the users for normal as well as pressed state.如果您想从用户那里动态分配 colors 按钮,我将分享正常以及按下 state。 then you can just call this function:那么你可以直接调用这个 function:

public static StateListDrawable convertColorIntoBitmap(String pressedColor, String normalColor){


        /*Creating bitmap for color which will be used at pressed state*/
        Rect rectPressed = new Rect(0, 0, 1, 1);

        Bitmap imagePressed = Bitmap.createBitmap(rectPressed.width(), rectPressed.height(), Config.ARGB_8888);
        Canvas canvas = new Canvas(imagePressed);       
        int colorPressed = Color.parseColor(pressedColor);
        Paint paintPressed = new Paint();
        paintPressed.setColor(colorPressed);
        canvas.drawRect(rectPressed, paintPressed);
        RectF bounds = new RectF();
        bounds.round(rectPressed);

        /*Creating bitmap for color which will be used at normal state*/
        Rect rectNormal = new Rect(0, 0, 1, 1);     
        Bitmap imageNormal = Bitmap.createBitmap(rectNormal.width(), rectNormal.height(), Config.ARGB_8888);
        Canvas canvasNormal = new Canvas(imageNormal);
        int colorNormal = Color.parseColor(normalColor);
        Paint paintNormal = new Paint();
        paintNormal.setColor(colorNormal);
        canvasNormal.drawRect(rectNormal, paintNormal);


        /*Now assigning states to StateListDrawable*/
        StateListDrawable stateListDrawable= new StateListDrawable();
        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new BitmapDrawable(imagePressed));
        stateListDrawable.addState(StateSet.WILD_CARD, new BitmapDrawable(imageNormal));

        return stateListDrawable;

    }

Now all you need is to set it as your textview or button background like below:现在您只需将其设置为 textview 或按钮背景,如下所示:

 if(android.os.Build.VERSION.SDK_INT>=16){
        yourbutton.setBackground(convertColorIntoBitmap("#CEF6CE00","#4C9D32"));        

            }else{

yourbutton.setBackgroundDrawable(convertColorIntoBitmap("#CEF6CE00","#4C9D32"));
    }

Here you can see all you need to pass the colors dynamically and we're done.在这里,您可以看到动态传递 colors 所需的一切,我们就完成了。 hope this will help someone:) You can find it's gist too here :)希望这会对某人有所帮助:)你也可以在这里找到它的要点:)

I have seen the previous answers, but came up with a much shorter and better solution using ColorDrawable .我已经看到了以前的答案,但使用ColorDrawable提出了一个更短更好的解决方案。

/**
     * Get {@link StateListDrawable} given the {@code normalColor} and {@code pressedColor}
     * for dynamic button coloring
     *
     * @param normalColor  The color in normal state.
     * @param pressedColor The color in pressed state.
     * @return
     */
    public static StateListDrawable getStateListDrawable(int normalColor, int pressedColor) {
        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(pressedColor));
        stateListDrawable.addState(StateSet.WILD_CARD, new ColorDrawable(normalColor));
        return stateListDrawable;
    }

This accepts resolved colors as integer and uses ColorDrawable to add them in a StateListDrawable .这接受解析的 colors 作为 integer 并使用ColorDrawable将它们添加到StateListDrawable中。

Once you have the drawable, you can use it simply like this,一旦你有了drawable,你可以像这样简单地使用它,

if (android.os.Build.VERSION.SDK_INT >= 16) {
            mButton.setBackground(Utils.getStateListDrawable(ResourceUtils.getColor(R.color.white),
                    ResourceUtils.getColor(R.color.pomegranate)));
        } else {
            mButton.setBackgroundDrawable(Utils.getStateListDrawable(ResourceUtils.getColor(R.color.white),
                    ResourceUtils.getColor(R.color.pomegranate)));
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM