Programatically change background of Drawable, maintaining the corner radius - android

interview_timeline_row.xml
<LinearLayout
android:id="#+id/interviewTimelineIconLayout"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_marginTop="20dp"
android:background="#drawable/timeline_row_icon_layout_bg"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/interviewTimelineRowIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:adjustViewBounds="false"
android:cropToPadding="false"
android:padding="6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
timeline_row_icon_layout_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="#dimen/_50sdp" />
<stroke android:width="1dp" android:color="#color/white" />
<solid android:color="#color/ic_rescheduled"/> //need to add this programatically
</shape>
InterviewTimeline.java
iconBg = row.findViewById(R.id.interviewTimelineIconLayout);
iconBg.setBackgroundColor(getResources().getColor(R.color.ic_rescheduled)); //this is the wrong way to go about it
I want to use the timeline_row_icon_layout_bg.xml in various places in my app, and it should have a different background color each time. If I use the iconBg.setBackgroundColor() method, then it ignores the radius and I have a square background color.

Since you are just using the shape to create a layout with rounded corners and a border, the first option is to wrap your LinearLayout inside a CardView and then apply to the card the corner radius, the stroke and the background color.
Otherwise you could use the MaterialShapeDrawable included in the Material Components Library to draw custom shapes.
Just remove from the LinearLayout the android:background:
<LinearLayout
android:id="#+id/interviewTimelineIconLayout"
android:layout_width=".."
android:layout_height="..
..>
<!-- ..... -->
</LinearLayout>
Then in your code you can apply a ShapeAppearanceModel. Something like:
float radius = getResources().getDimension(R.dimen.corner_radius);
LinearLayout linearLayout= findViewById(R.id.interviewTimelineIconLayout);
ShapeAppearanceModel shapeAppearanceModel = new ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED,radius)
.build();
MaterialShapeDrawable shapeDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
//Fill the LinearLayout with your color
shapeDrawable.setFillColor(ContextCompat.getColorStateList(this,R.color.yourColor));
//Stroke color and width
shapeDrawable.setStrokeWidth(2.0f);
shapeDrawable.setStrokeColor(...);
ViewCompat.setBackground(linearLayout,shapeDrawable);
In this way you easily change and set the color background and the stroke.

Copy your "timeline_row_icon_layout_bg.xml" file and rename it "timeline_row_icon_layout_bg_new.xml"
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="#dimen/_50sdp" />
<stroke android:width="1dp" android:color="#color/white" />
<solid android:color="#color/put_the_color_you_need" />
</shape>
And you can set background to layout:
iconBg.setBackgroundResource(R.drawable.timeline_row_icon_layout_bg_new);

set the background of LinearLayout programmatically.
val bgDrawable = resources.getDrawable(R.drawable.timeline_row_icon_layout_bg, null).apply{
colorFilter = PorterDuffColorFilter(
ResourcesCompat.getColor(resources, R.color.ic_rescheduled, null),
PorterDuff.Mode.SRC_IN
)
}
iconBg.background = bgDrawable
move the shape drawable timeline_row_icon_layout_bg to the drawable resource folder.

Related

change the background color of textview according to textview shape when click on text view

I have three Material text views, I want to change the background color of the text view according to the text view shape. I tried multiple options but it goes outside of text view shape. I have round corners text view but color fill like a simple rectangle.
<com.google.android.material.textview.MaterialTextView
android:id="#+id/textViewLoseWeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="65dp"
android:text="Lose Weight"
android:textColor="#color/purple_500"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewGoals"
tools:ignore="MissingConstraints" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginTop="4dp"
android:id="#+id/textViewLoseWeightSubtitle"
android:background="#drawable/textview_outline_style"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:text="Get leaner and improve Your fitness"
android:textColor="#color/purple_500"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewLoseWeight" />
//.java
View.OnClickListener listener = new View.OnClickListener() {
#SuppressLint("NonConstantResourceId")
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.textViewLoseWeightSubtitle:
mLoseWgt.setBackgroundColor(Color.parseColor("#363C60"));
mLoseWgt.setTextColor(Color.WHITE);
after clicking on the text view, it changes like above. I want background color inside round corner shape like the original text view have
You define your TextView and also it's android:background attribute:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get leaner and improve Your fitness"
android:background="#drawable/round_fill_color"
android:layout_centerInParent="true"
android:textColor="#ffffff"
android:padding="10dp"/>
</RelativeLayout>
And here the mentioned attribute in a new drawable called round_fill_color.xml:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#363c60"/>
<stroke android:width="3dp" android:color="#ffffff" />
<corners android:radius="30dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
That is the suggested way to make custom attributes for single widgets.
If you want to define one layout for all I recommend to do that over styles.xml and themes.
The result:
You can use something different:
<com.google.android.material.textview.MaterialTextView
android:id="#+id/textview"
../>
and then apply a MaterialShapeDrawable
val radius = resources.getDimension(R.dimen.default_corner_radius)
val shapeAppearanceModel = ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, radius)
.build()
val shapeDrawable = MaterialShapeDrawable(shapeAppearanceModel)
shapeDrawable.fillColor = ContextCompat.getColorStateList(this,R.color.xxx)
shapeDrawable.setStroke(2.0f, ContextCompat.getColor(this,R.color.xx));
ViewCompat.setBackground(textView, shapeDrawable)
Use this ShapeAppearanceModel to have 50% rounded corners:
val shapeAppearanceModel = ShapeAppearanceModel()
.toBuilder()
.setAllCorners(RoundedCornerTreatment())
.setAllCornerSizes(RelativeCornerSize(0.5f))
.build()

