Skip to content

Instantly share code, notes, and snippets.

@alphamu
Last active February 3, 2023 18:54
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save alphamu/cd54e60c55da91578ea2 to your computer and use it in GitHub Desktop.
Save alphamu/cd54e60c55da91578ea2 to your computer and use it in GitHub Desktop.
Demonstration of how to make a custom TextView which has a separator running across it. Screenshot of TextView https://raw.githubusercontent.com/alphamu/RxAndroidDemo/master/app/SeparatorTextViewSample.png
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SeparatorTextViewFull">
<attr name="dividerResId" format="reference"/>
<attr name="dividerColor" format="color"/>
<attr name="dividerHeight" format="dimension"/>
<attr name="dividerTextPadding" format="dimension"/>
</declare-styleable>
</resources>
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.TextView;
/**
* Simple version used in the article:
* https://medium.com/@ali.muzaffar/creating-a-custom-textview-as-a-section-header-73cb8e194917
*/
public class SeparatorTextView extends TextView {
public SeparatorTextView(Context context) {
super(context);
}
public SeparatorTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SeparatorTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SeparatorTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// we use the default paint,
// our line will be the same color as the text.
Paint paint = getPaint();
//start at the vertical center of the textview
int top = (getHeight() + getPaddingTop() - getPaddingBottom())/2;
//start at the left margin
int left = getPaddingLeft();
//we draw all the way to the right
int right = getWidth() - getPaddingRight();
//we want the line to be 2 pixel thick
int bottom = top + 2;
int horizontalCenter = (getWidth() + getPaddingLeft() - getPaddingRight()) / 2;
int textWidth = (int) paint.measureText(getText().toString());
int halfTextWidth = textWidth/2;
int padding = 16;
int gravity = getGravity();
if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
canvas.drawRect(left + textWidth + padding, //left
top,
right,
bottom,
paint);
} else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
canvas.drawRect(left,
top,
right - textWidth - padding, //right
bottom,
paint);
} else {
canvas.drawRect(left,
top,
horizontalCenter - halfTextWidth - padding, //right
bottom,
paint);
canvas.drawRect(horizontalCenter + halfTextWidth + padding, //left
top,
right,
bottom,
paint);
}
}
}
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.TextView;
/**
* use along with attrs.xml
* dividerResId will be used if provided
* dividerHeight, dividerColor are only used if dividerResId is not provided.
* dividerTextPadding is the space between the lines and the text.
*/
public class SeparatorTextViewFull extends TextView {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
private Paint mDividerPaint;
int mPadding = 16;
int mDividerColor = -1;
int mDividerHeight = 2;
boolean isNinePatch = false;
public SeparatorTextViewFull(Context context) {
super(context);
init(context, null);
}
public SeparatorTextViewFull(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SeparatorTextViewFull(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SeparatorTextViewFull(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
int left = getPaddingLeft();
int top = getPaddingTop();
int bottom = getHeight() - getPaddingBottom();
if (isNinePatch || mDivider == null) {
top = (getHeight() + getPaddingTop()) / 2;
if (mDivider != null) {
bottom = top + mDivider.getIntrinsicHeight();
} else {
bottom = top + mDividerHeight;
if (mDividerHeight > 1) {
bottom -= mDividerHeight / 2;
top -= mDividerHeight / 2;
}
}
}
int right = getWidth() - getPaddingRight();
int textWidth = (int) getTextSize();
int gravity = getGravity();
if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
if (mDivider != null) {
mDivider.setBounds(left + textWidth + mPadding, top, right, bottom);
mDivider.draw(c);
} else {
c.drawRect(left + textWidth + mPadding, top, right, bottom, mDividerPaint);
}
} else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
if (mDivider != null) {
mDivider.setBounds(left, top, right - textWidth - mPadding, bottom);
mDivider.draw(c);
} else {
c.drawRect(left, top, right - textWidth - mPadding, bottom, mDividerPaint);
}
} else {
// int w = c.getWidth();
int w = getWidth() - getPaddingLeft() - getPaddingRight();
int xPos = (w / 2) - textWidth / 2;
if (mDivider != null) {
mDivider.setBounds(left, top, xPos - mPadding, bottom);
mDivider.draw(c);
mDivider.setBounds(xPos + textWidth + mPadding, top, right, bottom);
mDivider.draw(c);
} else {
c.drawRect(left, top, xPos - mPadding, bottom, mDividerPaint);
c.drawRect(xPos + textWidth + mPadding, top, right, bottom, mDividerPaint);
}
}
}
private void init(Context context, AttributeSet attrs) {
mDividerPaint = new Paint();
mDividerPaint.setColor(getPaint().getColor());
if (attrs == null) {
final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
mDivider = styledAttributes.getDrawable(0);
styledAttributes.recycle();
} else {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SeparatorTextViewFull, 0, 0);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.SeparatorTextViewFull_dividerResId:
int dividerResId = a.getResourceId(attr, 0);
if (dividerResId != 0) {
mDivider = context.getResources().getDrawable(dividerResId);
isNinePatch = mDivider instanceof NinePatchDrawable;
} else {
mDivider = null;
}
break;
case R.styleable.SeparatorTextViewFull_dividerTextPadding:
int textPadding = a.getDimensionPixelSize(attr, mPadding);
if (textPadding != mPadding) {
mPadding = textPadding;
}
break;
case R.styleable.SeparatorTextViewFull_dividerHeight:
int dividerHeight = a.getDimensionPixelSize(attr, -1);
if (dividerHeight != -1) {
mDividerHeight = dividerHeight;
} else {
mDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mDividerHeight, getResources().getDisplayMetrics());
}
break;
case R.styleable.SeparatorTextViewFull_dividerColor:
ColorStateList dividerColor = a.getColorStateList(attr);
if (dividerColor != null) {
mDividerColor = dividerColor.getColorForState(getDrawableState(), getPaint().getColor());
mDividerPaint.setColor(mDividerColor);
}
break;
}
}
a.recycle();
}
}
}
@imamsan
Copy link

imamsan commented Nov 6, 2019

Thank you. Helpfull tutorial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment