I have a linear layout view whose background I have set to oval shape solid color. So far I have the background as circle. Now I want to achieve same i.e using shape drawable to get a circle with 2 colors. Please see attached.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:centerX="-1"
android:type="sweep"
android:startColor="color1"
android:endColor="color2"
/>
</shape>
create shape.xml in your drawable folder
shape.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<gradient android:startColor="#0000FF" android:endColor="#00FF00"
android:angle="270"/>
</shape>
This might come late, but I had trouble finding good answer so hear my take.
I used a custom drawable to draw the circle and with it a LinearGradient shader which is configured by positions array to have no gradient transition. The gradient line direction is configured in LinearGradient constructor (here it is diagonal).
public class MultiColorCircleDrawable extends Drawable {
private Paint paint;
private int[] colors;
public MultiColorOvalDrawable(int[] colors) {
this.colors = colors;
}
private void init() {
paint = new Paint();
paint.setShader(createShader());
}
private Shader createShader() {
int[] colorsArray = new int[colors.length * 2];
float[] positions = new float[colors.length * 2];
for (int i = 0; i < colors.length; i++) {
int y = i * 2;
int color = colors[i];
colorsArray[y] = color;
colorsArray[y+1] = color;
positions[y] = 1f / colors.length * i;
positions[y+1] = 1f / colors.length * (i+1);
}
Rect bounds = getBounds();
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
return new LinearGradient(0, 0, width, height, colorsArray, positions, Shader.TileMode.REPEAT);
}
#Override
public void draw(Canvas canvas) {
if (null == paint) {
init();
}
Rect bounds = getBounds();
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
canvas.drawCircle(width/2, height/2, (width/2) - strokeWidth, maskPaint);
}
#Override
public void setAlpha(int i) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
Related
I have a number of icons I have to display in my app, and everytime they show up they do within a colored circle; Now I only get the icon itself so I have to create the circle myself. I would like to create a custom view that does this but I really have no clue on how to implement it!
This will be helpful https://github.com/hdodenhof/CircleImageView
To set background color use this line-
app:civ_circle_background_color="#color/colorPrimary"
User vector drawable : https://developer.android.com/reference/android/graphics/drawable/VectorDrawable
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="64"
android:viewportWidth="64">
<path
android:fillColor="#ff00ff"
android:pathData="M22,32
A10,10 0 1,1 42,32
A10,10 0 1,1 22,32 Z" />
</vector>
public class ShapeView extends View {
int shape;
int color;
Paint paint;
public static final int CIRCLE = 0;
public static final int SQUARE = 1;
public ShapeView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ShapeView,
0, 0
);
try {
shape = a.getInteger(R.styleable.ShapeView_shape, 0);
color = a.getColor(R.styleable.ShapeView_color, ContextCompat.getColor(context, R.color.white));
} finally {
a.recycle();
}
paint = new Paint();
paint.setColor(color);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (shape) {
case CIRCLE:
drawCircle(canvas);
break;
case SQUARE:
drawSquare(canvas);
break;
}
}
private void drawCircle(Canvas canvas) {
int x = getWidth() / 2;
int y = getHeight() / 2;
canvas.drawCircle(x, y, x, paint);
}
private void drawSquare(Canvas canvas) {
int x = getWidth() / 2;
int y = getHeight() / 2;
int right = 2 * x;
int bottom = 2 * y;
canvas.drawRect(0, 0, right, bottom, paint);
}
public void changeShape(int shape) {
this.shape = shape;
invalidate();
requestLayout();
}
public void changeColor(int color) {
this.color = color;
invalidate();
}
}
create an attr.xml file in resource folder
<declare-styleable name="ShapeView">
<attr name="shape" format="enum">
<enum name="circle" value="0" />
<enum name="square" value="1" />
</attr>
<attr name="color" format="color" />
</declare-styleable>
The use it in xml as below
<com.app.custom.ShapeView
android:id="#+id/v_recording_state"
android:layout_width="#dimen/_15sdp"
android:layout_height="#dimen/_15sdp"
android:layout_gravity="center"
app:color="#color/white"
app:shape="circle" />
I need to add to the actionbar Home Button (Activity icon) a badge count.
i have a drawerLayout that opens when i press on the Home Button
i need to add a badge if a certain action took place.
i searched on how to add badge count to button but couldn't find what i want.(i know how to do this for an icon in menu.xml).
any help would be appreciated.
what i tried:
BitmapDrawable d1 = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_launcher);
Drawable drawableArray[]= new Drawable[]{d1};
LayerDrawable layerDraw = new LayerDrawable(drawableArray);
NewMessageIcon.setBadgeCount(this,layerDraw,getActionBar(),3);
setbadgecountfunction:
public static void setBadgeCount(Context context, LayerDrawable icon, ActionBar action,int countunread) {
BadgeDrawable badge=null;
Drawable reuse;
reuse = icon.findDrawableByLayerId(R.id.ic_badge_fornewmessage);
if (reuse != null && reuse instanceof BadgeDrawable) {
reuse.invalidateSelf();
badge = (BadgeDrawable) reuse;
}
else {
badge = new BadgeDrawable(context);
}
badge.setCount(countunread);
if (countunread>0)
{
badge.mBadgePaint.setColor(Color.parseColor("#F00000"));
}
else
{
badge.mBadgePaint.setColor(Color.parseColor("#ffae19"));
}
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_badge_fornewmessage, badge);
action.setIcon(icon); /////SHOULD CHANGE BUT NOT CHANGING!
}
badge class (I USED THIS CLASS FOR AN ICON IN MENU.XML AND ITS WORKING FINE)
public class BadgeDrawable extends Drawable {
private float mTextSize;
public Paint mBadgePaint;
private Paint mTextPaint;
private Rect mTxtRect = new Rect();
private String mCount = "";
private boolean mWillDraw = false;
public BadgeDrawable(Context context) {
mTextSize = context.getResources().getDimension(R.dimen.badge_text_size); //GET THE PREDIFINED TEXT SIZE
//FOR THE CIRCLE
mBadgePaint = new Paint();
mBadgePaint.setColor(Color.parseColor("#ffae19"));
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
//FOR THE NUMBER WITHIN THE CIRCLE
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
#Override
public void draw(Canvas canvas) {
if (!mWillDraw) {
return;
}
CREATING THE CIRCLE SHAPE
Rect bounds = getBounds();
//float width = bounds.right - bounds.left;
//float height = bounds.bottom - bounds.top;
float width = (bounds.right - bounds.left) + 20;
float height = (bounds.bottom - bounds.top) + 20;
// Position the badge in the top-right quadrant of the icon.
float radius = ((Math.min(width, height) / 2) - 1) / 2;
float centerX = width - radius - 1;
float centerY = radius + 1;
// Draw badge circle.
canvas.drawCircle(centerX, centerY, radius, mBadgePaint);
// Draw badge count text inside the circle.
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
float textHeight = mTxtRect.bottom - mTxtRect.top;
float textY = centerY + (textHeight / 2f);
canvas.drawText(mCount, centerX, textY, mTextPaint);
}
/*
Sets the count (i.e notifications) to display.
*/
public void setCount(int count) {
mCount = Integer.toString(count);
// Only draw a badge if there are notifications.
mWillDraw = count > 0;
invalidateSelf();
}
#Override
public void setAlpha(int alpha) {
// do nothing
}
#Override
public void setColorFilter(ColorFilter cf) {
// do nothing
}
#Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}
ic_menu_newmessage.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/ic_newmessage_notification"
android:drawable="#drawable/ic_launcher"
android:gravity="center" />
<item
android:id="#+id/ic_badge_fornewmessage"
android:drawable="#drawable/ic_launcher" />
</layer-list>
I have created a circle with a stroke and white background using xml. How can this be filled gradually from bottom to top on user actions(e.g. on successive button press)?
Is there any free library which can be used to achieve similar thing?
I created a Custom View class that will do what you want. There are four custom attributes that can be set in your layout xml:
fillColor, color - Sets the color of the fill area. Default is Color.WHITE.
strokeColor, color - Sets the color of the bounding circle. Default is Color.BLACK.
strokeWidth, float - Sets the thickness of the bounding circle. Default is 1.0.
value, integer: 0-100 - Sets the value for the fill area. Default is 0.
Please note that these attributes must have the custom prefix in lieu of the android prefix in your layout xml. The root View should also contain the custom xml namespace. (See the example below.) The other standard View attributes - such as layout_width, background, etc. - are available.
First, the CircleFillView class:
public class CircleFillView extends View
{
public static final int MIN_VALUE = 0;
public static final int MAX_VALUE = 100;
private PointF center = new PointF();
private RectF circleRect = new RectF();
private Path segment = new Path();
private Paint strokePaint = new Paint();
private Paint fillPaint = new Paint();
private int radius;
private int fillColor;
private int strokeColor;
private float strokeWidth;
private int value;
public CircleFillView(Context context)
{
this(context, null);
}
public CircleFillView(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.CircleFillView,
0, 0);
try
{
fillColor = a.getColor(R.styleable.CircleFillView_fillColor, Color.WHITE);
strokeColor = a.getColor(R.styleable.CircleFillView_strokeColor, Color.BLACK);
strokeWidth = a.getFloat(R.styleable.CircleFillView_strokeWidth, 1f);
value = a.getInteger(R.styleable.CircleFillView_value, 0);
adjustValue(value);
}
finally
{
a.recycle();
}
fillPaint.setColor(fillColor);
strokePaint.setColor(strokeColor);
strokePaint.setStrokeWidth(strokeWidth);
strokePaint.setStyle(Paint.Style.STROKE);
}
public void setFillColor(int fillColor)
{
this.fillColor = fillColor;
fillPaint.setColor(fillColor);
invalidate();
}
public int getFillColor()
{
return fillColor;
}
public void setStrokeColor(int strokeColor)
{
this.strokeColor = strokeColor;
strokePaint.setColor(strokeColor);
invalidate();
}
public int getStrokeColor()
{
return strokeColor;
}
public void setStrokeWidth(float strokeWidth)
{
this.strokeWidth = strokeWidth;
strokePaint.setStrokeWidth(strokeWidth);
invalidate();
}
public float getStrokeWidth()
{
return strokeWidth;
}
public void setValue(int value)
{
adjustValue(value);
setPaths();
invalidate();
}
public int getValue()
{
return value;
}
private void adjustValue(int value)
{
this.value = Math.min(MAX_VALUE, Math.max(MIN_VALUE, value));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
center.x = getWidth() / 2;
center.y = getHeight() / 2;
radius = Math.min(getWidth(), getHeight()) / 2 - (int) strokeWidth;
circleRect.set(center.x - radius, center.y - radius, center.x + radius, center.y + radius);
setPaths();
}
private void setPaths()
{
float y = center.y + radius - (2 * radius * value / 100 - 1);
float x = center.x - (float) Math.sqrt(Math.pow(radius, 2) - Math.pow(y - center.y, 2));
float angle = (float) Math.toDegrees(Math.atan((center.y - y) / (x - center.x)));
float startAngle = 180 - angle;
float sweepAngle = 2 * angle - 180;
segment.rewind();
segment.addArc(circleRect, startAngle, sweepAngle);
segment.close();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawPath(segment, fillPaint);
canvas.drawCircle(center.x, center.y, radius, strokePaint);
}
}
Now, for the custom xml attributes to work, you will need to put the following file in the /res/values folder of your project.
attrs.xml:
<resources>
<declare-styleable name="CircleFillView" >
<attr name="fillColor" format="color" />
<attr name="strokeColor" format="color" />
<attr name="strokeWidth" format="float" />
<attr name="value" format="integer" />
</declare-styleable>
</resources>
Following are the files for a simple demonstration app, where the CircleFillView's value is controlled with a SeekBar.
The layout file for our Activity, main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.circlefill"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<com.example.circlefill.CircleFillView
android:id="#+id/circleFillView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ffffff"
custom:fillColor="#6bcae2"
custom:strokeColor="#75b0d0"
custom:strokeWidth="20"
custom:value="65" />
<SeekBar android:id="#+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
And, the MainActivity class:
public class MainActivity extends Activity
{
CircleFillView circleFill;
SeekBar seekBar;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
circleFill = (CircleFillView) findViewById(R.id.circleFillView);
seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setProgress(circleFill.getValue());
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener()
{
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (fromUser)
circleFill.setValue(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
}
);
}
}
And a screenshot of the demo app:
I want to programatically render the edge fading of a TextView. By which mode I can create a true alpha gradient?
A real edge fading should smoothly reduce the transparency (no overlay gradient hack) from 1.0 to 0.0 so that the top view fade into background view.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int y = 0;
int height = 300;
int width = getWidth();
LinearGradient shader = new LinearGradient(width / 2, y, width / 2, y + height, 0xff000000, 0x0000000, Shader.TileMode.CLAMP);
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XXX)); // Which mode works?
p.setShader(shader);
canvas.drawRect(0, y, width, y + height, p);
}
I tried all the combinations and none of them worked? Is it impossible to do so?
Here is my solution with an ImageView.
I use a GradientDrawable for the alpha.
Activity:
#Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawableResource(R.drawable.background);
GradientImageView giv = new GradientImageView(this);
giv.setImageResource(R.drawable.bttf);
giv.setScaleType(ImageView.ScaleType.CENTER_CROP);
setContentView(giv);
}
class GradientImageView extends ImageView
{
Drawable mDrawableMask;
Paint mPaintMask;
Bitmap mOriginal, mBitmapMask;
Canvas mCanvasOriginal, mCanvasMask;
public GradientImageView (final Context context)
{
super(context);
if (android.os.Build.VERSION.SDK_INT >= 11)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mDrawableMask = getResources().getDrawable(R.drawable.gradient);
mPaintMask = new Paint();
mPaintMask.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
protected void setMask (Drawable mask)
{
mDrawableMask = mask;
mBitmapMask = null;
}
#Override
protected void onDraw (final Canvas canvas)
{
if (mOriginal == null)
{
mOriginal = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mCanvasOriginal = new Canvas(mOriginal);
}
super.onDraw(mCanvasOriginal);
if (mBitmapMask == null)
{
mBitmapMask = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mCanvasMask = new Canvas(mBitmapMask);
mDrawableMask.setBounds(0, 0, getWidth(), getHeight());
mDrawableMask.draw(mCanvasMask);
}
canvas.drawBitmap(mOriginal, 0, 0, null);
canvas.drawBitmap(mBitmapMask, 0, 0, mPaintMask);
}
}
gradient.xml
<?xml version="1.0" encoding="utf-8"?>
<gradient
android:angle="-90"
android:centerColor="#android:color/transparent"
android:centerY="0.5"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
values.xml
<drawable name="background">#FF0000</drawable>
The result (activity background is Red)
This question already has answers here:
Add new item count to icon on button - Android
(5 answers)
Closed 7 years ago.
I would like to make a notification icon in the action bar to inside the number of notifications.
For example (Google Adsence) :
I found this answer on stackoverflow, but it does not fully answer my question because in this case it is only the number and not an icon with a number: Actionbar notification icon with count
After a lot of trying of nearly all resources on SO I turned to blogs; successfully. I want to share what worked for me (Api >= 13).
Let's start with the way it's used in code:
public boolean onCreateOptionsMenu(Menu menu) {
//inflate menu
getMenuInflater().inflate(R.menu.menu_my, menu);
// Get the notifications MenuItem and LayerDrawable (layer-list)
MenuItem item = menu.findItem(R.id.action_notifications);
LayerDrawable icon = (LayerDrawable) item.getIcon();
// Update LayerDrawable's BadgeDrawable
Utils2.setBadgeCount(this, icon, 2);
return true;
}
The menu_my.xml:
<menu 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"
tools:context=".MainActivity">
<item
android:id="#+id/action_notifications"
android:icon="#drawable/ic_menu_notifications"
android:title="Notifications"
app:showAsAction="always" />
</menu>
This class that conveniently makes a BadgeDrawable:
public class BadgeDrawable extends Drawable {
private float mTextSize;
private Paint mBadgePaint;
private Paint mTextPaint;
private Rect mTxtRect = new Rect();
private String mCount = "";
private boolean mWillDraw = false;
public BadgeDrawable(Context context) {
//mTextSize = context.getResources().getDimension(R.dimen.badge_text_size);
mTextSize = 12F;
mBadgePaint = new Paint();
mBadgePaint.setColor(Color.RED);
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
#Override
public void draw(Canvas canvas) {
if (!mWillDraw) {
return;
}
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
// Position the badge in the top-right quadrant of the icon.
float radius = ((Math.min(width, height) / 2) - 1) / 2;
float centerX = width - radius - 1;
float centerY = radius + 1;
// Draw badge circle.
canvas.drawCircle(centerX, centerY, radius, mBadgePaint);
// Draw badge count text inside the circle.
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
float textHeight = mTxtRect.bottom - mTxtRect.top;
float textY = centerY + (textHeight / 2f);
canvas.drawText(mCount, centerX, textY, mTextPaint);
}
/*
Sets the count (i.e notifications) to display.
*/
public void setCount(int count) {
mCount = Integer.toString(count);
// Only draw a badge if there are notifications.
mWillDraw = count > 0;
invalidateSelf();
}
#Override
public void setAlpha(int alpha) {
// do nothing
}
#Override
public void setColorFilter(ColorFilter cf) {
// do nothing
}
#Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}
This class that helps to set the number.
public class Utils2 {
public static void setBadgeCount(Context context, LayerDrawable icon, int count) {
BadgeDrawable badge;
// Reuse drawable if possible
Drawable reuse = icon.findDrawableByLayerId(R.id.ic_badge);
if (reuse != null && reuse instanceof BadgeDrawable) {
badge = (BadgeDrawable) reuse;
} else {
badge = new BadgeDrawable(context);
}
badge.setCount(count);
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_badge, badge);
}
}
And mui importante a drawable (like a layout) in res/drawable:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/ic_notification"
android:drawable="#drawable/ice_skate"
android:gravity="center" />
<!-- set a place holder Drawable so android:drawable isn't null -->
<item
android:id="#+id/ic_badge"
android:drawable="#drawable/ice_skate" />
</layer-list>
Good luck!