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
Related
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.
I have a custom view (a circle) which is rendered correctly when the app runs. But, the view is not aligned correctly when viewed in the XML preview pane.
Below are the screenshot of preview pane where the alignment is not correct.
Below is the screenshot of the same view with the correct alignment when being drawn on a running app.
Here is the code which defines the custom view -
public class LinearTimerView extends View {
private Paint arcPaint;
private RectF rectF;
private int initialColor;
private int progressColor;
private int circleRadiusInDp;
// The point from where the color-fill animation will start.
private int startingAngle = 270;
// The point up-till which user wants the circle to be pre-filled.
private float preFillAngle;
public LinearTimerView(Context context,
AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
R.styleable.LinearTimerView);
// Retrieve the view attributes.
this.circleRadiusInDp =
(int) typedArray.getDimension(R.styleable.LinearTimerView_radius, 5);
int strokeWidthInDp =
(int) typedArray.getDimension(R.styleable.LinearTimerView_strokeWidth, 2);
this.initialColor =
typedArray.getColor(R.styleable.LinearTimerView_initialColor,
ContextCompat.getColor(getContext(), R.color.colorInitial));
this.progressColor =
typedArray.getColor(R.styleable.LinearTimerView_progressColor,
ContextCompat.getColor(getContext(), R.color.colorProgress));
this.startingAngle =
typedArray.getInt(R.styleable.LinearTimerView_startingPoint, 270);
// Define the size of the circle.
rectF = new RectF(
(int) convertDpIntoPixel(strokeWidthInDp),
(int) convertDpIntoPixel(strokeWidthInDp),
(int) convertDpIntoPixel(circleRadiusInDp * 2)
+ (int) convertDpIntoPixel(strokeWidthInDp),
(int) convertDpIntoPixel(circleRadiusInDp * 2)
+ (int) convertDpIntoPixel(strokeWidthInDp));
arcPaint = new Paint();
arcPaint.setAntiAlias(true);
arcPaint.setStyle(Paint.Style.STROKE);
arcPaint.setStrokeWidth((int) convertDpIntoPixel(strokeWidthInDp));
typedArray.recycle();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
// Grey Circle - This circle will be there by default.
arcPaint.setColor(initialColor);
canvas.drawCircle(rectF.centerX(), rectF.centerY(),
(int) convertDpIntoPixel(circleRadiusInDp), arcPaint);
// Green Arc (Arc with 360 angle) - This circle will be animated as time progresses.
arcPaint.setColor(progressColor);
canvas.drawArc(rectF, startingAngle, preFillAngle, false, arcPaint);
} catch (NullPointerException ex) {
ex.printStackTrace();
}
}
/**
* Method to get the degrees up-till which the arc is already pre-filled.
* #return
*/
public float getPreFillAngle() {
return preFillAngle;
}
public void setPreFillAngle(float preFillAngle) {
this.preFillAngle = preFillAngle;
}
/**
* Method to get the starting point of the angle
* #return
*/
public int getStartingPoint() {
return startingAngle;
}
public void setStartingPoint(int startingPointInDegrees) {
this.startingAngle = startingPointInDegrees;
}
/**
* Method to convert DPs into Pixels.
*/
private float convertDpIntoPixel(float dp) {
float scale = getResources().getDisplayMetrics().density;
return dp * scale + 0.5f;
} }
How do I fix this?
Turns out I had not overriden theh onMeasure() method.
I took hint form this answer and was able to fix the problem.
I want to create RoundedImageView for some ImageView used in my app. I am creating custom view for that.
Below is the Xml file and source code for custom ImageView.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/deleteCallLog"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="4dip" >
<com.nimbuzz.ui.RoundedImageView
android:id="#+id/avatarImage"
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_centerHorizontal="true"
android:src="#drawable/default_avatar" />
<ImageView
android:id="#+id/subCallTypeIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#id/avatarImage"
android:layout_alignRight="#id/avatarImage"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
Line: Bitmap bitmap = b.copy(Config.ARGB_8888, true); causing NullPointerException.
It does'nt always happens . On some screens this code works.
RoundedImageView.java:
public class RoundedImageView extends ImageView {
public RoundedImageView(Context context) {
super(context);
}
public RoundedImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas)
{
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable)drawable).getBitmap();
Bitmap bitmap = b.copy(Config.ARGB_8888, true);
int w = getWidth(), h = getHeight();
Bitmap roundBitmap = getCroppedBitmap(b, w);
canvas.drawBitmap(roundBitmap, 0, 0, null);
}
public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
Bitmap sbmp;
if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
float smallest = Math.min(bmp.getWidth(), bmp.getHeight());
float factor = smallest / radius;
sbmp = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth() / factor), (int)(bmp.getHeight() / factor), false);
} else {
sbmp = bmp;
}
Bitmap output = Bitmap.createBitmap(radius, radius,
Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xffa19774;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, radius, radius);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawCircle(radius / 2 + 0.7f,
radius / 2 + 0.7f, radius / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(sbmp, rect, rect, paint);
return output;
}
}
Try this o create own Imageview and directly set the bitmap or resource in imageview..
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.ImageView;
#SuppressLint("DrawAllocation")
public class RoundedCornerImageView extends ImageView {
public RoundedCornerImageView(Context context) {
super(context);
}
public RoundedCornerImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedCornerImageView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
float radius = 90.0f; // angle of round corners
Path clipPath = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
clipPath.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(clipPath);
super.onDraw(canvas);
}
}
Custom CirularImageView
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
* Custom ImageView for circular images in Android while maintaining the
* best draw performance and supporting custom borders & selectors.
*/
public class CircularImageView extends ImageView {
// For logging purposes
private static final String TAG = CircularImageView.class.getSimpleName();
// Default property values
private static final boolean SHADOW_ENABLED = false;
private static final float SHADOW_RADIUS = 4f;
private static final float SHADOW_DX = 0f;
private static final float SHADOW_DY = 2f;
private static final int SHADOW_COLOR = Color.BLACK;
// Border & Selector configuration variables
private boolean hasBorder;
private boolean hasSelector;
private boolean isSelected;
private int borderWidth;
private int canvasSize;
private int selectorStrokeWidth;
// Shadow properties
private boolean shadowEnabled;
private float shadowRadius;
private float shadowDx;
private float shadowDy;
private int shadowColor;
// 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, R.styleable.CircularImageViewStyle_circularImageViewDefault);
}
public CircularImageView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CircularImageViewStyle_circularImageViewDefault);
}
public CircularImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CircularImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr);
}
/**
* Initializes paint objects and sets desired attributes.
* #param context Context
* #param attrs Attributes
* #param defStyle Default Style
*/
private void init(Context context, AttributeSet attrs, int defStyle) {
// Initialize paint objects
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
paintBorder.setAntiAlias(true);
paintBorder.setStyle(Paint.Style.STROKE);
paintSelectorBorder = new Paint();
paintSelectorBorder.setAntiAlias(true);
// Enable software rendering on HoneyComb and up. (needed for shadow)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
setLayerType(LAYER_TYPE_SOFTWARE, null);
// Load the styled attributes and set their properties
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CircularImageView, defStyle, 0);
// Check for extra features being enabled
hasBorder = attributes.getBoolean(R.styleable.CircularImageView_civ_border, false);
hasSelector = attributes.getBoolean(R.styleable.CircularImageView_civ_selector, false);
shadowEnabled = attributes.getBoolean(R.styleable.CircularImageView_civ_shadow, SHADOW_ENABLED);
// Set border properties, if enabled
if(hasBorder) {
int defaultBorderSize = (int) (2 * context.getResources().getDisplayMetrics().density + 0.5f);
setBorderWidth(attributes.getDimensionPixelOffset(R.styleable.CircularImageView_civ_borderWidth, defaultBorderSize));
setBorderColor(attributes.getColor(R.styleable.CircularImageView_civ_borderColor, 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_civ_selectorColor, Color.TRANSPARENT));
setSelectorStrokeWidth(attributes.getDimensionPixelOffset(R.styleable.CircularImageView_civ_selectorStrokeWidth, defaultSelectorSize));
setSelectorStrokeColor(attributes.getColor(R.styleable.CircularImageView_civ_selectorStrokeColor, Color.BLUE));
}
// Set shadow properties, if enabled
if(shadowEnabled) {
shadowRadius = attributes.getFloat(R.styleable.CircularImageView_civ_shadowRadius, SHADOW_RADIUS);
shadowDx = attributes.getFloat(R.styleable.CircularImageView_civ_shadowDx, SHADOW_DX);
shadowDy = attributes.getFloat(R.styleable.CircularImageView_civ_shadowDy, SHADOW_DY);
shadowColor = attributes.getColor(R.styleable.CircularImageView_civ_shadowColor, SHADOW_COLOR);
setShadowEnabled(true);
}
// We no longer need our attributes TypedArray, give it back to cache
attributes.recycle();
}
/**
* Sets the CircularImageView's border width in pixels.
* #param borderWidth Width in pixels for the border.
*/
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
if(paintBorder != null)
paintBorder.setStrokeWidth(borderWidth);
requestLayout();
invalidate();
}
/**
* Sets the CircularImageView's basic border color.
* #param borderColor The new color (including alpha) to set the border.
*/
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 The color (including alpha) to set for the selector overlay.
*/
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 Width in pixels for the selector stroke.
*/
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 selectorStrokeColor The color (including alpha) to set for the selector stroke.
*/
public void setSelectorStrokeColor(int selectorStrokeColor) {
if (paintSelectorBorder != null)
paintSelectorBorder.setColor(selectorStrokeColor);
this.invalidate();
}
/**
* Enables a dark shadow for this CircularImageView.
* #param enabled Set to true to draw a shadow or false to disable it.
*/
public void setShadowEnabled(boolean enabled) {
shadowEnabled = enabled;
updateShadow();
}
/**
* Enables a dark shadow for this CircularImageView.
* If the radius is set to 0, the shadow is removed.
* #param radius Radius for the shadow to extend to.
* #param dx Horizontal shadow offset.
* #param dy Vertical shadow offset.
* #param color The color of the shadow to apply.
*/
public void setShadow(float radius, float dx, float dy, int color) {
shadowRadius = radius;
shadowDx = dx;
shadowDy = dy;
shadowColor = color;
updateShadow();
}
#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;
// Update shader if canvas size has changed
int oldCanvasSize = canvasSize;
canvasSize = getWidth() < getHeight() ? getWidth() : getHeight();
if(oldCanvasSize != canvasSize)
updateBitmapShader();
// 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);
RectF rekt = new RectF(0 + outerWidth / 2, 0 + outerWidth / 2, canvasSize - outerWidth / 2, canvasSize - outerWidth / 2);
canvas.drawArc(rekt, 360, 360, false, paintBorder);
//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), 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);
}
#Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
// Extract a Bitmap out of the drawable & set it as the main shader
image = drawableToBitmap(getDrawable());
if(canvasSize > 0)
updateBitmapShader();
}
#Override
public void setImageResource(int resId) {
super.setImageResource(resId);
// Extract a Bitmap out of the drawable & set it as the main shader
image = drawableToBitmap(getDrawable());
if(canvasSize > 0)
updateBitmapShader();
}
#Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
// Extract a Bitmap out of the drawable & set it as the main shader
image = drawableToBitmap(getDrawable());
if(canvasSize > 0)
updateBitmapShader();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
// Extract a Bitmap out of the drawable & set it as the main shader
image = bm;
if(canvasSize > 0)
updateBitmapShader();
}
#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;
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;
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);
}
// TODO: Update shadow layers based on border/selector state and visibility.
private void updateShadow() {
float radius = shadowEnabled ? shadowRadius : 0;
//paint.setShadowLayer(radius, shadowDx, shadowDy, shadowColor);
paintBorder.setShadowLayer(radius, shadowDx, shadowDy, shadowColor);
paintSelectorBorder.setShadowLayer(radius, shadowDx, shadowDy, shadowColor);
}
/**
* Convert a drawable object into a Bitmap.
* #param drawable Drawable to extract a Bitmap from.
* #return A Bitmap created from the drawable parameter.
*/
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
Log.i(TAG, "Bitmap drawable!");
return ((BitmapDrawable) drawable).getBitmap();
}
int intrinsicWidth = drawable.getIntrinsicWidth();
int intrinsicHeight = drawable.getIntrinsicHeight();
if (!(intrinsicWidth > 0 && intrinsicHeight > 0))
return null;
try {
// Create Bitmap object out of the drawable
Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (OutOfMemoryError e) {
// Simply return null of failed bitmap creations
Log.e(TAG, "Encountered OutOfMemoryError while generating bitmap!");
return null;
}
}
// TODO TEST REMOVE
public void setIconModeEnabled(boolean e) {}
/**
* Re-initializes the shader texture used to fill in
* the Circle upon drawing.
*/
public void updateBitmapShader() {
if (image == null)
return;
shader = new BitmapShader(image, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if(canvasSize != image.getWidth() || canvasSize != image.getHeight()) {
Matrix matrix = new Matrix();
float scale = (float) canvasSize / (float) image.getWidth();
matrix.setScale(scale, scale);
shader.setLocalMatrix(matrix);
}
}
/**
* #return Whether or not this view is currently
* in its selected state.
*/
public boolean isSelected() {
return this.isSelected;
}
}
Usage
<com.yourpackage.CircularImageView
android:layout_width="100dp"
android:id="#+id/btn_myprofile"
android:layout_height="100dp"
app:civ_border="true"
app:civ_borderColor="#FF9900"
app:civ_borderWidth="2dp"
app:civ_shadow="false"
android:background="#drawable/profile_icon" />
https://github.com/hdodenhof/CircleImageView
https://github.com/lopspower/CircularImageView
I have created a CustomeEditText using this answer in my android application for assigning a drawable click on the right Drawable.
The class is given below.
public class CustomEditText extends EditText {
private Drawable drawableRight;
private Drawable drawableLeft;
private Drawable drawableTop;
private Drawable drawableBottom;
int actionX, actionY;
int mHeight = 0;
private DrawableClickListener clickListener;
public CustomEditText(Context context) {
super(context);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// this Contructure required when you are using this view in xml
}
public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
Log.v("Login", "setCompoundDrawables");
if (left != null) {
Log.v("Login", "Left Not Null");
drawableLeft = left;
}
if (right != null) {
drawableRight = right;
}
if (top != null) {
drawableTop = top;
}
if (bottom != null) {
drawableBottom = bottom;
}
Log.v("CustomeEditText", "Left :"+left);
super.setCompoundDrawables(left, top, right, bottom);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Rect bounds;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
actionX = (int) event.getX();
actionY = (int) event.getY();
if (drawableBottom != null
&& drawableBottom.getBounds().contains(actionX, actionY)) {
clickListener.onClick(DrawableClickListener.DrawablePosition.BOTTOM);
return super.onTouchEvent(event);
}
if (drawableTop != null
&& drawableTop.getBounds().contains(actionX, actionY)) {
clickListener.onClick(DrawableClickListener.DrawablePosition.TOP);
return super.onTouchEvent(event);
}
// this works for left since container shares 0,0 origin with bounds
if (drawableLeft != null) {
bounds = null;
bounds = drawableLeft.getBounds();
int x, y;
int extraTapArea = (int) (13 * getResources()
.getDisplayMetrics().density + 0.5);
x = actionX;
y = actionY;
if (!bounds.contains(actionX, actionY)) {
/** Gives the +20 area for tapping. */
x = (int) (actionX - extraTapArea);
y = (int) (actionY - extraTapArea);
if (x <= 0)
x = actionX;
if (y <= 0)
y = actionY;
/** Creates square from the smallest value */
if (x < y) {
y = x;
}
}
if (bounds.contains(x, y) && clickListener != null) {
clickListener
.onClick(DrawableClickListener.DrawablePosition.LEFT);
event.setAction(MotionEvent.ACTION_CANCEL);
return false;
}
}
if (drawableRight != null) {
bounds = null;
bounds = drawableRight.getBounds();
int x, y;
int extraTapArea = 13;
/**
* IF USER CLICKS JUST OUT SIDE THE RECTANGLE OF THE DRAWABLE
* THAN ADD X AND SUBTRACT THE Y WITH SOME VALUE SO THAT AFTER
* CALCULATING X AND Y CO-ORDINATE LIES INTO THE DRAWBABLE
* BOUND. - this process help to increase the tappable area of
* the rectangle.
*/
x = (int) (actionX + extraTapArea);
y = (int) (actionY - extraTapArea);
/**
* Since this is right drawable subtract the value of x from the
* width of view. so that width - tappedarea will result in x
* co-ordinate in drawable bound.
*/
x = getWidth() - x;
/*
* x can be negative if user taps at x co-ordinate just near the
* width. e.g views width = 300 and user taps 290. Then as per
* previous calculation 290 + 13 = 303. So subtract X from
* getWidth() will result in negative value. So to avoid this
* add the value previous added when x goes negative.
*/
if (x <= 0) {
x += extraTapArea;
}
/*
* If result after calculating for extra tappable area is
* negative. assign the original value so that after subtracting
* extratapping area value doesn't go into negative value.
*/
if (y <= 0)
y = actionY;
/**
* If drawble bounds contains the x and y points then move
* ahead.
*/
if (bounds.contains(x, y) && clickListener != null) {
clickListener
.onClick(DrawableClickListener.DrawablePosition.RIGHT);
event.setAction(MotionEvent.ACTION_CANCEL);
return false;
}
return super.onTouchEvent(event);
}
}
return super.onTouchEvent(event);
}
#Override
protected void finalize() throws Throwable {
drawableRight = null;
drawableBottom = null;
drawableLeft = null;
drawableTop = null;
super.finalize();
}
public void setDrawableClickListener(DrawableClickListener listener) {
this.clickListener = listener;
}
public interface DrawableClickListener {
public static enum DrawablePosition {
TOP, BOTTOM, LEFT, RIGHT
};
public void onClick(DrawablePosition target);
}
}
So in the xml
<com.mypack.CustomEditText
android:id="#+id/category"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_margin="2dp"
android:background="#drawable/b"
android:drawablePadding="10dp"
android:drawableRight="#drawable/category"
android:hint="My hint"
android:padding="10dp"
android:textColor="#000000"
android:textSize="15sp"
android:textStyle="bold" />
Now i want to create the same view through code.
I tried to create like this
CustomEditText example = new CustomEditText(this);
Log.v("Login", "message");
example.setCompoundDrawables(getResources().getDrawable(R.drawable.delete_category), null, null, null);
example.setBackgroundResource(R.drawable.category_background);
example.setGravity(Gravity.CENTER);
example.setTextColor(getResources().getColor(
android.R.color.white));
//example.setMargins(4, 4, 4, 4);
example.setPadding(4, 4, 4, 4);
example.setTextSize(20);
example.setText(category);
ll.addView(example);
It is creating a normal edittext without drawable on left side.How i can create a CustomeEditText with Drawable on Right Side.
You should use
example.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.delete_category), null, null, null);
Instead of
example.setCompoundDrawables(getResources().getDrawable(R.drawable.delete_category), null, null, null);
This will result in the your custom EditText with Drawable on left side.
So your code will be like :
CustomEditText example = new CustomEditText(this);
Log.v("Login", "message");
example.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.delete_category), null, null, null);
example.setBackgroundResource(R.drawable.category_background);
example.setGravity(Gravity.CENTER);
example.setTextColor(getResources().getColor(
android.R.color.white));
//example.setMargins(4, 4, 4, 4);
example.setPadding(4, 4, 4, 4);
example.setTextSize(20);
example.setText(category);
ll.addView(example);
1. setCompoundDrawable to add Drawables:
If you want to use setCompoundDrawable to add Drawables then you need to specify the bounds using setBounds. That will define a bounding rectangle around the image. If you don't do that, you wont be able to see the Drawable.
eg. In your case use it like,
Drawable img = getResources().getDrawable(R.drawable.delete_category);
img.setBounds( 0, 0, 60, 60 );
example.setCompoundDrawables(img, null, null, null);
2. setCompoundDrawableWithIntrinsicBounds to add Drawables:
You can use setCompoundDrawableWithIntrinsicBounds() instead. It will use the size of the image as the size of the Drawable. ie The Drawables' bounds will be set to their intrinsic bounds.
android:gravity="right"
put it in your xml or setGravity(Gravity.RIGHT);
I am trying to make the CoverFlow below appear like a Horizontal Gallery. The original file looks like this
There are three classes
the CoverFlow.class which extends a gallery
public class CoverFlow extends Gallery {
private Camera mCamera = new Camera();
private int mMaxRotationAngle = 50;
private int mMaxZoom = -380;
private int mCoveflowCenter;
private boolean mAlphaMode = true;
private boolean mCircleMode = false;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
public boolean getCircleMode() {
return mCircleMode;
}
public void setCircleMode(boolean isCircle) {
mCircleMode = isCircle;
}
public boolean getAlphaMode() {
return mAlphaMode;
}
public void setAlphaMode(boolean isAlpha) {
mAlphaMode = isAlpha;
}
public int getMaxZoom() {
return mMaxZoom;
}
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ getPaddingLeft();
}
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth();
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle
: mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle);
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* #param w
* Current width of this view.
* #param h
* Current height of this view.
* #param oldw
* Old width of this view.
* #param oldh
* Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
*
* #param imageView
* ImageView the ImageView whose bitmap we want to rotate
* #param t
* transformation
* #param rotationAngle
* the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t,
int rotationAngle) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();
final int imageHeight = child.getLayoutParams().height;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
// As the angle of the view gets less, zoom in
if (rotation <= mMaxRotationAngle) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
if (mCircleMode) {
if (rotation < 40)
mCamera.translate(0.0f, 155, 0.0f);
else
mCamera.translate(0.0f, (255 - rotation * 2.5f), 0.0f);
}
if (mAlphaMode) {
((ImageView) (child)).setAlpha((int) (255 - rotation * 2.5));
}
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth ), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth ), (imageHeight / 2));
mCamera.restore();
}
}
The second class is my coverFlowActivity which is my class class
public class CoverFlowActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
CoverFlow cf = new CoverFlow(this);
ImageAdapter ia = new ImageAdapter(this);
cf.setAdapter(ia);
cf.setAnimationDuration(1000);
setContentView(cf);
}
public class ImageAdapter extends BaseAdapter {
private int[] mImgs = {
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img5,
R.drawable.img6,
R.drawable.img7,
R.drawable.img8
};
Context mContext;
public ImageAdapter(Context context) {
this.mContext = context;
}
#Override
public int getCount() {
return mImgs.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return mImgs[position];
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ReflectionImage i = new ReflectionImage(mContext);
i.setImageResource(mImgs[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(100, 100));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
}
public float getScale(boolean focused, int offset) {
return Math.max(0, 1f/(float)Math.pow(2, Math.abs(offset)));
}
}
}
and the third activity is the reflected image below each image.
public class ReflectionImage extends ImageView {
//是否为Reflection模式
private boolean mReflectionMode = true;
public ReflectionImage(Context context) {
super(context);
}
public ReflectionImage(Context context, AttributeSet attrs) {
super(context, attrs);
//取得原始图片的bitmap并重画
Bitmap originalImage = ((BitmapDrawable)this.getDrawable()).getBitmap();
DoReflection(originalImage);
}
public ReflectionImage(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
Bitmap originalImage = ((BitmapDrawable)this.getDrawable()).getBitmap();
DoReflection(originalImage);
}
public void setReflectionMode(boolean isRef) {
mReflectionMode = isRef;
}
public boolean getReflectionMode() {
return mReflectionMode;
}
//偷懒了,只重写了setImageResource,和构造函数里面干了同样的事情
#Override
public void setImageResource(int resId) {
Bitmap originalImage = BitmapFactory.decodeResource(
getResources(), resId);
DoReflection(originalImage);
//super.setImageResource(resId);
}
private void DoReflection(Bitmap originalImage) {
final int reflectionGap = 4; //原始图片和反射图片中间的间距
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//反转
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//reflectionImage就是下面透明的那部分,可以设置它的高度为原始的3/4,这样效果会更好些
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
0, width, height, matrix, false);
//创建一个新的bitmap,高度为原来的两倍
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height), Config.ARGB_8888);
Canvas canvasRef = new Canvas(bitmapWithReflection);
//先画原始的图片
canvasRef.drawBitmap(originalImage, 0, 0, null);
//画间距
Paint deafaultPaint = new Paint();
canvasRef.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//画被反转以后的图片
canvasRef.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
// 创建一个渐变的蒙版放在下面被反转的图片上面
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0,
originalImage.getHeight(), 0, bitmapWithReflection.getHeight()
+ reflectionGap, 0x80ffffff, 0x00ffffff, TileMode.CLAMP);
// Set the paint to use this shader (linear gradient)
paint.setShader(shader);
// Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// Draw a rectangle using the paint with our linear gradient
canvasRef.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);
//调用ImageView中的setImageBitmap
this.setImageBitmap(bitmapWithReflection);
}
}
I want to make it appears like a gallery that is all images must have four dp margin and appear like the center image without any effects. Can someone please help me to achieve this.
Please try this:
private int mMaxRotationAngle = 0;
and
CoverFlow cf = new CoverFlow(this);
cf.setSpacing(-30);
in the CoverFlowActivity
From what I understood, you need a ViewPager:
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
NOTE: add the support library to support API till level 4 ;-)
Some usefull code: http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html
setContentView(R.layout.main);
mContainer = (PagerContainer) findViewById(R.id.pager_container);
ViewPager pager = mContainer.getViewPager();
PagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
//Necessary or the pager will only have one extra page to show
// make this at least however many pages you can see
pager.setOffscreenPageLimit(adapter.getCount());
//A little space between pages
pager.setPageMargin(15);
//If hardware acceleration is enabled, you should also remove
// clipping on the pager for its children.
pager.setClipChildren(false);
Adding padding and margin should be trivial ;-)