CardView: How do I add a gradient background while maintaining the radius

I want to re-create the image below with a CardView. To achieve this, I created a gradient file (btn_gradient.xml) and then proceeded to create the CardView.
CardView implementation:
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_margin="25dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardPreventCornerOverlap="false">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#drawable/btn_gradient"
android:text="Create Account"
android:textColor="#000000"
android:textStyle="bold"
android:textAllCaps="false"/>
</android.support.v7.widget.CardView>
Everything works fine this way except that the radius disappears and this is not what I want. Is there a way I can set the gradient directly on the CardView? The cardBackgroundColor attribute accepts only colors, not drawables.
Any help would be appreciated.
Addendum: As requested, this is my btn_gradient.xml file:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:type="linear"
android:angle="0"
android:startColor="#ffc200"
android:endColor="#fca10b" />
</shape>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/_10sdp"
app:cardCornerRadius="#dimen/_10sdp"
app:cardElevation="#dimen/_1sdp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#drawable/gradient_16"
android:padding="#dimen/_6sdp">
</LinearLayout>
</androidx.cardview.widget.CardView>
and gradient be like
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#5B86E5"
android:endColor="#36D1DC"
android:angle="180" />
<corners
android:radius="10dp">
</corners>
</shape>
CardView card_radius and gradient radius should be same dimentions
If I may ask, are you per-chance testing/running on a pre-lollipop Android device? Your code seems to work as you desire (curved corners showing with the gradient) except on Android 4.
To achieve the desired result on pre-lollipop devices, you can add <corners android:radius="4dp" /> to your #drawable/btn_gradient file, (you would have to set the corner radius to match the CardView's cardCornerRadius.
Move this line
android:background="#drawable/btn_gradient"
To the CardView object
UPDATE
My bad:
Inside the place a layout to wrap the content of the .
In this case, I'd go with a <FrameLayout> like this:
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLyout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/btn_gradient">
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#004e92"
android:endColor="#614385"
android:angle="90" />
<corners
android:topRightRadius="10dp"
android:bottomLeftRadius="10dp"
android:bottomRightRadius="10dp"
android:topLeftRadius="10dp">
</corners>
</shape>
[![enter image description here][1]][1]
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:background="#drawable/color"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
app:cardCornerRadius="10dp"
android:layout_height="100dp">
<LinearLayout
android:layout_width="match_parent"
android:background="#drawable/cardcolor"
android:layout_height="100dp">
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>``
</LinearLayout>
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/iewIm.png
[1]: https://i.stack.imgur.com/pHIlW.png
I've tried integrating the gradient on card view dynamically & faced the issue that the corner radius got impacted that was set on the view property in xml file like app:cardCornerRadius="#dimen/margin_20".
Solution :
Implementing the gradient on card view. I'm getting 2 gradients from api that will be incorporated from left to right.
` try {
val startColor = sample?.gradient1
val endColor = sample?.gradient2
val colors =
intArrayOf(Color.parseColor(startColor),
Color.parseColor(endColor))
val gradientDrawable = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, colors
)
myCardView.background = gradientDrawable
} catch (e: Exception) {
// default color to white
myCardView.setBackgroundColor(
ContextCompat.getColor(
mContext,
R.color.white
)
)
}`
Now on running the code, the card view renders with the gradient value but the issue occurs with the corner radius of card view that was set statically at xml. The corner radius got removed. To solve the issue, we simply use to set the radius of the gradient drawable that holds the drawable color in gradient form. Just add below 3 lines before setting the background.
gradientDrawable.colors = colors
// setting the corner radius on gradient drawable
gradientDrawable.cornerRadius = 40f
myCardView.background = gradientDrawable
The final code with gradient configuration & setting the corner radius is :
try {
val startColor = sample?.gradient1
val endColor = sample?.gradient2
val colors =
intArrayOf(Color.parseColor(startColor),
Color.parseColor(endColor))
val gradientDrawable = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, colors
)
gradientDrawable.colors = colors
// setting the corner radius on gradient drawable
gradientDrawable.cornerRadius = 40f
myCardView.background = gradientDrawable
myCardView.background = gradientDrawable
} catch (e: Exception) {
// default color to white
myCardView.setBackgroundColor(
ContextCompat.getColor(
mContext,
R.color.white
)
)
}`
Screenshot for better view of code :
Hope this will help. Happy Coding :) Cheers!
Do not use android:background attribute in XML file.
use app:cardBackground instead.
To wrap it up, first create a gradient background XML file in drawables.
then assign it to the app:cardBackgroundColor like this:
app:cardBackgroundColor="#drawable/gradient_background"
if you don't know how to create the gradient_background.xml, write click on drawables directory, create new xml file and paste the code below.
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="#color/secondaryColor"
android:endColor="#color/primaryColor"
android:angle="90"/>
</shape>

After changing button color of drawable my stroke is gone and I cannot set the stroke again

I am changing the color of one of my buttons which is a drawable shape, but it removes the stroke. I try to set the stroke again, but it isn't working.
It needs to be mutable since I am using this shape with other buttons
Activity:
val bottomButton: Button = this.bottomButton
val drawable = bottomButton.getBackground() as GradientDrawable
drawable.mutate().setColorFilter(backBtnColor,PorterDuff.Mode.SRC_OVER)
drawable.setStroke(5,lightBgColor)
XML Drawable:
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="5dp"
android:color="#color/colorAccent"/>
<corners
android:radius="150dp" />
<padding
android:left="5dp"
android:right="5dp"
android:top="20dp"
android:bottom="20dp"/>
<solid android:color="#color/colorPrimary"/>
</shape>
I set the drawable in the Layout XML
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="#+id/bottomButton"
android:layout_marginHorizontal="24dp"
android:textSize="32dp"
android:layout_marginVertical="16dp"
android:background="#drawable/btn_rounded" tools:text="Options" android:text="Options"/>
The result is that the new color fills the entire button (which is good that the color changes) but it removes the Stroke. I want the new color, but retain the stroke, or at the very least let me set the stroke back to the original.
I figured out the answer:
In Activity file:
val backBtnColor:Int = getResources().getColor(R.color.colorPrimaryDark)
val bottomButton: Button = this.bottomButton
val drawable = bottomButton.getBackground().mutate() as GradientDrawable
drawable.setColor(backBtnColor)
the key is to add mutate() to the val drawable when declaring it, not when you call it later. This gives you the ability to setColor() which is the property you need.

How to paint a circle as background in a textView instead of square background?

I'm trying to paint a circle as background of a textView dynamically, but instead of that it paints the entire square background
I've tried with a static way that works :
here my code
circle_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<corners android:radius="4dip" />
<stroke
android:width="5dip"
android:color="#color/colorPrimary" />
<solid android:color="#color/colorPrimary" />
</shape>
list_item.xml
<TextView
android:id="#+id/tv_tag"
android:layout_width="50dp"
android:layout_height="54dp"
android:layout_marginStart="10dp"
android:layout_marginLeft="25dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:background="#drawable/circle_drawable"
android:gravity="center"
android:text="B"
android:textColor="#fff"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.454" />
it works statictly with:
TextView tv_tag = (TextView)v.findViewById(R.id.tv_tag);
tv_tag.setText(itemTag);
but when I try to change the background color with setBackgroundColor it doesn't work
TextView tv_tag = (TextView)v.findViewById(R.id.tv_tag);
tv_tag.setText(itemTag);
tv_tag.setBackgroundColor(Color.parseColor("#D81B60"));
Any help will be awesome for me.
try like this, hope it will help you.
GradientDrawable bg = (GradientDrawable)tv_tag.getBackground();
bg.setColor(Color.parseColor("#D81B60"));
setBackgroundColor will not use a shape drawable. It will just set color, not your expected shape. So instead use setBackground like-
tv_tag.setBackground(ContextCompat.getDrawable(context,R.drawable.circlw_drawable))
TextView tv_tag = (TextView)v.findViewById(R.id.tv_tag);
tv_tag.setText(itemTag);
tv_tag.setBackground(ContextCompat.getDrawable(getApplicationContext(), R.drawable.circle_drawable));
Using setBackgroundColor() method, we change the background color, but color set to background of textView, the by default background shape of any view in android is rectangle.
To set the particular background shape we can use the drawable.
In your code you changed the background color of textView with cause of rectangular shape of textview.
To change the color of background you need to change color of drawable.
Please try below code to change the color of drawable:
Add this code to your activity
TextView tv_tag=findViewById(R.id.tv_tag);
Drawable mDrawable = getResources().getDrawable(R.drawable.circle_drawable);
mDrawable.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SCREEN));
tv_tag.setBackground(mDrawable);
You could modify it simply like this
GradientDrawable bgShape = (GradientDrawable)btn.getBackground();
bgShape.setColor(Color.BLACK);

Ripple effect over the actual border

I have created a listview and added a border to it's items.
something like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"
android:background="#drawable/listviewborderbox"
android:padding="10dp" >
<LinearLayout
android:id="#+id/sharedbyyouNameLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight=".70"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="#+id/sharedbyyoutext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:text="#string/sampletext1"
android:textColor="#color/blackText"
android:textSize="14sp" />
<TextView
android:id="#+id/sharedbyyouselected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:text="#string/sampletext2"
android:textColor="#color/blackText"
android:textSize="16sp"
tools:ignore="RtlHardcoded" />
</LinearLayout>
<LinearLayout
android:id="#+id/sharedbyyouLayoutforarrow"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight=".10"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="#+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_next"
tools:ignore="RtlSymmetry,RtlHardcoded,ContentDescription" />
</LinearLayout>
</LinearLayout>
And I have ripple effect value in Drawable-v21 like this:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#android:color/white"> <item android:drawable="#color/footercolor"/> </ripple>
Border shape xml in drawable folder is this:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#color/colorforbodybox" />
<corners android:radius="10dip"/>
<stroke android:width="2dip" android:color="#color/colorforborder" />
</shape>
Ripple effect works but ripple effect is shown outside the border line that I have drawn. Please check pic below:
How do I make the ripple effect not to cross the border in the list view?
To achieve rounded corner ripple effect change your ripple xml file to:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="#android:color/white"
tools:targetApi="lollipop">
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="10dp"/>
<solid android:color="#color/footercolor"/>
</shape>
</item>
</ripple>
The problem I had was that the corner radius of my views was not a fixed value therefore using the xml suggested didn't work for me.
I needed a something that would adapt the ripple effect every time regardless of the shape used so...
I used a simple view extension:
fun View.addRippleEffect(rippleColorId: Int = R.color.rippleColor) { // Here you can pass the color you want for the ripple effect and assign a "default" value
val rippleColor = ColorStateList.valueOf(ContextCompat.getColor(App.context(), rippleColorId))
this.background = RippleDrawable(
rippleColor, // This is the color of the effect and needs to be a ColorStateList
this.background, // ( = content ) With this you use your view's background as the content of the ripple effect
this.background) // ( = mask ) With this the ripple will take the shape of the background and not "spill over". (Could be null IF you did set the previous variable "content = this.background")
}
OR, if you want to separate the two layers:
fun View.addRippleEffect(rippleColorId: Int = R.color.rippleColor) {
val rippleColor = ColorStateList.valueOf(ContextCompat.getColor(App.context(), rippleColorId))
this.foreground = RippleDrawable( //Using the foreground allows you to give the view whatever background you need
rippleColor,
null, //Whatever shape you put here will cover everything you've got underneath so you probably want to keep it "null"
this.background)
}
Basically you give a view a background (rounded rectangle with borders in your case) then you can simply call the extension in your Activity/Fragment:
whateverView.addRippleEffect()
//or
whateverView.addRippleEffect(R.color.red)
See: https://developer.android.com/reference/android/graphics/drawable/RippleDrawable
1. Create the Ripple Drawable Contains the Backgound Shape
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?colorControlHighlight"> //defaul ripple color
<item>
<shape //the background shape when it's not being click
android:shape="rectangle">
<solid
android:color="#color/colorPrimary" />
<corners
android:radius="32dp" />
</shape>
</item>
</ripple>
2. Applying the Drawable to the View and REMOVE THE SHADOW
<Button
style="?borderlessButtonStyle" //remove the default shadow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/background_button" //here
android:text="Sign up"
android:textAllCaps="false"
android:textColor="#android:color/white" />
Try setting clipToOutline of your View / ViewGroup to true, either in the code or through XML, it should limit the ripple's area accordingly (as long as you background shape matches the requirements, see the docs for more details).

Categories

Resources