0

I have a layout border_bottom_black_bluegrey_background.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape android:shape="rectangle" >
            <stroke
                android:width="1dp"
                android:color="@color/black" />
        </shape>
    </item>
    <item android:bottom="1dp">
        <shape android:shape="rectangle" >
            <solid android:color="@color/blue_grey" />
        </shape>
    </item>

</layer-list>

I'm trying to reuse this layout so that I don't have to create one for each different border and background color.

I know this can be accomplished with styles and attrs, I had a look on the internet for good tutorial but with no avail.

This is what i've got so far:

attrs.xml

<resources>
    <declare-styleable>
        <attr name="backgroundColor" format="reference"/>
        <attr name="borderColor" format="reference"/>
    </declare-styleable>
</resources>

styles.xml

 <style name="BorderBottom">
    <item name="android:background">@drawable/border_bottom</item>
</style>

<style name="BlackBorderBlueGreyBackground" parent="BorderBottom">
    <item name="borderColor">@color/black</item>
     <item name="backgroundColor">@color/blue_grey</item>
</style>

border_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
      <shape 
        android:shape="rectangle">
            <stroke android:width="1dp" android:color="@color/black" />
            <solid android:color="?attr/borderColor" />

        </shape>
   </item>

   <item android:bottom="1dp"> 
      <shape 
        android:shape="rectangle">
            <solid android:color="?attr/backgroundColor" />
        </shape>
   </item>

</layer-list>

My implementation of the style

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/BlackBorderBlueGreyBackground"
    android:orientation="vertical" >

</RelativeLayout>

Can someone point me in the right direction?

Mike Bryant
  • 2,455
  • 9
  • 39
  • 60
  • `1dp` might be the bug. Try a bigger value, like 5dp. – interlude Apr 09 '14 at 10:42
  • 1dp is for the border which displays correctly, I'm trying to be able to have only one layout where I can specify the background color and border color through styles and attr's – Mike Bryant Apr 09 '14 at 12:44
  • @MikeBryant First of all, I don't see where you apply your custom attributes to layout( for this you will have to implement custom RelativeLayout). Secondly, I don't understand the purpose of your first drawable declared in `border_bottom_black_bluegrey_background.xml`. Can you explain please ? – kiruwka Apr 14 '14 at 10:25
  • At the moment I have to create a layout for each border/background color combination. That's why I have a "border_bottom_black_bluegrey_background.xml" I would like to only have one "border_bottom.xml" where I can specify the border & background color through styles I specify the attributes in the border_bottom.xml (probably not correctly) which I would then be able to apply in styles.xml – Mike Bryant Apr 14 '14 at 11:15
  • I have 23 hours to award the bounty, however I'm not sure how to extend it without starting a new one. If you can find the answer before then, the bounty's all yours! – Mike Bryant Apr 14 '14 at 11:48
  • @MikeBryant Well, I tried and it turns out there is no way to access attributes from drawables declared in xml. Attributes are only to be used with custom Views. I tried myself and also there is confirmation for you [here](http://stackoverflow.com/questions/8041537/how-to-reference-style-attributes-from-a-drawable), [here](http://stackoverflow.com/questions/3757696/can-a-selector-resource-use-a-color-defined-in-a-style) and [here](http://stackoverflow.com/questions/12115125/creating-custom-style-crashes-app). Sorry, seems like you will have to create multiple drawables for each set of colors – kiruwka Apr 14 '14 at 14:00
  • or use the approach from provided answer if it works for you. But there is no way to do it declaratively in xml. – kiruwka Apr 14 '14 at 14:02
  • Yeah I had a feeling that it might not be possible, thanks for trying though, I appreciate you taking the time to help. I knew you can do this programatically, but I prefer to keep all styling in code to a minimum because I find it makes the controllers "dirty" and harder to debug.. – Mike Bryant Apr 14 '14 at 14:09

1 Answers1

0

I think the issue is that when you use the attrs.xml that is for use in creating custom views. The problem I see with your code above is that border_bottom.xml is pointing to color attributes that have not been explicitly declared and it has no way of knowing what color it should be. If want to create a layout that you can continuously use my suggestion would be to create a class that creates backgrounds for you and if need be you can just save the drawable created. This is a class that I use to draw underlines under views I think it should suffice.

public final class Background extends ShapeDrawable {
public final static int RECTANGLE = 0x00;
public final static int ROUND_RECTANGLE = 0x01;
public final static int UNDERLINE = 0x02;
    //For future use
//public final static int CIRCLE = 0x03;

private int shape_type;
private final Paint fill_paint, stroke_paint;
private int stroke_width;

private static RectShape rectangle = new RectShape();
private static RoundRectShape round_rectangle;
//private static OvalShape circle = new OvalShape();

public static final Background newInstance(int shape, int background_color, int stroke_color, int stroke_width) {
    switch (shape) {
    case UNDERLINE:
    case RECTANGLE:
        return new Background(shape, rectangle, background_color, stroke_color, stroke_width);
    case ROUND_RECTANGLE:
        round_rectangle = new RoundRectShape(new float[] { stroke_width, stroke_width,
                stroke_width, stroke_width, stroke_width, stroke_width, stroke_width, stroke_width }, null, null);

        return new Background(shape, round_rectangle, background_color, stroke_color, stroke_width);
    default:
        return null;
    }
}

private Background(int shape_type, Shape shape, int fill_color, int stroke_color, int stroke_width) {
    super(shape);
    this.shape_type = shape_type;
    this.stroke_width = stroke_width;

    //set the fill_paint
    fill_paint = new Paint(getPaint());
    fill_paint.setColor(fill_color);
    stroke_paint = new Paint(fill_paint);

    //set the stroke paint
    stroke_paint.setStyle(Paint.Style.STROKE);
    stroke_paint.setAntiAlias(false);
    stroke_paint.setStrokeWidth(stroke_width);
    stroke_paint.setColor(stroke_color);
}

@Override   
protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
    switch(shape_type) {
    case UNDERLINE:
        final RectF underline = new RectF(getBounds().left - stroke_width, getBounds().top - stroke_width, 
                getBounds().right + stroke_width,
                getBounds().bottom - stroke_width);

        canvas.drawRect(underline, stroke_paint);
        break;
    case RECTANGLE:
        final RectF rectangle = new RectF(stroke_width, stroke_width, getBounds().right - stroke_width,
                getBounds().bottom - stroke_width);

        canvas.drawRect(rectangle, fill_paint);
        canvas.drawRect(rectangle, stroke_paint);
        break;
    }
}

}

Then you can just save the drawable created and use it on multiple layouts in code.

//untested code
Layout layout = (RelativeLayout)findViewById(R.id.your_layout_id);
Background background = Background.newInstance(Background.RECTANGLE, Color.GRAY, Color.BLUE, 1);
layout.setBackground(background);
LeonPierre
  • 145
  • 2
  • 5