I'm trying to draw a custom text on an ImageButton. I have overwritten the method onDraw(Canvas) but it didn't work. This is the code:
public class CustomImageButton extends ImageButton {
private static final String TAG = "CustomImageButton";
private String text = "";
private float textSize;
private int textColor;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public CustomImageButton(Context context) {
super(context);
initialize(null);
}
public CustomImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public CustomImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(attrs);
}
private void initialize(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomImageButton);
text = a.getString(R.styleable.CustomImageButton_text);
textSize = a.getDimension(R.styleable.CustomImageButton_textSize, 20);
textColor = a.getColor(R.styleable.CustomImageButton_textColor, getResources().getColor(R.color.white));
//setBackgroundColor(getResources().getColor(R.color.transparent));
a.recycle();
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
Typeface font = Typefaces.get(getContext(), "fonts/my-font.ttf");
paint.setColor(Color.WHITE);
paint.setTypeface(font);
paint.setTextSize(100);
paint.setTextAlign(Paint.Align.CENTER);
int xPos = canvas.getWidth() / 2;
int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2));
canvas.drawText(text, xPos, yPos, paint);
}
}
And XML Layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#OOOOOO"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.my.app.CustomImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:text="Hello!"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"/>
. . .
</RelativeLayout>
I used setWillNotDraw(false) but it seems that does not work. I have verified that my custom view has a few correct dimensions.
Related
Im trying to draw text on EditText below by extending it and overriding onDraw function:
As you can see, the word gets cut off, from what am I seeing online, they don't do anything on the canvas aside from drawing on it. From what I've observed, I think because the canvas of the EditText is limited, that's why it is being cut off. I know there's a better solution rather than overriding onDraw, but I want to know the reason why this is happening. Can anybody explain or give a hint? Thank you very much.
CustomEditText.java:
public class CustomEditText extends AppCompatEditText {
private Rect mTitleRect;
private Rect mErrorTextRect;
private Paint mTitlePaint;
private Paint mErrorTextPaint;
private String mTitle = "";
private String mErrorText = "";
private int mEditTextHeight;
public CustomEditText(Context context) {
super(context);
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.customEditTextStyle);
init();
init(context, attrs);
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
init(context, attrs);
}
private void init() {
mTitleRect = new Rect();
mErrorTextRect = new Rect();
mTitlePaint = new Paint();
mErrorTextPaint = new Paint();
mTitlePaint.setColor(Color.BLACK);
mTitlePaint.setTextSize(getResources().getDimension(R.dimen.text_small));
mErrorTextPaint.setColor(Color.parseColor("#FF4336"));
mErrorTextPaint.setTextSize(getResources().getDimension(R.dimen.text_small));
}
private void init(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomEditText);
try {
mTitle = a.getString(R.styleable.CustomEditText_headerTitle);
mErrorText = a.getString(R.styleable.CustomEditText_errorText);
} finally {
a.recycle();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mEditTextHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mTitle != null && !mTitle.isEmpty()) {
mTitlePaint.getTextBounds(mTitle, 0, mTitle.length(), mTitleRect);
canvas.drawText(mTitle, getPaddingLeft(), getPaddingTop() - mTitleRect.height(), mTitlePaint);
}
if (mErrorText != null && !mErrorText.isEmpty()) {
mErrorTextPaint.getTextBounds(mErrorText, 0, mErrorText.length(), mErrorTextRect);
canvas.drawText(mErrorText, getPaddingLeft(), mEditTextHeight + mErrorTextRect.height() / 2, mErrorTextPaint);
}
}
}
attrs.xml
<declare-styleable name="CustomEditText">
<attr name="errorText" format="string|reference" />
<attr name="headerTitle" format="string|reference" />
</declare-styleable>
XML:
<com.mypackage.CustomEditText
android:id="#+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Username"
app:errorText="errorrrr"
app:headerTitle="testing title" />
I think you are misinterpreting android canvas coordinate. Origin coordinate (0, 0) of a canvas is at the very left top x-coordinate is increasing as you go to the right and y-coordinate is increasing as you go to the bottom.
You need to pass left top coordinate of the text that you want to draw.
https://developer.android.com/reference/android/graphics/Canvas#drawText(java.lang.String,%20float,%20float,%20android.graphics.Paint)
I could not understand where do you want to draw the the text so assuming that you want to draw at the top left of the view have to call draw text like this
canvas.drawText(mTitle, getPaddingLeft(), getPaddingTop(), mTitlePaint);
public class CustomEditText extends AppCompatEditText {
private Rect mTitleRect;
private Rect mErrorTextRect;
private Paint mTitlePaint;
private Paint mErrorTextPaint;
private String mTitle = "";
private String mErrorText = "";
private int mEditTextHeight;
public CustomEditText(Context context) {
this(context, null);
}
public CustomEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
init(attrs);
}
private void init() {
mTitleRect = new Rect();
mErrorTextRect = new Rect();
mTitlePaint = new Paint();
mErrorTextPaint = new Paint();
mTitlePaint.setColor(Color.BLACK);
mTitlePaint.setTextSize(getResources().getDimension(R.dimen.text_small));
mErrorTextPaint.setColor(Color.parseColor("#FF4336"));
mErrorTextPaint.setTextSize(getResources().getDimension(R.dimen.text_small));
}
private void init(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomEditText);
try {
mTitle = a.getString(R.styleable.CustomEditText_headerTitle);
mErrorText = a.getString(R.styleable.CustomEditText_errorText);
} finally {
a.recycle();
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mEditTextHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setPadding(0, 0, 0, (int) getResources().getDimension(R.dimen.text_small));
if (mTitle != null && !mTitle.isEmpty()) {
mTitlePaint.getTextBounds(mTitle, 0, mTitle.length(), mTitleRect);
canvas.drawText(mTitle, getPaddingLeft(), getPaddingTop() - mTitleRect.height(), mTitlePaint);
}
if (mErrorText != null && !mErrorText.isEmpty()) {
mErrorTextPaint.getTextBounds(mErrorText, 0, mErrorText.length(), mErrorTextRect);
canvas.drawText(mErrorText, getPaddingLeft(), getHeight(), mErrorTextPaint);
}
}
i m adding diagonal cut layout to RecyclerView, but i m not getting expected result. my second view start with end of first view, and thats obvious. but what i want is that each view is join with each-other like this.
My Output:
and this is what i wanted:
CutLayout.class:
public class CutLayout extends FrameLayout {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Xfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private Path path = new Path();
public CutLayout(Context context) {
super(context);
}
public CutLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CutLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CutLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
paint.setXfermode(pdMode);
path.reset();
path.moveTo(0, getHeight());
path.lineTo(getWidth(), getHeight());
path.lineTo(getWidth(), getHeight() - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()));
path.close();
canvas.drawPath(path, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
}
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".SampleActivity">
<com.yarolegovich.slidingrootnav.sample.helper.CutLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/homebulding" />
</com.yarolegovich.slidingrootnav.sample.helper.CutLayout>
</LinearLayout>
Change your CutLayout to have the same path on top like so:
public class CutLayout extends FrameLayout {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Xfermode pdMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private Path path = new Path();
public CutLayout(Context context) {
super(context);
}
public CutLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CutLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CutLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
paint.setXfermode(pdMode);
path.reset();
path.moveTo(0, 0);
path.lineTo(getWidth(), 0);
path.lineTo(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()));
path.close();
canvas.drawPath(path, paint);
path.reset();
path.moveTo(0, getHeight());
path.lineTo(getWidth(), getHeight());
path.lineTo(getWidth(), getHeight() - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()));
path.close();
canvas.drawPath(path, paint);
canvas.restoreToCount(saveCount);
paint.setXfermode(null);
}
}
Then all you have to do is to create an item decorator for your Recycler view that can accept a negative top margin
public class NegativeTopItemDecorator extends RecyclerView.ItemDecoration{
private final int mTopMargin;
public NegativeTopItemDecorator(int topMargin) {
this.mTopMargin = topMargin;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.top = mTopMargin;
}
}
And add it to your RecyclerView
int topMargin = - (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics());
NegativeTopItemDecorator itemDecorator = new NegativeTopItemDecorator(topMargin);
mRecyclerView.addItemDecoration(itemDecorator);
This will give you something like the following output
I want to have a line between each row in TextView.
Can original TextView do this?
If not, how can I do it?
ANSWER:
Thanks to #Slartibartfast reference and advice. I made a customized TextView. And I get something like this.
This is what I want!
The code:
public class LinedTextView extends TextView {
private Rect mRect;
private Paint mPaint;
public LinedTextView(Context context) {
super(context);
initialize();
}
public LinedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public LinedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0x800000ff);
}
#Override
protected void onDraw(Canvas canvas) {
int cnt = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < cnt; i++) {
int baseLine = getLineBounds(i, r);
canvas.drawLine(r.left, baseLine + 1, r.right, baseLine + 1, paint);
}
super.onDraw(canvas);
}
}
Use the following line of code below your TextView
<View android:layout_width="fill_parent"
android:layout_height="1px"
android:background="#android:color/background_dark" />
You can configure it according to your need.
You can also use ListView with divider.
I am trying to make custom Views in android I mention all my arrtrs list in MainActivity and the XML file but I there is an error in main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews.piechart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.customviews.charting.piechart.MainActivity
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
custom:titleText="Background color"
custom:valueColor="#android:color/holo_green_light"
/>
</LinearLayout>
Do you have a constructor with no arguments?
You should add a constructor like:
public MainActivity(Context context, AttributeSet attrs) {
super(context, attrs);
}
or
public MainActivity(Context context) {
super(context);
}
You asked for a simple example of a custom view:
public class BirdView extends ImageView {
private float direction = 0;
Paint paint = new Paint();
public BirdView(Context context) {
super(context);
}
public BirdView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(6);
}
public BirdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
public void draw(Canvas canvas) {
canvas.save();
int stratX = getWidth() / 2;
int startY = getHeight() / 2;
canvas.rotate(direction, stratX, startY);
super.draw(canvas);
canvas.restore();
canvas.drawLine(stratX, startY, stratX, startY + 80, paint);
}
public void update(float dir) {
direction = dir;
invalidate();
}
}
I have extended class from ImageView and want some text to be drawn on it. This doesn't work, do you know why? Thank you.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int imgWidth = getMeasuredWidth();
int imgHeight = getMeasuredHeight();
float txtWidth = mTextPaint.measureText("your text");
int x = Math.round(imgWidth/2 - txtWidth/2);
int y = imgHeight/2 - 6; // 6 is half of the text size
canvas.drawText("your text", x, y, mTextPaint);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
mTextPaint = new Paint();
mTextPaint.setColor(android.R.color.black);
mTextPaint.setTextSize(12);
mTextPaint.setTextAlign(Paint.Align.LEFT);}
I ran your code, and immediately got a Lint error. In init() you are setting your mTextPaint to android.R.color.black. Because it's a static value, I could immediately see that the actual int value of that variable is 0x0106000c, which is almost completely transparent. You should use getResources().getColor(android.R.color.black) or plain ol' Color.BLACK.
Note that a textSize of 12 is very, very small. This code shows 12 (albeit very small).
public class MyImageView extends ImageView {
public MyImageView(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
init();
}
public MyImageView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init();
}
public MyImageView(Context context) {
super(context);
init();
}
Paint mTextPaint;
private void init() {
mTextPaint = new Paint();
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(12);
mTextPaint.setTextAlign(Paint.Align.LEFT);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int imgWidth = getMeasuredWidth();
int imgHeight = getMeasuredHeight();
float txtWidth = mTextPaint.measureText("your text");
int x = Math.round(imgWidth/2 - txtWidth/2);
int y = imgHeight/2 - 6;
canvas.drawText("12", x, y, mTextPaint);
}
}
xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.mytest.MyImageView
android:layout_width="100dp"
android:layout_height="100dp"/>
</RelativeLayout>
Copy/paste, if problem persist, start logging. Again, this code works, I see 12 on my screen.
You should be able to accomplish the desire effect without creating your own class. Just set the background of a TextView with an image drawable. See my example with a text in a speech bubble.
<TextView
android:id="#+id/textOverImage"
android:background="#drawable/speech_bubble"
android:text="#string/hello_world"
android:gravity="center"
android:layout_width="..."
android:layout_height="..."
/>