Create button background by color in runtime - android

In my app, I have the selector below.
Is possible to create a similar selector, by color instead of by image-drawable, but at runtime?
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_window_focused="false"
android:drawable="#drawable/ic_button_orange_normal" />
<item
android:state_pressed="true"
android:drawable="#drawable/ic_button_orange_pressed" />
<item
android:state_focused="true"
android:drawable="#drawable/ic_button_orange_focused" />
<item
android:drawable="#drawable/ic_button_orange_normal" />
</selector>
I have tried the function below to create the selector in runtime, but I need to create a round button, not square.
Is this possible?
private StateListDrawable makeSelector(int color)
{
StateListDrawable res = new StateListDrawable();
res.setExitFadeDuration(400);
res.setAlpha(45);
ShapeDrawable d = new ShapeDrawable();
res.addState(new int[] { android.R.attr.state_pressed }, new ColorDrawable(ColorUtilities.decrease(color, 0x003030)));
res.addState(new int[] {}, new ColorDrawable(Color.TRANSPARENT));
return res;
}
Edit again... I have found the way to create round selectors...
private static ShapeDrawable getRoundShapeDrawable(int color)
{
ShapeDrawable shape_drawable = new ShapeDrawable(new OvalShape());
shape_drawable.getPaint().setColor(color);
return shape_drawable;
}
public static StateListDrawable getRoundShapeSelector(int normal_color, int pressed_color)
{
ShapeDrawable normal_shape_drawable = getRoundShapeDrawable(normal_color), pressed_shape_drawable = getRoundShapeDrawable(pressed_color);
StateListDrawable state_list_drawable = new StateListDrawable();
state_list_drawable.addState(new int[] { android.R.attr.state_pressed }, pressed_shape_drawable);
state_list_drawable.addState(new int[] { android.R.attr.state_focused }, pressed_shape_drawable);
state_list_drawable.addState(new int[] { }, normal_shape_drawable);
return state_list_drawable;
}

You can use "StateListDrawable"
StateListDrawable
and add state in it.
Let me know if you face problem in implementing this.

If you want to create a round button. Then you can use layer-list then add this drawable xml as a Layer.
Eg. How to create round button.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:bottom="0dp"
android:left="0dp"
android:right="0dp"
android:top="0dp">
<shape android:shape="rectangle" >
<corners android:radius="40dp" />
<solid android:color="#fff" />
</shape>
</item>
<item
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp">
<shape android:shape="rectangle" >
<corners android:radius="40dp" />
<solid android:color="#d3d3d3" />
</shape>
</item>
<item
android:bottom="1dp"
android:left="2dp"
android:right="1dp"
android:top="2dp">
<shape android:shape="rectangle" >
<corners android:radius="40dp" />
<solid android:color="#EBEBEB" />
</shape>
</item>
<item
android:id="#+id/wl_btn_bg_shape"
android:bottom="6dp"
android:left="6dp"
android:right="6dp"
android:top="6dp">
<shape android:shape="rectangle" >
<corners android:radius="40dp" />
<solid android:color="#00caa8" />
</shape>
</item>
</layer-list>
create and save about code in drawable as wl_btn_bg.xml
Now you need to add this xml as view background
LayerDrawable layers = (LayerDrawable) context.getResources().getDrawable(R.drawable.wl_btn_bg);
view.setBackground(layers);
and if you want to change the color of selector then you need to add following lines in layers
LayerDrawable layers = (LayerDrawable) context.getResources().getDrawable(R.drawable.wl_btn_bg);
GradientDrawable shape = (GradientDrawable) (layers.findDrawableByLayerId(R.id.wl_btn_bg_shape));
shape.setColor(Color.parseColor("#ff6655"));
Done....

Related

Customise drawable state

