How to add text to floating action button? - android

I want to display text on a floating action button instead of a image.I tried using the android:contentDescription="string" but action button appears to be empty do i need to set some color or do anything else.Please let me know.

In our project we are using a custom class called TextDrawable. This class extends Drawable and in its method draw(Canvas) it simple draws the text on canvas. The class is suitable for our specific needs but I think, the idea (mainly in draw() method will help you:
public class TextDrawable extends Drawable {
protected final Paint textPaint;
protected ColorStateList color;
protected String text;
protected int iHeight;
protected int iWidth;
protected int measuredWidth, measuredHeight;
private float ascent;
/**
* A flag whether the drawable is stateful - whether to redraw if the state of view has changed
*/
protected boolean stateful;
/**
* Vertical alignment of text
*/
private VerticalAlignment verticalAlignment;
.... some constructors...
public TextDrawable(Context ctx, String text, ColorStateList color, float textSize, VerticalAlignment verticalAlignment) {
textPaint = new Paint();
this.text = text;
initPaint();
this.textPaint.setTextSize(textSize);
measureSize();
setBounds(0, 0, iWidth, iHeight);
this.color = color;
textPaint.setColor(color.getDefaultColor());
this.verticalAlignment = verticalAlignment;
}
/**
* Set bounds of drawable to start on coordinate [0,0] and end on coordinate[measuredWidth,
* measuredHeight]
*/
public final void setBoundsByMeasuredSize() {
setBounds(0, 0, measuredWidth, measuredHeight);
invalidateSelf();
}
#Override
public boolean isStateful() {
return stateful;
}
public void setStateful(boolean stateful) {
this.stateful = stateful;
}
private void initPaint() {
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
}
/**
* Vertical alignment of text within the drawable (Horizontally it is always aligned to center
*/
public VerticalAlignment getVerticalAlignment() {
return verticalAlignment;
}
/**
* Vertical alignment of text within the drawable (Horizontally it is always aligned to center
*/
public void setVerticalAlignment(VerticalAlignment verticalAlignment) {
if (this.verticalAlignment != verticalAlignment) {
this.verticalAlignment = verticalAlignment;
invalidateSelf();
}
}
/**
* Displayed text
*/
public String getText() {
return text;
}
/**
* Displayed text
*/
public void setText(String text) {
if (this.text == null || !this.text.equals(text)) {
this.text = text;
invalidateSelf();
}
}
/**
* The color of text
*/
public ColorStateList getColor() {
return color;
}
/**
* The color of text
*/
public void setColor(ColorStateList colorStateList) {
if (this.color == null || !this.color.equals(colorStateList)) {
this.color = colorStateList;
invalidateSelf();
}
}
/**
* The color of text
*/
public void setColor(int color) {
setColor(ColorStateList.valueOf(color));
}
/**
* Text size
*/
public void setTextSize(float size) {
if (this.textPaint.getTextSize() != size) {
this.textPaint.setTextSize(size);
measureSize();
invalidateSelf();
}
}
/**
* Text size
*/
public void setTextSize(int unit, float size, Context context) {
setTextSize(TypedValue.applyDimension(unit, size, context.getResources().getDisplayMetrics()));
}
/**
* This method is called by default when any property that may have some influence on the size
* of drawable This method should use measuredWidth and measuredHeight properties to store the
* measured walues By default the measuredWIdth and measuredHeight are set to iWidth and iHeight
* (size of text) by this method.
*/
protected void measureSize() {
ascent = -textPaint.ascent();
iWidth = (int) (0.5f + textPaint.measureText(text));
iHeight = (int) (0.5f + textPaint.descent() + ascent);
measuredWidth = iWidth;
measuredHeight = iHeight;
}
public float getTextSize() {
return textPaint.getTextSize();
}
#Override
protected boolean onStateChange(int[] state) {
int clr = color != null ? color.getColorForState(state, 0) : 0;
if (textPaint.getColor() != clr) {
textPaint.setColor(clr);
return true;
} else {
return false;
}
}
public Typeface getTypeface() {
return textPaint.getTypeface();
}
public void setTypeface(Typeface typeface) {
if (!textPaint.getTypeface().equals(typeface)) {
textPaint.setTypeface(typeface);
invalidateSelf();
}
}
/**
* The method is called before the text is drawn. This method can be overridden to draw some background (by default this method does nothing).
* #param canvas The canvas where to draw.
* #param bounds The bounds of the drawable.
*/
protected void drawBefore(Canvas canvas, Rect bounds) {
}
/**
* The method is called after the text is drawn. This method can be overriden to draw some more graphics over the text (by default this method does nothing).
* #param canvas The canvas where to draw.
* #param bounds The bound of the drawable.
*/
protected void drawAfter(Canvas canvas, Rect bounds) {
}
#Override
public void draw(Canvas canvas) {
if (text == null || text.isEmpty()) {
return;
}
final Rect bounds = getBounds();
int stack = canvas.save();
canvas.translate(bounds.left, bounds.top);
drawBefore(canvas, bounds);
if (text != null && !text.isEmpty()) {
final float x = bounds.width() >= iWidth ? bounds.centerX() : iWidth * 0.5f;
float y = 0;
switch (verticalAlignment) {
case BASELINE:
y = (bounds.height() - iHeight) * 0.5f + ascent;
break;
case TOP:
y = bounds.height();
break;
case BOTTOM:
y = bounds.height();
break;
}
canvas.drawText(text, x, y, textPaint);
}
drawAfter(canvas, bounds);
canvas.restoreToCount(stack);
}
#Override
public void setAlpha(int alpha) {
if (textPaint.getAlpha() != alpha) {
textPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
if (textPaint.getColorFilter() == null || !textPaint.getColorFilter().equals(cf)) {
textPaint.setColorFilter(cf);
invalidateSelf();
}
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public enum VerticalAlignment {
TOP, BOTTOM, BASELINE
}
and how to use it:
fab.setImageDrawable(new TextDrawable(fab.getContext(), "FAB", ColorStateList.valueOf(Color.BLACK), 32.f, VerticalAlignment.BASELINE));
(fab is FloatingActionButton)

Related

when i use BackgroundImageSpan in android,but at the end of a line,it does not show

when i use BackgroundImageSpan in android,but at the end of a line,it does not show,Just like the red underline in the picture,Normally the background image should be displayed:
public class BackgroundImageSpan extends ReplacementSpan {
private static final String TAG = "BackgroundImageSpan";
private Drawable mDrawable;
private int mImageId;
private int mTextColor;
private int mWidth = -1;
/**
* new BackgroundImageSpan use resource id and Drawable
*
* #param id the drawable resource id
* #param drawable Drawable related to the id
* #internal
* #hide
*/
public BackgroundImageSpan(int id, Drawable drawable, int textColor) {
mImageId = id;
mDrawable = drawable;
mTextColor = textColor;
}
public BackgroundImageSpan(int id, Drawable drawable) {
mImageId = id;
mDrawable = drawable;
}
/**
* #hide
* #internal
*/
public BackgroundImageSpan(Parcel src) {
mImageId = src.readInt();
}
/**
* #hide
* #internal
*/
public void draw(Canvas canvas, int width, float x, int top, int y, int bottom, Paint paint) {
if (mDrawable == null) {//if no backgroundImage just don't do any draw
Log.e(TAG, "mDrawable is null draw()");
return;
}
Drawable drawable = mDrawable;
canvas.save();
canvas.translate(x, top); // translate to the left top point
mDrawable.setBounds(0, -4, width, bottom - top - 12);
drawable.draw(canvas);
canvas.restore();
}
#Override
public void updateDrawState(TextPaint tp) {
}
/**
* return a special type identifier for this span class
*
* #hide
* #internal
* #Override
*/
public int getSpanTypeId() {
return 0;
}
/**
* describe the kinds of special objects contained in this Parcelable's marshalled representation
*
* #hide
* #internal
* #Override
*/
public int describeContents() {
return 0;
}
/**
* flatten this object in to a Parcel
*
* #hide
* #internal
* #Override
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mImageId);
}
/**
* #hide
* #internal
*/
public void convertToDrawable(Context context) {
if (mDrawable == null) {
mDrawable = context.getResources().getDrawable(mImageId);
}
}
/**
* convert a style text that contain BackgroundImageSpan, Parcek only pass resource id,
* after Parcel, we need to convert resource id to Drawable.
*
* #hide
* #internal
*/
public static void convert(CharSequence text, Context context) {
if (!(text instanceof SpannableStringBuilder)) {
return;
}
SpannableStringBuilder builder = (SpannableStringBuilder) text;
BackgroundImageSpan[] spans = builder.getSpans(0, text.length(), BackgroundImageSpan.class);
if (spans == null || spans.length == 0) {
return;
}
for (int i = 0; i < spans.length; i++) {
spans[i].convertToDrawable(context);
}
}
/**
* draw the span
*
* #hide
* #internal
* #Override
*/
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
// draw image
draw(canvas, mWidth, x, top, y, bottom, paint);
// draw text
// the paint is already updated
if (mTextColor != 0) {
paint.setColor(mTextColor);
}
canvas.drawText(text, start, end, x, y, paint);
}
/**
* get size of the span
*
* #hide
* #internal
* #Override
*/
public int getSize(Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fm) {
float size = paint.measureText(text, start, end);
if (fm != null && paint != null) {
paint.getFontMetricsInt(fm);
}
mWidth = (int) size;
return mWidth;
}
}

Android Custom View Edge Clipping with ripple animation

I'm using a custom view to get ripple effect for the pre-lollipop devices. But I also need to customize the container shape like a curved shape.I want to be the button like this.
As you can see in the second and third buttons when we tap the view the ripple effect animation goes outside of the container view. So how to resolve this?
Please note that I want this ripple effect for the Kitkat version with the ability to change the ripple color. So is this possible?
Here is my custom view which is used for the ripple effect
public class MyRippleView extends FrameLayout {
private int WIDTH;
private int HEIGHT;
private int frameRate = 10;
private int rippleDuration = 400;
private int rippleAlpha = 90;
private Handler canvasHandler;
private float radiusMax = 0;
private boolean animationRunning = false;
private int timer = 0;
private int timerEmpty = 0;
private int durationEmpty = -1;
private float x = -1;
private float y = -1;
private int zoomDuration;
private float zoomScale;
private ScaleAnimation scaleAnimation;
private Boolean hasToZoom;
private Boolean isCentered;
private Integer rippleType;
private Paint paint;
private Bitmap originBitmap;
private int rippleColor;
private int ripplePadding;
private GestureDetector gestureDetector;
private final Runnable runnable = new Runnable() {
#Override
public void run() {
invalidate();
}
};
private OnRippleCompleteListener onCompletionListener;
public MyRippleView(Context context) {
super(context);
}
public MyRippleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyRippleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* Method that initializes all fields and sets listeners
*
* #param context Context used to create this view
* #param attrs Attribute used to initialize fields
*/
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));
rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration);
frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate);
rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha);
ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
canvasHandler = new Handler();
zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
typedArray.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(rippleColor);
paint.setAlpha(rippleAlpha);
this.setWillNotDraw(false);
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event);
sendClickEvent(true);
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true);
this.setClickable(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null) onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);
paint.setColor(Color.parseColor("#ffff4444"));
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 0.6f)
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}
/**
* Launch Ripple animation for the current view with a MotionEvent
*
* #param event MotionEvent registered by the Ripple gesture listener
*/
public void animateRipple(MotionEvent event) {
createAnimation(event.getX(), event.getY());
}
/**
* Launch Ripple animation for the current view centered at x and y position
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
public void animateRipple(final float x, final float y) {
createAnimation(x, y);
}
/**
* Create Ripple animation centered at x, y
*
* #param x Horizontal position of the ripple center
* #param y Vertical position of the ripple center
*/
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 2;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() / 2;
this.y = getMeasuredHeight() / 2;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
animateRipple(event);
sendClickEvent(false);
}
return super.onTouchEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
this.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
/**
* Send a click event if parent view is a Listview instance
*
* #param isLongClick Is the event a long click ?
*/
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
private Bitmap getCircleBitmap(final int radius) {
final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(x, y, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originBitmap, rect, rect, paint);
return output;
}
/**
* Set Ripple color, default is #FFFFFF
*
* #param rippleColor New color resource
*/
#ColorRes
public void setRippleColor(int rippleColor) {
this.rippleColor = getResources().getColor(rippleColor);
}
public int getRippleColor() {
return rippleColor;
}
public RippleType getRippleType()
{
return RippleType.values()[rippleType];
}
/**
* Set Ripple type, default is RippleType.SIMPLE
*
* #param rippleType New Ripple type for next animation
*/
public void setRippleType(final RippleType rippleType)
{
this.rippleType = rippleType.ordinal();
}
public Boolean isCentered()
{
return isCentered;
}
/**
* Set if ripple animation has to be centered in its parent view or not, default is False
*
* #param isCentered
*/
public void setCentered(final Boolean isCentered)
{
this.isCentered = isCentered;
}
public int getRipplePadding()
{
return ripplePadding;
}
/**
* Set Ripple padding if you want to avoid some graphic glitch
*
* #param ripplePadding New Ripple padding in pixel, default is 0px
*/
public void setRipplePadding(int ripplePadding)
{
this.ripplePadding = ripplePadding;
}
public Boolean isZooming()
{
return hasToZoom;
}
/**
* At the end of Ripple effect, the child views has to zoom
*
* #param hasToZoom Do the child views have to zoom ? default is False
*/
public void setZooming(Boolean hasToZoom)
{
this.hasToZoom = hasToZoom;
}
public float getZoomScale()
{
return zoomScale;
}
/**
* Scale of the end animation
*
* #param zoomScale Value of scale animation, default is 1.03f
*/
public void setZoomScale(float zoomScale)
{
this.zoomScale = zoomScale;
}
public int getZoomDuration()
{
return zoomDuration;
}
/**
* Duration of the ending animation in ms
*
* #param zoomDuration Duration, default is 200ms
*/
public void setZoomDuration(int zoomDuration)
{
this.zoomDuration = zoomDuration;
}
public int getRippleDuration()
{
return rippleDuration;
}
/**
* Duration of the Ripple animation in ms
*
* #param rippleDuration Duration, default is 400ms
*/
public void setRippleDuration(int rippleDuration)
{
this.rippleDuration = rippleDuration;
}
public int getFrameRate()
{
return frameRate;
}
/**
* Set framerate for Ripple animation
*
* #param frameRate New framerate value, default is 10
*/
public void setFrameRate(int frameRate)
{
this.frameRate = frameRate;
}
public int getRippleAlpha()
{
return rippleAlpha;
}
/**
* Set alpha for ripple effect color
*
* #param rippleAlpha Alpha value between 0 and 255, default is 90
*/
public void setRippleAlpha(int rippleAlpha)
{
this.rippleAlpha = rippleAlpha;
}
public void setOnRippleCompleteListener(OnRippleCompleteListener listener) {
this.onCompletionListener = listener;
}
/**
* Defines a callback called at the end of the Ripple effect
*/
public interface OnRippleCompleteListener {
void onComplete(MyRippleView rippleView);
}
public enum RippleType {
SIMPLE(0),
DOUBLE(1),
RECTANGLE(2);
int type;
RippleType(int type)
{
this.type = type;
}
}
}
In the layout XML file
<FrameLayout
android:background="#drawable/curved_button"
android:layout_width="match_parent"
android:layout_height="50dp">
<com.package.MyRippleView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rv_color="#color/colorAccent"
rv_centered="true">
</com.package.MyRippleView>
</FrameLayout>
Curved Shape
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item >
<shape android:shape="rectangle" >
<corners android:radius="40dip" />
<stroke android:width="1dp" android:color="#FF9A00" />
</shape>
</item>
It's possible. The easiest way is to use Carbon which does such things just like that. I was able to recreate your button using only xml and run it on Gingerbread.
<carbon.widget.Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rounded with ripple"
android:textColor="#color/carbon_amber_700"
app:carbon_cornerRadius="100dp"
app:carbon_backgroundTint="#color/carbon_white"
app:carbon_rippleColor="#40ff0000"
app:carbon_stroke="#color/carbon_amber_700"
app:carbon_strokeWidth="2dp" />
The downside is that Carbon is huge and you may not want to include it just for that one button.
If you wish to do that by yourself, you should use a path and a PorterDuff mode to clip your button to a rounded rect.
private float cornerRadius;
private Path cornersMask;
private static PorterDuffXfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private void initCorners() {
cornersMask = new Path();
cornersMask.addRoundRect(new RectF(0, 0, getWidth(), getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
cornersMask.setFillType(Path.FillType.INVERSE_WINDING);
}
#Override
public void draw(#NonNull Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.draw(canvas);
paint.setXfermode(pdMode);
canvas.drawPath(cornersMask, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
And you should probably use ViewOutlineProvider on Lollipop to use native stuff where possible.

TextDrawable sometimes not centering vertically when binding to a RecyclerView

Problem Solved:
TextPaint's setTextSize() should be called before TextPaint's descent() & ascend() as those two methods seem to depend on the text size for calculations.
#Override
public void draw(Canvas canvas) {
Rect r = getBounds();
float boundSize = Math.min(r.width(), r.height());
float fontSize = Math.min(mFontSize, boundSize);
mTextPaint.setTextSize(fontSize); // Should be before descent() & ascent()
int originX = r.width() / 2;
int originY = (int) ((r.height() / 2)
- ((mTextPaint.descent() + mTextPaint.ascent()) / 2)) ;
canvas.drawText(mText, originX, originY, mTextPaint);
}
Context: I created a TextDrawable to turn strings into Drawables so I can set them as icons. I want them to be centered both horizontally and vertically when I assign them to an ImageView.
Problem: When I bind them to a RecyclerView, sometimes they center vertically but sometimes they don't (as you can see above). This can alternate as the items are rebinded each time I scroll through the RecyclerView.
I suspect the cause might be inside TextDrawable's draw() but I'm not sure how to fix it.
TextDrawable:
public class TextDrawable extends Drawable {
private String mText;
private Paint mTextPaint = new Paint();
private float mFontSize = 48;
public TextDrawable(String text) {
mText = text;
mTextPaint.setColor(Color.WHITE);
mTextPaint.setAntiAlias(true);
mTextPaint.setFakeBoldText(false);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setStrokeWidth(0);
}
public void setFontSize(float fontSize) {
mFontSize = fontSize;
}
#Override
public void draw(Canvas canvas) {
Rect r = getBounds();
int originX = r.width() / 2;
int originY = (int) ((r.height() / 2)
- ((mTextPaint.descent() + mTextPaint.ascent()) / 2)) ;
float boundSize = Math.min(r.width(), r.height());
float fontSize = Math.min(mFontSize, boundSize);
mTextPaint.setTextSize(fontSize);
canvas.drawText(mText, originX, originY, mTextPaint);
}
#Override
public void setAlpha(int alpha) {
mTextPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
mTextPaint.setColorFilter(colorFilter);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Task's getIcon() Method:
public Drawable getIcon() {
String letter = mTitle.substring(0, 1);
return new TextDrawable(letter);
}
RecyclerView ViewHolder's bind() Method:
void bind(Task task) {
mCircleView.setImageDrawable(task.getIcon());
}

How to create Circle ProgressDrawable to use in fresco?

I'm new to Fresco library and trying to show circle Progressbar when loading image from uri into DraweeView. Now I'm using default progressbar as below:
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(getResources());
GenericDraweeHierarchy hierarchy = builder.setFadeDuration(fadeInTime).build();
hierarchy.setProgressBarImage(new ProgressBarDrawable());
thumbnailImageView.setHierarchy(hierarchy);
however, the ProgressBar is horizontal ProgressBar? Is there anyway to change it to circle?
After a lot of time to searching with Google, but I cant find any result. I decided to write my own CircleProgressDrawable. I just wanna share it for anyone who dont want to waste time as me.
public class CircleProgressDrawable extends ProgressBarDrawable {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mBackgroundColor = 0x80000000;
private int mColor = 0x800080FF;
private int mBarWidth = 20;
private int mLevel = 0;
private boolean mHideWhenZero = false;
private int radius = 50;
public CircleProgressDrawable() {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
}
public void setRadius(int radius) {
this.radius = radius;
}
/**
* Sets the progress bar color.
*/
public void setColor(int color) {
if (mColor != color) {
mColor = color;
invalidateSelf();
}
}
/**
* Gets the progress bar color.
*/
public int getColor() {
return mColor;
}
/**
* Sets the progress bar background color.
*/
public void setBackgroundColor(int backgroundColor) {
if (mBackgroundColor != backgroundColor) {
mBackgroundColor = backgroundColor;
invalidateSelf();
}
}
/**
* Gets the progress bar background color.
*/
public int getBackgroundColor() {
return mBackgroundColor;
}
/**
* Sets the progress bar width.
*/
public void setBarWidth(int barWidth) {
if (mBarWidth != barWidth) {
mBarWidth = barWidth;
invalidateSelf();
}
}
/**
* Gets the progress bar width.
*/
public int getBarWidth() {
return mBarWidth;
}
/**
* Sets whether the progress bar should be hidden when the progress is 0.
*/
public void setHideWhenZero(boolean hideWhenZero) {
mHideWhenZero = hideWhenZero;
}
/**
* Gets whether the progress bar should be hidden when the progress is 0.
*/
public boolean getHideWhenZero() {
return mHideWhenZero;
}
#Override
protected boolean onLevelChange(int level) {
mLevel = level;
invalidateSelf();
return true;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return DrawableUtils.getOpacityFromColor(mPaint.getColor());
}
#Override
public void draw(Canvas canvas) {
if (mHideWhenZero && mLevel == 0) {
return;
}
drawCircle(canvas, mBackgroundColor);
drawArc(canvas, mLevel, mColor);
}
private final int MAX_LEVEL = 10000;
private void drawArc(Canvas canvas, int level, int color) {
mPaint.setColor(color);
Rect bounds = getBounds();
// find center point
int xpos = bounds.left + bounds.width() / 2;
int ypos = bounds.bottom - bounds.height() / 2;
RectF rectF = new RectF(xpos - radius, ypos - radius, xpos + radius, ypos + radius);
float degree = (float) level / (float) MAX_LEVEL * 360;
canvas.drawArc(rectF, 270, degree, false, mPaint);
LogUtils.e("level: " + level + ", degree: " + degree);
}
private void drawCircle(Canvas canvas, int color) {
mPaint.setColor(color);
Rect bounds = getBounds();
int xpos = bounds.left + bounds.width() / 2;
int ypos = bounds.bottom - bounds.height() / 2;
canvas.drawCircle(xpos, ypos, radius, mPaint);
}
}

Android widget for user info page and circle shape user icon

How do I make a user info page like this:
For the grey background under the user icon, is it a widget? like simply user a image view or is there a better way to implement this?
And if I want the user icon to be a circle shape, other than customize a widget, is there a more convenient way to do it?
You can make use of circular ImageView for Android. It can be used with all kinds of drawables, i.e. a PicassoDrawable from Picasso or other non-standard drawables. Here is the link of library:
https://github.com/hdodenhof/CircleImageView
You can use this class as your widget and set image inside same as imageview with picasso or image loader
public class CircularImageView extends ImageView
{
// Border & Selector configuration variables
private boolean hasBorder;
private boolean hasSelector;
private boolean isSelected;
private int borderWidth;
private int canvasSize;
private int selectorStrokeWidth;
// Objects used for the actual drawing
private BitmapShader shader;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private Paint paintSelectorBorder;
private ColorFilter selectorFilter;
public CircularImageView(Context context)
{
this(context, null);
}
public CircularImageView(Context context, AttributeSet attrs)
{
this(context, attrs, R.attr.circularImageViewStyle);
}
public CircularImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
/**
* Initializes paint objects and sets desired attributes.
*
* #param context
* #param attrs
* #param defStyle
*/
private void init(Context context, AttributeSet attrs, int defStyle)
{
// Initialize paint objects
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
paintBorder.setAntiAlias(true);
paintSelectorBorder = new Paint();
paintSelectorBorder.setAntiAlias(true);
// load the styled attributes and set their properties
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyle, 0);
// Check if border and/or border is enabled
hasBorder = attributes.getBoolean(R.styleable.CircularImageView_border, false);
hasSelector = attributes.getBoolean(R.styleable.CircularImageView_selector, false);
// Set border properties if enabled
if(hasBorder) {
int defaultBorderSize = (int) (2 * context.getResources().getDisplayMetrics().density + 0.5f);
setBorderWidth(attributes.getDimensionPixelOffset(R.styleable.CircularImageView_border_width, defaultBorderSize));
setBorderColor(attributes.getColor(R.styleable.CircularImageView_border_color, Color.WHITE));
}
// Set selector properties if enabled
if(hasSelector) {
int defaultSelectorSize = (int) (2 * context.getResources().getDisplayMetrics().density + 0.5f);
setSelectorColor(attributes.getColor(R.styleable.CircularImageView_selector_color, Color.TRANSPARENT));
setSelectorStrokeWidth(attributes.getDimensionPixelOffset(R.styleable.CircularImageView_selector_stroke_width, defaultSelectorSize));
setSelectorStrokeColor(attributes.getColor(R.styleable.CircularImageView_selector_stroke_color, Color.BLUE));
}
// Add shadow if enabled
if(attributes.getBoolean(R.styleable.CircularImageView_shadow, false))
addShadow();
// We no longer need our attributes TypedArray, give it back to cache
attributes.recycle();
}
/**
* Sets the CircularImageView's border width in pixels.
*
* #param borderWidth
*/
public void setBorderWidth(int borderWidth)
{
this.borderWidth = borderWidth;
this.requestLayout();
this.invalidate();
}
/**
* Sets the CircularImageView's basic border color.
*
* #param borderColor
*/
public void setBorderColor(int borderColor)
{
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
/**
* Sets the color of the selector to be draw over the
* CircularImageView. Be sure to provide some opacity.
*
* #param selectorColor
*/
public void setSelectorColor(int selectorColor)
{
this.selectorFilter = new PorterDuffColorFilter(selectorColor, PorterDuff.Mode.SRC_ATOP);
this.invalidate();
}
/**
* Sets the stroke width to be drawn around the CircularImageView
* during click events when the selector is enabled.
*
* #param selectorStrokeWidth
*/
public void setSelectorStrokeWidth(int selectorStrokeWidth)
{
this.selectorStrokeWidth = selectorStrokeWidth;
this.requestLayout();
this.invalidate();
}
/**
* Sets the stroke color to be drawn around the CircularImageView
* during click events when the selector is enabled.
*
* #param borderColor
*/
public void setSelectorStrokeColor(int selectorStrokeColor)
{
if (paintSelectorBorder != null)
paintSelectorBorder.setColor(selectorStrokeColor);
this.invalidate();
}
/**
* Adds a dark shadow to this CircularImageView.
*/
public void addShadow()
{
setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
}
#Override
public void onDraw(Canvas canvas)
{
// Don't draw anything without an image
if(image == null)
return;
// Nothing to draw (Empty bounds)
if(image.getHeight() == 0 || image.getWidth() == 0)
return;
// Compare canvas sizes
int oldCanvasSize = canvasSize;
canvasSize = canvas.getWidth();
if(canvas.getHeight() < canvasSize)
canvasSize = canvas.getHeight();
// Reinitialize shader, if necessary
if(oldCanvasSize != canvasSize)
refreshBitmapShader();
// Apply shader to paint
paint.setShader(shader);
// Keep track of selectorStroke/border width
int outerWidth = 0;
// Get the exact X/Y axis of the view
int center = canvasSize / 2;
if(hasSelector && isSelected) { // Draw the selector stroke & apply the selector filter, if applicable
outerWidth = selectorStrokeWidth;
center = (canvasSize - (outerWidth * 2)) / 2;
paint.setColorFilter(selectorFilter);
canvas.drawCircle(center + outerWidth, center + outerWidth, ((canvasSize - (outerWidth * 2)) / 2) + outerWidth - 4.0f, paintSelectorBorder);
}
else if(hasBorder) { // If no selector was drawn, draw a border and clear the filter instead... if enabled
outerWidth = borderWidth;
center = (canvasSize - (outerWidth * 2)) / 2;
paint.setColorFilter(null);
canvas.drawCircle(center + outerWidth, center + outerWidth, ((canvasSize - (outerWidth * 2)) / 2) + outerWidth - 4.0f, paintBorder);
}
else // Clear the color filter if no selector nor border were drawn
paint.setColorFilter(null);
// Draw the circular image itself
canvas.drawCircle(center + outerWidth, center + outerWidth, ((canvasSize - (outerWidth * 2)) / 2) - 4.0f, paint);
}
#Override
public boolean dispatchTouchEvent(MotionEvent event)
{
// Check for clickable state and do nothing if disabled
if(!this.isClickable()) {
this.isSelected = false;
return super.onTouchEvent(event);
}
// Set selected state based on Motion Event
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.isSelected = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_SCROLL:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
this.isSelected = false;
break;
}
// Redraw image and return super type
this.invalidate();
return super.dispatchTouchEvent(event);
}
public void invalidate(Rect dirty) {
super.invalidate(dirty);
image = drawableToBitmap(getDrawable());
if(shader != null || canvasSize > 0)
refreshBitmapShader();
}
public void invalidate(int l, int t, int r, int b) {
super.invalidate(l, t, r, b);
image = drawableToBitmap(getDrawable());
if(shader != null || canvasSize > 0)
refreshBitmapShader();
}
#Override
public void invalidate() {
super.invalidate();
image = drawableToBitmap(getDrawable());
if(shader != null || canvasSize > 0)
refreshBitmapShader();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// The parent has determined an exact size for the child.
result = specSize;
}
else if (specMode == MeasureSpec.AT_MOST) {
// The child can be as large as it wants up to the specified size.
result = specSize;
}
else {
// The parent has not imposed any constraint on the child.
result = canvasSize;
}
return result;
}
private int measureHeight(int measureSpecHeight)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
// The child can be as large as it wants up to the specified size.
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = canvasSize;
}
return (result + 2);
}
/**
* Convert a drawable object into a Bitmap
*
* #param drawable
* #return
*/
public Bitmap drawableToBitmap(Drawable drawable)
{
if (drawable == null) { // Don't do anything without a proper drawable
return null;
}
else if (drawable instanceof BitmapDrawable) { // Use the getBitmap() method instead if BitmapDrawable
return ((BitmapDrawable) drawable).getBitmap();
}
// Create Bitmap object out of the drawable
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
/**
* Reinitializes the shader texture used to fill in
* the Circle upon drawing.
*/
public void refreshBitmapShader()
{
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvasSize, canvasSize, false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
/**
* Returns whether or not this view is currently
* in its selected state.
*/
public boolean isSelected()
{
return this.isSelected;
}
}
for full example refer https://github.com/Pkmmte/CircularImageView/blob/master/circularimageview-eclipse/CircularImageView/src/com/pkmmte/circularimageview/CircularImageView.java

Categories

Resources