I have a drawable object that I use to set the background of a linear layout, leaving the linear with the circular shape with a semi transparent orange line. but at some point in the code I need to change the color of this background (drawable object) to a color that I have only as a parameter and I don't have it in the colors of my color file. I need to change the stroke color of this drawable to a color that I have in one of my runtime variables
bg_static_show_password.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- center circle -->
<stroke android:color="#color/accent_color_alpha"
android:width="3dp" />
<solid android:color="#android:color/transparent" />
<size
android:width="28dp"
android:height="28dp"/>
</shape>
linear layout
<android.support.constraint.ConstraintLayout
android:id="#+id/card_show_password"
android:layout_width="#dimen/anim_layout_size"
android:layout_height="#dimen/anim_layout_size"
android:layout_marginTop="#dimen/anim_margin_top"
android:background="#drawable/bg_static_show_password"
android:layout_gravity="center"
app:layout_constraintTop_toBottomOf="#id/view_group_item"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
method I'm trying to use to make this color change
fun showAlternativeForAnimation(view: LinearLayout) {
val drawable = view.background as GradientDrawable
val theme = PasswordRecoveryTheme(ApplicationSession.instance?.themeId)
drawable.setStroke(1, theme.getAccentColor(ApplicationFactory.context!!))
}
the parameter of method is the LinearLayout
When I try, I get this exception:
kotlin.TypeCastException: null cannot be cast to non-null type android.graphics.drawable.GradientDrawable
Make a safe cast (as?), ensure that the view you pass has a shape drawable set as it's background and change parameter to view: View so as to allow usage for any view(LinearLayout, ConstraintLayout, etc).
fun showAlternativeForAnimation(view: View) {
val drawable = view.background as? GradientDrawable
val theme = PasswordRecoveryTheme(ApplicationSession.instance?.themeId)
drawable?.setStroke(1, theme.getAccentColor(ApplicationFactory.context!!))
}
I'm making a messenger application i made for every message a background with this shape:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners
android:bottomLeftRadius="80dp"
android:bottomRightRadius="80dp"
android:topRightRadius="80dp"
android:topLeftRadius="80dp"
/>
<solid android:color="#fff"/>
But the problem is when the message is long the corners be big and this make my message layout is bad! i want to know if there's another method to make a rounded background!
You can use 9-patch drawables.
These are png files whose allow you to define areas that can't be stretched and areas that will stay same size regardless the view size.
You can read about it here:
https://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
You can read about how to create it here:
https://developer.android.com/studio/write/draw9patch.html
9-patch drawables are commonly used in Adnroid SDK Standard views.
You can create a custom button class which extend Button class and then override the onLayout() method may be like this... where basically you are setting the corner radius = height/2
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(this.getBackground()!=null && this.getBackground() instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable)(this.getBackground().mutate());
GradientDrawable gd1 = (GradientDrawable)layerDrawable.findDrawableByLayerId(R.id.layer_one);
roundEdgeGradientDrawable(gd1, this.getHeight());
}
}
public static GradientDrawable roundEdgeGradientDrawable(GradientDrawable bgShape, float height) {
if (bgShape != null) {
float radius = height / 2;
float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};
bgShape.setCornerRadii(radii);
}
return bgShape;
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#color/headerColor" />
<corners android:radius="5dp" />
</shape>
I have an ImageButton which have a drawable background resource which is oval shape.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:angle="270"
android:color="#FFFF0000" />
</shape>
Here is the ImageButton in XML:
<ImageButton
android:id="#+id/c1"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_columnSpan="1"
android:layout_rowSpan="1"
android:background="#drawable/circle"
android:layout_margin="10dp"
/>
I need to change the color of the circle shape dynamically, this will be done by either change the backgroundTint property in the ImageButton or change the circle shape color.
NOTE:
I have array of strings that stores a list of RGB colors i need to use these RGB colors.
You can do it like this:
mImageView.setBackgroundTintList(getResources().getColorStateList(R.color.my_color));
Or you can do better, to support versions pre LOLLIPOP:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
ColorStateList stateList = ColorStateList.valueOf(getResources().getColor(R.color.my_color));
mImageView.setBackgroundTintList(stateList);
}
else
{
mImageView.getBackground().getCurrent().setColorFilter(
new PorterDuffColorFilter(getResources().getColor(R.color.my_color),
PorterDuff.Mode.MULTIPLY));
}
More about PorterDuffColorFilter here.
i just found the answer, it works as follow:
In my changeColors(int id) function
Create ImageButton variable.
Assign the passed id to the ImageButton variable.
Define GradientDrawable variable to store the ImageButton background.
Update the color of the background using GradientDrawable variable.
Update the ImageButton to the new background.
This is the code:
ImageButton circle;
circle = (ImageButton) findViewById(id);
GradientDrawable drawable = (GradientDrawable) getResources().getDrawable(R.drawable.circle);
drawable.setColor(Color.parseColor("color in format #FFFFFF");
circle.setBackground(drawable);
You may use ImageView instead and use app:tint to set the tint color for the background drawable.
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="#drawable/circle"
app:tint="#android:color/holo_red_dark"/>
I have a RelativeLayout:
<RelativeLayout
android:id="#+id/my_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/my_shape" >
The my_shape.xml is this:
<stroke
android:width="1dp"
android:color="#FFF"/>
<solid android:color="#000"/>
So, from my activity, i would change the color of this RelativeLayout.
I've tried with
RelativeLayout tvCard = (RelativeLayout) findViewById(R.id.my_id);
tvCard.setBackgroundColor(color);
N.B. color is an input color!
By this solution, the RelativeLayout is completely draw of red! How can i do to change color of it without change the "stroke color"?
Thank you.
try to create ShapeDrawable in java code.
like this:
ShapeDrawable sd1 = new ShapeDrawable(new RectShape());
sd1.getPaint().setColor(0xFFFFFFFF);
sd1.getPaint().setStyle(Style.STROKE);
sd1.getPaint().setStrokeWidth(1);
// sd1.getPaint().somehow_set_stroke_color?
ShapeDrawable sd2 = new ShapeDrawable(new RectShape());
sd2.getPaint().setColor(0xFF000000);
sd2.getPaint().setStyle(Style.STROKE);
// sd2.getPaint().somehow_set_stroke_color?
// sd2.getPaint().somehow_set_gradient_params?
layers[0] = sd1;
layers[1] = sd2;
LayerDrawable composite = new LayerDrawable(layers);
tvCard.setBackgroundDrawable(composite);
Could anyone tell me how they have got RotateDrawable to work whether it be from code or XML or both? The documentation on animating Drawables is pretty poor and animation only seems to work for images. I want to be able to animate all drawables. When i tried to get a RotateDrawble from XML is just causes an exception. What is the correct function to find a RotateDrawable from XML?
here's a nice solution for putting a rotated drawable for an imageView:
Drawable getRotateDrawable(final Bitmap b, final float angle) {
final BitmapDrawable drawable = new BitmapDrawable(getResources(), b) {
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle, b.getWidth() / 2, b.getHeight() / 2);
super.draw(canvas);
canvas.restore();
}
};
return drawable;
}
usage:
Bitmap b=...
float angle=...
final Drawable rotatedDrawable = getRotateDrawable(b,angle);
root.setImageDrawable(rotatedDrawable);
another alternative:
private Drawable getRotateDrawable(final Drawable d, final float angle) {
final Drawable[] arD = { d };
return new LayerDrawable(arD) {
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle, d.getBounds().width() / 2, d.getBounds().height() / 2);
super.draw(canvas);
canvas.restore();
}
};
}
also, if you wish to rotate the bitmap, but afraid of OOM, you can use an NDK solution i've made here
You have to animate the "level" property, where 0 is the start value and 10000 is the end value.
The below example animates from start to finish, you can reverse the animation easily with this method.
final RotateDrawable rotateDrawable = ...
ObjectAnimator.ofInt(rotateDrawable, "level", 0, 10000).start();
I would like to add a full example of animating a progress icon on ImageView, it is based on Mark Hetherington answer.
So my animation looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="-360"
android:duration="100"
android:drawable="#drawable/ic_loop_black_24dp"
/>
icon comes from https://material.io/icons/
then my layout contains an ImageView as follows:
<ImageView
android:id="#+id/progress"
android:layout_marginTop="0dp"
android:layout_marginLeft="-3dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:visibility="gone"
android:scaleType="fitCenter"
android:background="#drawable/progress_anim"
android:layout_gravity="center_horizontal|center_vertical"
/>
and finally in code when I need to show animation I do:
RotateDrawable rotateDrawable = ((RotateDrawable)progressImage.getBackground());
ObjectAnimator anim = ObjectAnimator.ofInt(rotateDrawable, "level", 0, 10000);
anim.setDuration(1000);
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.start();
RotateDrawable does not seem to be animated. Instead, you have to use setLevel to change the rotation of the drawable.
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/your_drawable"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
And set the level will rotate the drawable:
final ImageView image = (ImageView)findViewById(R.id.imageView1);
final RotateDrawable drawable = (RotateDrawable)image.getDrawable();
drawable.setLevel(500);
The following code returns a Drawable wrapper that rotates another Drawable programmatically:
Drawable rotateDrawable(Drawable d, final float angle) {
// Use LayerDrawable, because it's simpler than RotateDrawable.
Drawable[] arD = {
d
};
return new LayerDrawable(arD) {
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.rotate(angle);
super.draw(canvas);
canvas.restore();
}
};
}
I haven't worked with a RotateDrawable, but if you're simply trying to animate rotation on a graphic, you don't need it. Drawables with a 'level' like RotateDrawable are meant to convey information rather than animate views.
The following code rotates an ImageView around its center:
ImageView myImageView = (ImageView)findViewById(R.id.my_imageview);
AnimationSet animSet = new AnimationSet(true);
animSet.setInterpolator(new DecelerateInterpolator());
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
final RotateAnimation animRotate = new RotateAnimation(0.0f, -90.0f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animRotate.setDuration(1500);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);
myImageView.startAnimation(animSet);
Since you're trying to use Almero's Android Gesture Detectors, I decided to do the same in order to find an appropriate solution:
public class MainActivity extends Activity {
private RotateGestureDetector mRotateDetector;
private float mRotationDegrees = 0.f;
private static final float ROTATION_RATIO = 2;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mRotateDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
#Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegrees -= detector.getRotationDegreesDelta();
ImageView v = (ImageView) findViewById(R.id.imageView);
// For NineOldAndroids library only!
ViewHelper.setRotation(v, mRotationDegrees * ROTATION_RATIO);
// For HONEYCOMB and later only!
v.setRotation(mRotationDegrees * ROTATION_RATIO);
return true;
}
}
}
It works fine for me (I'm able to rotate the ImageView with two-finger rotation gesture.
NOTE: Don't forget to chose appropriate rotation method call. I commented both of them to draw your attention.
ROTATION_RATIO is just a multiplier to speed-up a rotation response on my fingers movement.
You can use any rotation axis (setRotation(), setRotationX() and setRotationY()) methods for a View.
To enable support of this code on Android devices with API Level lower than 11 (pre-Honeycomb devices) you may want to engage NineOldAndroid library.
You could manually call RotatedDrawable.setLevel() to rotate the drawable, or you could read the code of ProgressBar, the indeterminate drawable is a LayerDrawable whose children were RotatedDrawable, like this one:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<rotate
android:drawable="#drawable/spinner_48_outer_holo"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="1080" />
</item>
<item>
<rotate
android:drawable="#drawable/spinner_48_inner_holo"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="720"
android:toDegrees="0" />
</item>
</layer-list>
The rotate animation was driven by ProgressBar's onDraw method.
This is a good working example. Param duration is used to animate it.
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720" >
<shape
android:innerRadius="20dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false" >
<size
android:height="48dp"
android:width="48dp" />
<gradient
android:centerY="0.5"
android:endColor="#android:color/white"
android:startColor="#00ffffff"
android:type="sweep"
android:useLevel="false" />
</shape>
</rotate>
If you want to rotate drawable forever you can use animated-rotate tag in drawable xml like this.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<animated-rotate android:drawable="#drawable/ic_refresh" android:pivotX="50%" android:pivotY="50%" />
</item>
</selector>