【问题标题】:Android: How to create a StateListDrawable programmaticallyAndroid:如何以编程方式创建 StateListDrawable
【发布时间】:2011-09-24 00:12:39
【问题描述】:

我有一个 GridView 来显示一些对象,并且在视觉上每个对象都有一个图像图标和一个文本标签。我还希望图像图标在单击时具有一些“推送和弹出”效果,即按下时,图像会向右下方向移动一小段距离,释放时会回到原来的位置。

对象(及其图像图标)来自一些动态来源。我的直觉是为每个项目创建一个 StateListDrawable,它将有两种状态:按下或不按下。对于 GridView 项目视图,我会使用一个 Button,它可以容纳一个 Drawable 和一个标签,完全满足我的要求。

我定义了一个项目类来包装原始对象:

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

}

现在的问题是,当我触摸一个网格项目时,图标会像我预期的那样“移动”,但是当我的手指抬起离开该项目时,它不会恢复其原始位置。

我的问题是:如何以编程方式创建一个 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>

?

【问题讨论】:

  • 不幸的是,问题似乎与InsetDrawable 的使用有关,您是否尝试过使用BitmapDrawable 代替?声明为 xml 的插图是否可以正常工作?我知道这不是一个解决方案,但也许我们可以诊断出问题......
  • 是的@wjeshak,你是对的。我修改了GridItem.setIcon() 方法,创建了两个BitmapDrawables,然后是InsetDrawables 然后我可以看到状态正常变化

标签: android statelist


【解决方案1】:

我已经看到了之前的答案,但使用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;
    }

这接受解析颜色为整数并使用ColorDrawable 将它们添加到StateListDrawable

一旦你有了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)));
        }

【讨论】:

    【解决方案2】:

    我可以看到答案已被接受。如果您想为正常状态和按下状态的用户动态分配按钮颜色,我正在分享。那么你可以调用这个函数:

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

    现在您只需将其设置为您的文本视图或按钮背景,如下所示:

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

    在这里您可以看到动态传递颜色所需的一切,我们就完成了。希望这会对某人有所帮助:)你也可以找到它的要点here :)

    【讨论】:

    • 那是很多代码。您可以使用 ColorDrawable() 代替 BitmapDrawable 并避免该代码的前三节。
    【解决方案3】:

    如果你的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));
    

    【讨论】:

    • 嗨@wjeshak,我更喜欢Drawable 作为输入/输出接口。我使用Drawable.setBounds()Drawable.draw(Canvas) 方法在位图边缘添加“填充”,问题就解决了。您为我指明了正确的方向:)谢谢!
    猜你喜欢
    • 2011-07-02
    • 1970-01-01
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-28
    • 2015-04-19
    • 2011-11-11
    相关资源
    最近更新 更多