I want an underline below OPTING like below, but when i create this will the help of customview, underline appears just below opting but i want some space between text and line like image
I had created a Custom view, in which a word will searched in a string if that is found, then that corresponding text will be underlined but the only thing i want is to give some space between underline and text,
My class is as follows,
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.AppCompatTextView;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.Display;
import android.view.WindowManager;
import android.widget.TextView;
public class UnderLine extends AppCompatTextView {
private Rect mRect;
private Paint mPaint;
private int mColor;
private float density;
private float mStrokeWidth;
private String stringSeach;
public UnderLine(Context context) {
this(context, null, 0);
}
public UnderLine(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UnderLine(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
density = context.getResources().getDisplayMetrics().density;
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.UnderLine, defStyle, 0);
mColor = typedArray.getColor(R.styleable.UnderLine_underlineColorr, 0xFFFF0000);
stringSeach = typedArray.getString(R.styleable.UnderLine_underlineTextt);
mStrokeWidth = typedArray.getDimension(R.styleable.UnderLine_underlineWidthh, density * 2);
typedArray.recycle();
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mColor); //line mColor
mPaint.setStrokeWidth(mStrokeWidth);
}
public int getUnderLineColor() {
return mColor;
}
public void setUnderLineColor(int mColor) {
this.mColor = mColor;
invalidate();
}
public float getUnderlineWidth() {
return mStrokeWidth;
}
public void setUnderlineWidth(float mStrokeWidth) {
this.mStrokeWidth = mStrokeWidth;
invalidate();
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension( getMeasuredWidth(), getMeasuredHeight()+110);
}
#Override
protected void onDraw(Canvas canvas) {
TextView parentTextView = this;
Rect parentTextViewRect = new Rect();
String targetWord = stringSeach.toLowerCase();
int startOffsetOfClickedText = this.getText().toString().toLowerCase().indexOf(targetWord);
int endOffsetOfClickedText = startOffsetOfClickedText + targetWord.length();
// Initialize values for the computing of clickedText position
Layout textViewLayout = parentTextView.getLayout();
double startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)startOffsetOfClickedText);
double endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)endOffsetOfClickedText);
// Get the rectangle of the clicked text
int currentLineStartOffset = textViewLayout.getLineForOffset((int)startOffsetOfClickedText);
int currentLineEndOffset = textViewLayout.getLineForOffset((int)endOffsetOfClickedText);
boolean keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset;
textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect);
// Update the rectangle position to his real position on screen
int[] parentTextViewLocation = {0,0};
parentTextView.getLocationOnScreen(parentTextViewLocation);
double parentTextViewTopAndBottomOffset = (
//parentTextViewLocation[1] -
parentTextView.getScrollY() +
parentTextView.getCompoundPaddingTop()
);
parentTextViewRect.top += parentTextViewTopAndBottomOffset;
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
// In the case of multi line text, we have to choose what rectangle take
if (keywordIsInMultiLine){
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int screenHeight = display.getHeight();
int dyTop = parentTextViewRect.top;
int dyBottom = screenHeight - parentTextViewRect.bottom;
boolean onTop = dyTop > dyBottom;
if (onTop){
endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
}
else{
parentTextViewRect = new Rect();
textViewLayout.getLineBounds(currentLineEndOffset, parentTextViewRect);
parentTextViewRect.top += parentTextViewTopAndBottomOffset;
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
}
}
parentTextViewRect.left += (
parentTextViewLocation[0] +
startXCoordinatesOfClickedText +
parentTextView.getCompoundPaddingLeft() -
parentTextView.getScrollX()
);
parentTextViewRect.right = (int) (
parentTextViewRect.left +
endXCoordinatesOfClickedText -
startXCoordinatesOfClickedText
);
canvas.drawLine(parentTextViewRect.left,parentTextViewRect.bottom+mStrokeWidth, parentTextViewRect.right,
parentTextViewRect.bottom+mStrokeWidth, mPaint);
super.onDraw(canvas);
}
}
attrs.xml is as follows,
<declare-styleable name="UnderLine" >
<attr name="underlineWidthh" format="dimension" />
<attr name="underlineColorr" format="color" />
<attr name="underlineTextt" format="string" />
</declare-styleable>
Sample layout as follows,
<UnderLine
style="#style/textView"
android:gravity="top|center"
app:underlineColorr="#color/signup_bottom_darkWhite"
app:underlineWidthh="2dp"
app:underlineTextt="OPTING"
android:text="ON FREE PARKING + DISCOUNTED RATES \n BY OPTING IN"/>
I am a beginner with customview, and i had created this with the help of some answers on stackoverflow. Please don't suggest any other way to do this.
Anyhelp will be greatly appreciated.
I had developed my own view, as follows,
package com.example.reprator.underlinetextview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.AppCompatTextView;
import android.text.Layout;
import android.util.AttributeSet;
public class UnderlinedTextView extends AppCompatTextView {
private Rect mRect;
private Paint mPaint;
private int mColor;
private float mStrokeWidth;
private float mMarginTop;
private boolean isAllSelected;
private int lineNumber;
private int selectTextEachLine;
public UnderlinedTextView(Context context) {
this(context, null, 0);
}
public UnderlinedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UnderlinedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
float density = context.getResources().getDisplayMetrics().density;
TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.UnderlinedTextView, defStyle, 0);
mColor = typedArray.getColor(R.styleable.UnderlinedTextView_underlineColor, 0xFFFF0000);
mStrokeWidth = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineWidth, density * 2);
mMarginTop = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineMarginTop, density * 2);
isAllSelected = typedArray.getBoolean(R.styleable.UnderlinedTextView_underlineIsAll, false);
lineNumber = typedArray.getInteger(R.styleable.UnderlinedTextView_underlineNoLine, 1);
selectTextEachLine = typedArray.getInteger(R.styleable.UnderlinedTextView_underlineTextEachLine, 3);
typedArray.recycle();
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mColor); //line mColor
mPaint.setStrokeWidth(mStrokeWidth);
}
public int getUnderLineColor() {
return mColor;
}
public void setUnderLineColor(int mColor) {
this.mColor = mColor;
invalidate();
}
public float getUnderlineWidth() {
return mStrokeWidth;
}
public void setUnderlineWidth(float mStrokeWidth) {
this.mStrokeWidth = mStrokeWidth;
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int h = (int) (getMeasuredHeight() + mMarginTop);
setMeasuredDimension(widthMeasureSpec, h);
}
#Override
protected void onDraw(Canvas canvas) {
final Layout layout = getLayout();
float x_start, x_stop;
int firstCharInLine, lastCharInLine;
int limit = isAllSelected ? getLineCount() : lineNumber;
for (int i = 0; i < limit; i++) {
int baseline = getLineBounds(i, mRect);
firstCharInLine = layout.getLineStart(i);
lastCharInLine = layout.getLineEnd(i);
int textHighlight = isAllSelected ? lastCharInLine - 1 : (firstCharInLine + selectTextEachLine);
x_start = layout.getPrimaryHorizontal(firstCharInLine);
x_stop = layout.getPrimaryHorizontal(textHighlight);
float y = baseline + mStrokeWidth + mMarginTop;
canvas.drawLine(x_start, y, x_stop, y, mPaint);
}
super.onDraw(canvas);
}
}
and my attars.xml are as follows,
<declare-styleable name="UnderlinedTextView">
<attr name="underlineWidth" format="dimension" />
<attr name="underlineMarginTop" format="dimension" />
<attr name="underlineColor" format="color" />
<attr name="underlineText" format="string" />
<attr name="underlineIsAll" format="boolean" />
<attr name="underlineNoLine" format="integer" />
<attr name="underlineTextEachLine" format="integer" />
</declare-styleable>
Related
I need get image from external memory and place that image into round shape
And also save that image into sheredprefernce
I tried may way but I can't make it
I need it to done without any external library
For Rounded imageview without any library you can use the below class.
package com.sample;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Custom Circular Image view.
*/
public class RoundImageview extends ImageView {
private int borderWidth = 0;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private Paint shaderPaint;
private BitmapShader shader;
private static int mBorderColor = android.R.color.white;
private Context context;
private boolean needToDrawOverlay = true;
private AttributeSet attrs;
public RoundImageview(final Context context) {
super(context);
this.context = context;
}
public RoundImageview(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.attrs = attrs;
init();
}
public RoundImageview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
this.attrs = attrs;
init();
}
/**
* To initialize the paint components.
*/
private void init() {
Resources res = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView, 0, 0);
int backgroundColor = a.getColor(R.styleable.RoundImageView_bg_color,
res.getColor(android.R.color.darker_gray));
a.recycle();
paint = new Paint();
paint.setAntiAlias(true);
shaderPaint = new Paint();
shaderPaint.setAntiAlias(true);
shaderPaint.setColor(backgroundColor);
shaderPaint.setStyle(Paint.Style.FILL);
paintBorder = new Paint();
paintBorder.setColor(mBorderColor);
borderWidth = 2;
paintBorder.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas) {
// load the bitmap
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null) {
image = bitmapDrawable.getBitmap();
}
if (needToDrawOverlay) {
int circleCenter = viewWidth / 2;
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter
+ borderWidth - 4.0f, shaderPaint);
}
// init shader
if (image != null) {
shader = createBitmapShader(image, canvas);
paint.setShader(shader);
// calculate the center point.
int circleCenter = viewWidth / 2;
// circleCenter is the x or y of the view's center
// radius is the radius in pixels of the cirle to be drawn
// paint contains the shader that will texture the shape
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter
+ borderWidth - 4.0f, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth,
circleCenter - 4.0f, paint);
}
}
/**
* To create bitmap shader.
*
* #param image
* #param canvas
* #return BitmapShader object
*/
private BitmapShader createBitmapShader(Bitmap image, Canvas canvas) {
return new BitmapShader(Bitmap.createScaledBitmap(image, this.getWidth(), this.getHeight(),
false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width;
viewHeight = height;
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) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth) {
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 {
// Measure the text (beware: ascent is a negative number)
result = viewHeight;
}
return (result);
}
}
Create a attrs.xml in the values module of the res module
<!-- Round Image view -->
<declare-styleable name="RoundImageView">
<attr name="bg_color" format="color" />
<attr name="border_color" format="color" />
</declare-styleable>
Use it in your layout this way
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:roundImageview="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.sample.RoundImageview
android:id="#+id/imageButton"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:src="#drawable/image"
roundImageview:bg_color="#000000" />
</RelativeLayout>
I have created one EditText programatically when I type in EditText if the text goes to next line then I want to draw horizontal line inside EditText. How can I achieve it ?
As per the Raghunandan comment, You can use this class in your project
LinedEditText.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.EditText;
public class LinedEditText extends EditText {
private Paint mPaint = new Paint();
public LinedEditText(Context context) {
super(context);
initPaint();
}
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public LinedEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPaint();
}
private void initPaint() {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x80000000);
}
#Override protected void onDraw(Canvas canvas) {
int left = getLeft();
int right = getRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int height = getHeight();
int lineHeight = getLineHeight();
int count = (height-paddingTop-paddingBottom) / lineHeight;
for (int i = 0; i < count; i++) {
int baseline = lineHeight * (i+1) + paddingTop;
canvas.drawLine(left+paddingLeft, baseline, right-paddingRight, baseline, mPaint);
}
super.onDraw(canvas);
}
}
use it in layout file
<yourpackagename.LinedEditText
android:id="#+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#android:color/transparent"
android:ems="10"
android:gravity="top"
android:hint="#string/note_body"
android:inputType="textMultiLine"
android:lineSpacingExtra="4dp"
android:textColor="#000000"
android:textColorHint="#e2e2e2" />
I had successfully created an answer of my This question(left drawable alignment with text in andorid)
but the problem is that the canvas.drawText x co-ordinate is not working properly as the x co-ordinate is not affecting the position of text.
Here is the result with overlapped text on drawable,
I just wanted to remove the overlap and give the distance between the drawable and text on canvas.
Here is my code as follows,
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
android.widget.TextView;
import cl.tt.R;
public class CenterDrawableButton extends TextView{
private Drawable mDrawableCenter;
public CenterDrawableButton(Context context) {
super(context);
init(context, null);
}
public CenterDrawableButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CenterDrawableButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CenterDrawableButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs){
if(attrs!=null){
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CenterDrawableButton, 0, 0);
try {
setCenterDrawable(a.getDrawable(R.styleable.CenterDrawableButton_drawableCenter));
} finally {
a.recycle();
}
}
}
public void setCenterDrawable(#Nullable Drawable center) {
int[] state;
state = getDrawableState();
if (center != null) {
center.setState(state);
center.setBounds(0, 0, center.getIntrinsicWidth(), center.getIntrinsicHeight());
center.setCallback(this);
}
mDrawableCenter = center;
invalidate();
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mDrawableCenter != null) {
setMeasuredDimension(Math.max(getMeasuredWidth(), mDrawableCenter.getIntrinsicWidth()),
Math.max(getMeasuredHeight(), mDrawableCenter.getIntrinsicHeight()));
}
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mDrawableCenter != null) {
int[] state = getDrawableState();
mDrawableCenter.setState(state);
mDrawableCenter.setBounds(0, 0, mDrawableCenter.getIntrinsicWidth(),
mDrawableCenter.getIntrinsicHeight());
}
invalidate();
}
#Override
protected void onDraw(#NonNull Canvas canvas) {
super.onDraw(canvas);
if (mDrawableCenter != null) {
float textWidth = getPaint().measureText((String)getText());
int newPosition=getWidth()-((int)textWidth+getPaddingLeft() + getPaddingRight()+mDrawableCenter.getIntrinsicWidth()+ (int)getResources().getDimension(R.dimen._5sdp));
if(newPosition>0) {
canvas.translate(newPosition, (getHeight() - mDrawableCenter.getIntrinsicHeight()) / 2);
mDrawableCenter.draw(canvas);
canvas.save();
Paint textPaint = new Paint();
int yPos = (int) ((getHeight() - ((textPaint.descent() + textPaint.ascent())) / 2));
canvas.drawText(getText().toString(), newPosition, yPos, textPaint);
canvas.restore();
}else
{
int gnewPosition=getPaddingLeft() + getPaddingRight()+(int)getResources().getDimension(R.dimen._5sdp);
canvas.translate(gnewPosition, (getHeight() - mDrawableCenter.getIntrinsicHeight()) / 2);
mDrawableCenter.draw(canvas);
canvas.save();
int nggewPosition=getPaddingLeft() + getPaddingRight()+mDrawableCenter.getIntrinsicWidth()+ (int)getResources().getDimension(R.dimen._5sdp);
Paint textPaint = new Paint();
int yPos = (int) (getHeight() - ((textPaint.descent() + textPaint.ascent())) / 2);
canvas.translate(nggewPosition, yPos);
canvas.drawText(getText().toString(), nggewPosition, yPos, textPaint);
canvas.restore();
}
}
}
}
here is the attr,
<attr name="drawableCenter" format="reference"/>
<declare-styleable name="CenterDrawableButton">
<attr name="drawableCenter"/>
</declare-styleable>
I would like to draw an image as below using android drawable something as below:
I could do the arrow but when I add circle, the image gets distorted.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<solid android:color="#android:color/transparent"/>
<size android:width="2dp" android:height="50dp"/>
</shape>
</item>
<item android:bottom="20dp">
<rotate
android:fromDegrees="45"
android:toDegrees="45">
<shape android:shape="rectangle">
<solid android:color="#F0AD4E"/>
<corners
android:radius="0dp"
android:bottomRightRadius="0dp"
android:bottomLeftRadius="0dp"/>
</shape>
</rotate>
</item>
<item android:top="20dp">
<rotate
android:fromDegrees="-45"
android:toDegrees="45">
<shape android:shape="rectangle">
<solid android:color="#F0AD4E"/>
<corners
android:radius="0dp"
android:topRightRadius="0dp"
android:topLeftRadius="0dp"/>
</shape>
</rotate>
</item>
Can anyone help me to complete the picture.
Thanks.
Don't use drawables for this, use a custom view and canvas.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Bojan on 17.5.2015.
*/
public class CircleRightArrow extends View {
private int circleColor = 0xFF505070;
private int arrowColor = 0xFF505070;
private int measuredSize;
private int strokeWidth;
private Paint mCirclePiant, mArrowPaint;
public CircleRightArrow(Context context) {
super(context);
init(context, null, 0);
}
public CircleRightArrow(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public CircleRightArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
mCirclePiant = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePiant.setColor(circleColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArrowPaint.setColor(arrowColor);
mArrowPaint.setStyle(Paint.Style.STROKE);
mArrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredSize = Math.min(measuredHeight, measuredWidth);
strokeWidth = Math.round(measuredSize * 0.05f);
mCirclePiant.setStrokeWidth(strokeWidth);
mArrowPaint.setStrokeWidth(strokeWidth);
// Make a square
setMeasuredDimension(measuredSize + strokeWidth, measuredSize + strokeWidth);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredSize <= 0) {
// Not much we can draw, can we
return;
}
float center = (measuredSize + strokeWidth) * 0.5f;
canvas.drawCircle(center, center, measuredSize * 0.5f, mCirclePiant);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center + 0.2f * measuredSize, mArrowPaint);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center - 0.2f * measuredSize, mArrowPaint);
}
}
Result of 50x50dp at 1920x1080p
Following Bojan's answer, I edited the view to redraw based on the touch events and added the OnClickListener part. Feel free to suggest any improvements.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by noorul.ahmed on 5/18/2015.
*/
public class CircleRightArrow extends View {
private int viewColor;
private int backgroundColor;
private int normalColor = 0xFFF0AD4E;
private int normalBackground = 0xFFFFFFFF;
private int touchBackground = 0xFFEC971F;
private int touchColor = 0xFFFFFFFF;
private int disabledBackground = 0xFFFFFFFF;
private int disabledColor = 0xFFCECECE;
private int measuredSize;
private int strokeWidth;
private boolean touched = false;
private float startX, startY, endX, endY;
private OnClickListener listener;
private Paint mCirclePiant, mArrowPaint;
public CircleRightArrow(Context context) {
super(context);
init(context, null, 0);
setInitialColor();
}
public CircleRightArrow(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
setInitialColor();
}
public CircleRightArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
setInitialColor();
}
private void init(Context context, AttributeSet attributeSet, int defStyle) {
mCirclePiant = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArrowPaint.setColor(viewColor);
mArrowPaint.setStyle(Paint.Style.STROKE);
mArrowPaint.setStrokeCap(Paint.Cap.ROUND);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
measuredSize = Math.min(measuredHeight, measuredWidth);
strokeWidth = Math.round(measuredSize * 0.025f);
mCirclePiant.setStrokeWidth(strokeWidth);
mArrowPaint.setStrokeWidth(strokeWidth);
// Make a square
setMeasuredDimension(measuredSize + strokeWidth, measuredSize + strokeWidth);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (measuredSize <= 0) {
// Not much we can draw, can we
return;
}
float center = (measuredSize + strokeWidth) * 0.5f;
canvas.drawCircle(center, center, measuredSize * 0.5f, mCirclePiant);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center + 0.2f * measuredSize, mArrowPaint);
canvas.drawLine(center + 0.2f * measuredSize, center, center - 0.1f * measuredSize, center - 0.2f * measuredSize, mArrowPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
touched = true;
startX = event.getX();
startY = event.getY();
togglePaintColor(event);
postInvalidate();
return true;
case MotionEvent.ACTION_UP :
endX = event.getX();
endY = event.getY();
togglePaintColor(event);
postInvalidate();
float diffX = Math.abs(startX - endX);
float diffY = Math.abs(startY - endY);
if (diffX <= 5 && diffY <= 5 && touched ) {
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,1));
}
return true;
default:
return false;
}
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_UP) {
touched = false;
if(listener != null) listener.onClick(this);
}
return super.dispatchKeyEvent(event);
}
public void setListener(OnClickListener listener) {
this.listener = listener;
}
public void setInitialColor(){
if (isClickable()){
viewColor = normalColor;
backgroundColor = normalBackground;
}
else {
viewColor = disabledColor;
backgroundColor = disabledBackground;
this.listener = null;
}
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
}
#Override
public void setClickable(boolean isClickable){
super.setClickable(isClickable);
if (isClickable){
viewColor = normalColor;
backgroundColor = normalBackground;
}
else {
viewColor = disabledColor;
backgroundColor = disabledBackground;
this.listener = null;
}
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
postInvalidate();
}
private void togglePaintColor(MotionEvent event) {
if (isClickable()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
viewColor = touchColor;
backgroundColor = touchBackground;
mCirclePiant.setColor(backgroundColor);
mCirclePiant.setStyle(Paint.Style.FILL);
mArrowPaint.setColor(viewColor);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
viewColor = normalColor;
backgroundColor = normalBackground;
mCirclePiant.setColor(viewColor);
mCirclePiant.setStyle(Paint.Style.STROKE);
mArrowPaint.setColor(viewColor);
}
}
}
}
The three button states are:
Normal State
Pressed State
Disabled state
I'm currently working with graphics and paths, and I can successufully display whatever I want.
But instead of drawing a line directly on my SurfaceView, I'd like to draw it progressively in an animation.
What I've done so far is to create a Path and then to use PathMeasure to retrieve the coordinates progressively along the path. Here is basically what I've done so far
PathMeasure pm = new PathMeasure(myPath, false);
float position = 0;
float end = pm.getLength();
float[] coord = {0,0,0,0,0,0,0,0,0};
while (position < end){
Matrix m = new Matrix();
// put the current path position coordinates into the matrix
pm.getMatrix(position, m, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
// put the matrix data into the coord array (coord[2] = x and coord[5] = y)
m.getValues(coord);
????
position += 1;
}
The question marks is where I'm stuck. I want to draw the path progressively and see it animated on the screen. I couldn't find much info about it on the internet, so any clue would be much appreciated if you have already come across the same situation. The final effect I want to create is like a pencil drawing progressively a text automatically.
Instead of creating a for loop, you can use the ObjectAnimator class to callback to one of your class's methods every time you'd like to draw a bit more of the path.
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;
import android.util.Log;
public class PathView extends View
{
Path path;
Paint paint;
float length;
public PathView(Context context)
{
super(context);
}
public PathView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public PathView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
public void init()
{
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
path.moveTo(50, 50);
path.lineTo(50, 500);
path.lineTo(200, 500);
path.lineTo(200, 300);
path.lineTo(350, 300);
// Measure the path
PathMeasure measure = new PathMeasure(path, false);
length = measure.getLength();
float[] intervals = new float[]{length, length};
ObjectAnimator animator = ObjectAnimator.ofFloat(PathView.this, "phase", 1.0f, 0.0f);
animator.setDuration(3000);
animator.start();
}
//is called by animtor object
public void setPhase(float phase)
{
Log.d("pathview","setPhase called with:" + String.valueOf(phase));
paint.setPathEffect(createPathEffect(length, phase, 0.0f));
invalidate();//will calll onDraw
}
private static PathEffect createPathEffect(float pathLength, float phase, float offset)
{
return new DashPathEffect(new float[] { pathLength, pathLength },
Math.max(phase * pathLength, offset));
}
#Override
public void onDraw(Canvas c)
{
super.onDraw(c);
c.drawPath(path, paint);
}
}
Then, just call init() to begin the animation, like this (or if you'd like it to start as soon as the view is inflated, put the init() call inside the constructors):
PathView path_view = (PathView) root_view.findViewById(R.id.path);
path_view.init();
Also see this question here, and this example, which I've based my code on.
I just have resolve this problem, here what I do:
private float[] mIntervals = { 0f, 0f };
private float drawSpeed = 2f;
private int currentPath = -1;
private PathMeasure mPathMeasure = new PathMeasure();
private ArrayList<Path> mListPath = new ArrayList<Path>(this.pathCount);
#Override
protected void onDraw(Canvas canvas) {
if (mIntervals[1] <= 0f && currentPath < (pathCount - 1)) {
// Set the current path to draw
// getPath(int num) a function to return a path.
Path newPath = this.getPath(mListPath.size());
this.mListPath.add(newPath);
this.mPathMeasure.setPath(newPath, false);
mIntervals[0] = 0;
mIntervals[1] = this.mPathMeasure.getLength();
}
if (mIntervals[1] > 0) {
// draw the previous path
int last = this.mListPath.size();
for (int i = 0; i < last; i++) {
canvas.drawPath(this.mListPath.get(i), mPaint);
}
// partially draw the last path
this.mPaint.setPathEffect(new DashPathEffect(mIntervals, 0f));
canvas.drawPath(this.mListPath.get(last), mPaint);
// Update the path effects values, to draw a little more
// on the path.
mIntervals[0] += drawSpeed;
mIntervals[1] -= drawSpeed;
super.invalidate();
} else {
// The drawing have been done, draw it entirely
for (int i = 0; i < this.mListPath.size(); i++) {
canvas.drawPath(this.mListPath.get(i), mPaint);
}
}
}
This example, is an adaptation of what I've done (to simplify the example). Hope you will understand it. Since I've just made this function working, It lacks of optimizations and things like that.
Hope it will help ;-)
here is an alternative solution that worked for me
package com.sample;
/**
* Created by Sumit
*/
public class PathView extends View {
Paint mPaint;
Path mPath;
int mStrokeColor;
float mStrokeWidth;
float mProgress = 0.0f;
float mLength = 0f;
float mTotal;
public PathView(Context context) {
this(context, null);
init();
}
public PathView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init();
}
public PathView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mStrokeColor = Color.RED;
mStrokeWidth = 8.0f;
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(mStrokeColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
setPath(new Path());
// setPath2(new Path());
}
public void setPath(Path p) {
mPath = p;
PathMeasure measure = new PathMeasure(mPath, false);
mPathLength = measure.getLength();
}
public void setPath(List<float[][]> list) {
Log.d("Path", "size " + list.size());
Path p = new Path();
p.moveTo(list.get(0)[0][0], list.get(1)[0][1]);
for (int i = 1; i < list.size(); i++) {
p.lineTo(list.get(i)[0][0], list.get(i)[0][1]);
//if (i > 100)
//p.moveTo(list.get(i)[0][0], list.get(i)[0][1]);
}
//p.setFillType(FillType.WINDING);
setPath(p);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTotal = (mLength - mLength * mProgress);
PathEffect pathEffect = new DashPathEffect(new float[] { mLength, mLength }, mTotal);
Log.d("Path Tag", "length =" + mLength + ", totla=" + mTotal);
mPaint.setPathEffect(pathEffect);
canvas.save();
// canvas.translate(getPaddingLeft(), getPaddingTop());
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(widthMeasureSpec);
int measuredWidth, measuredHeight;
if (widthMode == MeasureSpec.AT_MOST)
throw new IllegalStateException("Use MATCH_PARENT");
else
measuredWidth = widthSize;
if (heightMode == MeasureSpec.AT_MOST)
throw new IllegalStateException("Use MATCH_PARENT");
else
measuredHeight = heightSize;
setMeasuredDimension(measuredWidth, measuredHeight);
setPath();
}
void setPath() {
int cX = getWidth() / 2;
int cY = getHeight() / 2;
cY += 50;
cX -= 50;
List<float[][]> list = new ArrayList<float[][]>();
for (int i = 0; i < 50; i++) {
list.add(new float[][] { { cX--, cY++ } });
}
for (int i = 0; i < 100; i++) {
list.add(new float[][] { { cX--, cY-- } });
}
for (int i = 0; i < 100; i++) {
list.add(new float[][] { { cX++, cY-- } });
}
for (int i = 0; i < 200; i++) {
list.add(new float[][] { { cX++, cY++ } });
}
for (int i = 0; i < 100; i++) {
list.add(new float[][] { { cX++, cY-- } });
}
for (int i = 0; i < 100; i++) {
list.add(new float[][] { { cX--, cY-- } });
}
for (int i = 0; i < 100; i++) {
list.add(new float[][] { { cX--, cY++ } });
}
setPath(list);
}
}
and use
final PathView pathView = (PathView) findViewById(R.id.path_view);
pathView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "percentage", 0.0f, 1.0f);
anim.setDuration(2000);
anim.setInterpolator(new LinearInterpolator());
anim.setRepeatCount(Animation.INFINITE);
anim.start();
}
});
You will have to add this view to the layout, setting height to 1 and width to match parent. The line will be animated from left to right. The later line will be placed over the first one.
public class AnimatorLineView extends RelativeLayout {
private View animatorLineView;
private View simpleLineView;
View animatorLine;
private int colorBeforeAnimation;
private int colorAfterAnimation;
private int colorForErrorLine;
public AnimatorLineView(Context context) {
super(context);
init();
startAnimation();
}
public AnimatorLineView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
initAttributes(context, attrs);
setColors();
startAnimation();
}
public AnimatorLineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
initAttributes(context, attrs);
setColors();
startAnimation();
}
private void setColors() {
simpleLineView.setBackgroundColor(colorBeforeAnimation);
animatorLine.setBackgroundColor(colorAfterAnimation);
}
public void init() {
animatorLineView = inflate(getContext(), R.layout.ainimator_line_view, this);
animatorLine = findViewById(R.id.simple_line);
simpleLineView = findViewById(R.id.animator_line);
}
public void setColor(int color) {
animatorLine.setBackgroundColor(color);
}
public void startAnimation() {
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
}
public void showErrorLine(){
animatorLine.setBackgroundColor(colorForErrorLine);
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
}
public void hideErrorLine(){
animatorLine.setBackgroundColor(colorAfterAnimation);
animatorLine.setVisibility(View.VISIBLE);
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.enter_animation_underline);
animatorLine.startAnimation(animation);
}
private void initAttributes(Context context, AttributeSet attributeSet) {
TypedArray attr = getTypedArray(context, attributeSet, R.styleable.ProgressButton);
if (attr == null) {
return;
}
try {
colorBeforeAnimation = attr.getColor(R.styleable.AnimatorLineView_al_color_after_animation,ContextCompat.getColor(getContext(), R.color.animation_line_text_color));
colorAfterAnimation = attr.getColor(R.styleable.ProgressButton_pb_text_color, ContextCompat.getColor(getContext(), R.color.black_color));
colorForErrorLine = attr.getColor(R.styleable.ProgressButton_pb_text_color, ContextCompat.getColor(getContext(), R.color.error_msgs_text_color));
} finally {
attr.recycle();
}
}
protected TypedArray getTypedArray(Context context, AttributeSet attributeSet, int[] attr) {
return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
}
public void resetColor(){
animatorLine.setBackgroundColor(colorAfterAnimation);
animatorLine.setVisibility(View.GONE);
}
}
<animator_line_view>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View
android:id="#+id/simple_line"
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:background="#E0E0E0" />
<View
android:id="#+id/animator_line"
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:background="#000000"
android:visibility="gone" />
</FrameLayout>
<enter_animation_underline>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#integer/animator_line_duration" />
</set>
---- styles------
<style name="animator_line">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="al_color_before_animation">#E0E0E0</item>
<item name="al_color_after_animation">#0000000</item>
<item name="al_error_line_color">#FF3352</item>
</style>
<declare-styleable name="AnimatorLineView">
<attr name="al_color_before_animation" format="color" />
<attr name="al_color_after_animation" format="color" />
<attr name="al_error_line_color" format="color" />
</declare-styleable>
-------- to be include in the xml
<com.careem.acma.widget.AnimatorLineView
android:id="#+id/animator_line"
style="#style/animator_line" />