I would like to change an existing drawable state list (default and pressed) colours programmatically.
This is my custom drawable I would like to edit:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<ripple android:color="#1AFFFFFF">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
<corners android:radius="5dp" />
</shape>
</item>
</ripple>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
<corners android:radius="5dp" />
</shape>
</item>
</selector>
I've tried How can I change colors in my StateListDrawable? but I am getting an error:
val customButtonTest: Drawable
get() {
val stateListDrawable = ContextCompat.getDrawable(context, R.drawable.custom_button) as StateListDrawable
val drawableContainerState = stateListDrawable.constantState as DrawableContainer.DrawableContainerState
val children = drawableContainerState.children
val selectedItem = children[0] as GradientDrawable
selectedItem.setColor(Color.parseColor("#FD00DF"))
return stateListDrawable.current
}
**ClassCastException: android.graphics.drawable.RippleDrawable cannot be cast to android.graphics.drawable.GradientDrawable**
As I am intend to use it for a BandingAdapter, how can I change both state colours and return it as a drawable?
Thanks
** Edited ---
val customButtonTest: Drawable
get() {
val stateListDrawable =
ContextCompat.getDrawable(context, R.drawable.custom_button) as StateListDrawable
val drawableContainerState =
stateListDrawable.constantState as DrawableContainer.DrawableContainerState
val children = drawableContainerState.children
val pressedState = children[0] as RippleDrawable
val defaultState = children[1] as GradientDrawable
pressedState.setColor(ColorStateList.valueOf(Color.parseColor("#1AFFFFFF")))
defaultState.setColor(Color.parseColor("#4267b2"))
drawableContainerState.addChild(pressedState)
drawableContainerState.addChild(defaultState)
return drawableContainerState.newDrawable()
}
How to change an item solid colour inside the ripple container? As
pressedState.setColor(ColorStateList.valueOf(Color.parseColor("#1AFFFFFF")
changes ripple colour itself.
If someone needs to access ripple and its child, here you go:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<ripple android:color="#1A1C1C1C">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
<corners android:radius="5dp" />
</shape>
</item>
</ripple>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
<corners android:radius="5dp" />
</shape>
</item>
</selector>
val customButtonTest: Drawable
get() {
val stateListDrawable = ContextCompat.getDrawable(context, R.drawable.custom_button) as StateListDrawable
val drawableContainerState = stateListDrawable.constantState as DrawableContainer.DrawableContainerState
val children = drawableContainerState.children
val pressedState = children[0] as RippleDrawable
val defaultState = children[1] as GradientDrawable
val layerDrawable = pressedState as LayerDrawable
val rippleChildDrawable = layerDrawable.getDrawable(0) as GradientDrawable
rippleChildDrawable.setColor(primaryColor)
defaultState.setColor(primaryColor)
return drawableContainerState.newDrawable()
}
To reach <items> inside <ripple> you need to cast RippleDrawable to LayerDrawable

Bug in drawable

I created a custom drawable for a button and found that if I share the drawable between an array of button, any button I press within the array of button will cause the last button in the array to be appear to be pressed as well. Here is the code...
//xml code for stage button
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/stage_button_selected"></item>
<item android:state_pressed="false"
android:drawable="#drawable/stage_button_deselected"></item>
</selector>
//xml code for stage_button_selected
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="5dp" android:right="5dp" android:top="5dp"
android:bottom="5dp">
<shape android:shape="oval" android:innerRadius="5dp">
<solid android:color="#android:color/holo_blue_light"></solid>
<stroke android:color="#android:color/holo_blue_dark"
android:width="2dp"></stroke>
<corners android:radius="5dp"></corners>
</shape>
</item>
</layer-list>
//xml code for stage_button_deselected
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="5dp" android:right="5dp" android:top="5dp"
android:bottom="5dp">
<shape android:shape="oval" android:innerRadius="5dp">
<solid android:color="#android:color/white"></solid>
<stroke android:color="#dddddd" android:width="2dp"></stroke>
<corners android:radius="5dp"></corners>
</shape>
</item>
</layer-list>`
//android code
Drawable drawable = resources.getDrawable(R.drawable.stage_button, null);
for(int ctr=0; ctr<btnStage.length; ctr++){
GridLayout.Spec specRow = GridLayout.spec(specStage[ctr][0], 1);
GridLayout.Spec specCol = GridLayout.spec(specStage[ctr][1], 1);
GridLayout.LayoutParams lp = new GridLayout.LayoutParams(specRow,
specCol);
btnStage[ctr] = new Button(MainActivity.this);
btnStage[ctr].setBackground(drawable);
btnStage[ctr].setText("Stage " + stageNo(ctr));
btnStage[ctr].setWidth(btnWidth);
btnStage[ctr].setHeight(btnHeight);
final String temp = stageNo(ctr);
btnStage[ctr].setOnClickListener(MainActivity.this);
ss_gridLayout.addView(btnStage[ctr], lp);
}
The solution to the problem is to create an array of drawable for use of the array of buttons, however it doesn't make sense that I cannot share the drawable between the array of buttons without it causing an unwanted effect...

Change color of progress bar background in android

I have a Progressbar, which look is defined in a xml file:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background">
<shape>
<corners android:radius="#dimen/round_corners_elements" />
<solid android:color="#color/blue_50" />
<stroke android:width="1px" android:color="#color/light_gray" />
</shape>
</item>
<item android:id="#android:id/progress">
<clip android:clipOrientation="horizontal" android:gravity="left">
<shape>
<corners
android:radius="#dimen/round_corners_elements"
/>
<solid android:color="#color/signaling_color" />
<stroke android:width="1px" android:color="#color/light_gray" />
</shape>
</clip>
</item>
</layer-list>
But as i need to change the background color (blue50) to any other color dynamically, i failed. Anyone got an idea how i can change it programmatically? The progress can stay as it is. The special thing about is, that progress bar has round corners, which is necessary. I tried several approaches, but none worked for me.
setBackgroundTintList didn't worked as is only available >API21
int[][] states = new int[][] { new int[] { android.R.attr.background }, new int[] { -android.R.attr.process } };
int[] colors = new int[] { Color.parseColor("#000000"), Color.BLACK };
ColorStateList myList = new ColorStateList(states, colors);
progressBar.setBackgroundTintList(myList);
progressBar.setBackgroundTintMode(PorterDuff.Mode.SRC_OVER);
setBackgroundColor Also had no effect:
progressBar.setBackgroundColor(ContextCompat.getColor(this, R.color.white));
Anyone got an idea?
You will have to obtain the LayerDrawable used by the ProgressBar and then either
use findDrawableByLayerId(android.R.id.background) to get the Drawable for that layer and make modifications to it at runtime; or
create a new Drawable and call setDrawableByLayerId(android.R.id.background, newBackgroundDrawable)
If I recall correctly, <shape> tags result in a GradientDrawable being created at runtime by the system, and that conveniently has methods like setColor()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progressBar.setProgressBackgroundTintList(ColorStateList.valueOf(tint));
} else {
LayerDrawable drawable = (LayerDrawable) progressBar.getProgressDrawable();
Drawable background = new ColorDrawable(tint);
drawable.setDrawableByLayerId(android.R.id.background, background);
progressBar.setProgressDrawable(drawable);
}

Change color of Button on click while keeping its shape

The code below changes the background color of the button from green to red when clicked, where the appropriate MvxColor's for bgColor are defined in my StartStopCommand. It works, but the MvxBind BackgroundColor overrides the shape of the Button.
Is there a way to keep this shape in Android and only change the color in such a way that it does not require platform specific code (as below)?
The button in Android:
<Button
android:text="Start"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:layout_margin="5dip"
android:layout_width="270dp"
android:layout_height="wrap_content"
android:background="#drawable/buttonshape"
local:MvxBind="Click StartStopCommand; BackgroundColor bgColor, Converter=NativeColor" />
where the shape xml is:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<corners
android:radius="10dp"
/>
<solid
android:color="#3497db"
/>
<size
android:width="270dp"
android:height="60dp"
/>
</shape>
Since you are setting a Background resource for your button, using BackgroundColor later will just discard everything you set there.
What you probably want is a Selector for your Background resource, which determines what is set when the Button is normal, focused, active and whatever other states you want to respond to. This is a very platform specific thing and you probably won't be able to reuse the behavior on other platforms.
Anyways. You could try something like this:
shape_selected.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="10dp" />
<solid android:color="#dddddd" /> <!-- use selected color here -->
<size
android:width="270dp"
android:height="60dp"/>
</shape>
shape_unselected.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="10dp" />
<solid android:color="#3497db" />
<size
android:width="270dp"
android:height="60dp"/>
</shape>
button_background.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/shape_selected" android:state_selected="true" />
<item android:drawable="#drawable/shape_selected" android:state_pressed="true" />
<item android:drawable="#drawable/shape_unselected" />
</selector>
Then you would use button_background for your Buttons Background property:
<Button
...
android:background="#drawable/buttonshape"
local:MvxBind="Click StartStopCommand" />
Otherwise if you want to keep using your property to set the BackgroundColor without nuking the shape, you will need to use the Background property instead, have a converter which dynamically builds the shape drawable.
So in your converter you would just return a ShapeDrawable with your color, which you could build like this:
private ShapeDrawable CreateShapeDrawable(Color color)
{
var roundRect = new RoundRectShape (new [] { 10f, 10f, 10f, 10f, 10f, 10f, 10f, 10f }, null, null);
var shape = new ShapeDrawable (roundRect) {
Paint = new Paint { Color = color }
};
return shape;
}
EDIT:
Something like this would probably work for a converter approach:
public class MyStateToDrawableConverter : MvxValueConverter<MvxColor, Drawable>
{
protected override Drawable Convert(MvxColor value, object parameter, CultureInfo culture)
{
return CreateShapeDrawable(value.ToNative());
}
private ShapeDrawable CreateShapeDrawable(Color color)
{
var roundRect = new RoundRectShape (new [] { 10f, 10f, 10f, 10f, 10f, 10f, 10f, 10f }, null, null);
var shape = new ShapeDrawable (roundRect) {
Paint = new Paint { Color = color }
};
return shape;
}
}
button_selected
<solid android:color="#color/btn_grey" />
<stroke
android:width="1dp"
android:color="#color/transparant" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
button_unselected
<solid android:color="#color/orange_500" />
<stroke
android:width="1dp"
android:color="#color/transparant" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
In java file
private Button btnMap;
btnMap = (Button) findViewById(R.id.btn_map);
if(bool)
{
btnMap.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_selected));
}
else {
btnMap.setBackgroundDrawable(getResources().getDrawable(R.drawable.button_unselected));

How can I change ALL bitmaps on a layer-list programmatically?

Here is the layer-list, named index.xml:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="#+id/ll_23_1015_0" android:top="0dp" android:left="0dp" android:bottom="0dp" android:right="0dp">
<shape android:shape="rectangle">
<size android:width="1205dp" android:height="1795dp"/>
<solid android:color="#FFF"/>
</shape>
</item>
<item android:id="#+id/ll_23_1015_1" android:top="75dp" android:left="50dp" android:bottom="1366dp" android:right="919dp">
<bitmap android:src="#drawable/pic_23" />
</item>
<item android:id="#+id/ll_23_1015_2" android:top="504dp" android:left="50dp" android:bottom="937dp" android:right="919dp">
<bitmap android:src="#drawable/pic_23" />
</item>
<item android:id="#+id/ll_23_1015_3" android:top="933dp" android:left="50dp" android:bottom="508dp" android:right="919dp">
<bitmap android:src="#drawable/pic_23" />
</item>
<item android:id="#+id/ll_23_1015_4" android:top="1362dp" android:left="50dp" android:bottom="79dp" android:right="919dp">
<bitmap android:src="#drawable/pic_23" />
</item>
</layer-list>
and this is my code:
imgVShare = (ImageView) findViewById(R.id.imgVShare);
String path = "/storage/sdcard0/temp_photo.jpg";
Drawable finalPic = Drawable.createFromPath(path);
LayerDrawable myDrawable= (LayerDrawable)getResources().getDrawable(R.drawable.index);
for(int i=0; i<5; i++)
{
String ID = "ll_23_1015_" + (i+1);
int resID = getResources().getIdentifier(ID, "id", getPackageName());
Drawable layer = myDrawable.findDrawableByLayerId(resID);
layer = finalPic;
myDrawable.setDrawableByLayerId(resID, layer);
}
imgVShare.setImageDrawable(myDrawable);
All I want to do is to replace all bitmaps inside layer-list with "temp_photo.jpg" that is on internal storage. This method just replace the last bitmap and clear the rest.....please help....!

Categories

Resources