Excluding word wrapped lines?
What I want to do is have a class that extends EditText draw line numbers to the left of each line. On it's face this is quite simple by iterating lines 0 through super.getLineCount() and drawing an index at the start of a line.
But what I'm not seeing a simple say to do; skip over lines that are wrapped (lines that don't end with a \n). For instance in vim it would like:
What I'm using to do the above scenario (line count not skipping wrapped lines)
protected void onDraw(Canvas canvas) {
...
int count = getLineCount();
for (int i = 0; i < count; ++i) {
getLineBounds(i, mRect);
String num = String.valueOf(i + 1);
mPaint.getTextBounds(num, 0, num.length(), mNumberRect);
canvas.drawText(num, NUMBER_LEFT_PAD, mRect.top + mTextHeight - 5, mPaint);
}
...
super.onDraw(canvas);
}
How would I detect which lines are word wrapped lines and which are line-broken lines?
Going off what Mike said, you can accomplish this by checking each rendered line to see if it's an actual line (preceded by a line break \n) or a word-wrapped line and only incrementing/drawing line numbers if it's an actual line.
protected void onDraw(Canvas canvas) {
int count = getLineCount();
int lineNumber = 1;
for (int i = 0; i < count; ++i) {
getLineBounds(i, mRect);
String num = String.valueOf(i + 1);
mPaint.getTextBounds(num, 0, num.length(), mNumberRect);
if (i == 0) {
canvas.drawText(num, NUMBER_LEFT_PAD, mRect.top + mTextHeight - 5, mPaint);
++lineNumber;
} else if (getText().charAt(getLayout().getLineStart(i) - 1) == '\n') {
canvas.drawText(num, NUMBER_LEFT_PAD, mRect.top + mTextHeight - 5, mPaint);
++lineNumber;
}
}
super.onDraw(canvas);
}
Improvising the code in #Lrdwhyt answer, I implemented subclass of EditText like this. It also sets left padding of EditText according to the line number text width.
public class LineNumberedEditText extends EditText
{
private final Context context;
private Rect rect;
private Paint paint;
public LineNumberedEditText(Context context)
{
super(context);
this.context = context;
init();
}
public LineNumberedEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
init();
}
public LineNumberedEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
this.context=context;
init();
}
private void init()
{
rect = new Rect();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GRAY);
paint.setTextSize(20);
paint.setTypeface(Typeface.MONOSPACE);
}
#Override
protected void onDraw(Canvas canvas) {
int baseline;
int lineCount = getLineCount();
int lineNumber = 1;
for (int i = 0; i < lineCount; ++i)
{
baseline=getLineBounds(i, null);
if (i == 0)
{
canvas.drawText(""+lineNumber, rect.left, baseline, paint);
++lineNumber;
}
else if (getText().charAt(getLayout().getLineStart(i) - 1) == '\n')
{
canvas.drawText(""+lineNumber, rect.left, baseline, paint);
++lineNumber;
}
}
// for setting edittext start padding
if(lineCount<100)
{
setPadding(40,getPaddingTop(),getPaddingRight(),getPaddingBottom());
}
else if(lineCount>99 && lineCount<1000)
{
setPadding(50,getPaddingTop(),getPaddingRight(),getPaddingBottom());
}
else if(lineCount>999 && lineCount<10000)
{
setPadding(60,getPaddingTop(),getPaddingRight(),getPaddingBottom());
}
else if(lineCount>9999 && lineCount<100000)
{
setPadding(70,getPaddingTop(),getPaddingRight(),getPaddingBottom());
}
super.onDraw(canvas);
}
}
I recommend it.(draw line numbers in edittext)
#Override
protected void onDraw(Canvas canvas) {
int baseline = getBaseline()
for (int i = 0; i < getLineCount(); i++) {
canvas.drawText("" + (i+1), rect.left, baseline, paint);
baseline += getLineHeight();
}
super.onDraw(canvas);
}
Related
I am trying to create a custom view that has a Circle and in it, I have to have sections in run time as shown in the image below. I tried a lot of stuff in onDraw method but got no luck. I even tried https://github.com/donvigo/CustomProgressControls . Basically, I want to give a number of sections and then in each section I can select colors as per my need.
I am looking for ProgressBar that should have gap/space as shown in the image; in between circles. Say if I have given 5 sections, 3 of which should be "full", it should color the first 3 in red, and the other 2 in green, for example.
To draw I am doing like:
private void initExternalCirclePainter() {
internalCirclePaint = new Paint();
internalCirclePaint.setAntiAlias(true);
internalCirclePaint.setStrokeWidth(internalStrokeWidth);
internalCirclePaint.setColor(color);
internalCirclePaint.setStyle(Paint.Style.STROKE);
internalCirclePaint.setPathEffect(new DashPathEffect(new float[]{dashWith, dashSpace}, dashSpace));
}
I might be a little late to the party, but I actually wrote a custom component that has 2 rings that look quite similar to what you're trying to achieve. You can just remove the outer ring easily. The image of what I got in the end:
Here's the class:
public class RoundedSectionProgressBar extends View {
// The amount of degrees that we wanna reserve for the divider between 2 sections
private static final float DIVIDER_ANGLE = 7;
public static final float DEGREES_IN_CIRCLE = 360;
public static final int PADDING = 18;
public static final int PADDING2 = 12;
protected final Paint paint = new Paint();
protected final Paint waitingPaint = new Paint();
protected final Paint backgroundPaint = new Paint();
private int totalSections = 5;
private int fullSections = 2;
private int waiting = 3; // The outer ring. You can omit this
private RectF rect = new RectF();
public RoundedSectionProgressBar(Context context) {
super(context);
init(context, null);
}
public RoundedSectionProgressBar(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RoundedSectionProgressBar(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
// Can come from attrs if need be?
int strokeWidth = 3;
setupPaint(context, strokeWidth, paint, R.color.filled_color_inner_ring);
setupPaint(context, strokeWidth, waitingPaint, R.color.empty_color_inner_ring);
setupPaint(context, strokeWidth, backgroundPaint, R.color.filled_color_outer_ring);
}
private void setupPaint(Context context, int strokeWidth, Paint backgroundPaint, int colorRes) {
backgroundPaint.setStrokeCap(Paint.Cap.SQUARE);
backgroundPaint.setColor(context.getResources().getColor(colorRes));
backgroundPaint.setAntiAlias(true);
backgroundPaint.setStrokeWidth(strokeWidth);
backgroundPaint.setStyle(Paint.Style.STROKE);
}
public int getTotalSections() {
return totalSections;
}
public void setTotalSections(int totalSections) {
this.totalSections = totalSections;
invalidate();
}
public int getFullSections() {
return fullSections;
}
public void setNumberOfSections(int fullSections, int totalSections, int waiting) {
this.fullSections = fullSections;
this.totalSections = totalSections;
this.waiting = waiting;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rect.set(getLeft() + PADDING, getTop() + PADDING, getRight() - PADDING, getBottom() - PADDING);
float angleOfSection = (DEGREES_IN_CIRCLE / totalSections) - DIVIDER_ANGLE;
// Drawing the inner ring
for (int i = 0; i < totalSections; i++) {
// -90 because it doesn't start at the top, so rotate by -90
// divider_angle/2 especially in 2 sections, it's visibly rotated by Divider angle, so we split this between last and first
float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
if (i < fullSections) {
canvas.drawArc(rect, startAngle, angleOfSection, false, paint);
} else {
canvas.drawArc(rect, startAngle, angleOfSection, false, backgroundPaint);
}
}
// Drawing the outer ring
rect.set(getLeft() + PADDING2, getTop() + PADDING2, getRight() - PADDING2, getBottom() - PADDING2);
for (int i = 0; i < waiting; i++) {
float startAngle = -90 + i * (angleOfSection + DIVIDER_ANGLE) + DIVIDER_ANGLE / 2;
canvas.drawArc(rect, startAngle, angleOfSection, false, waitingPaint);
}
}
}
Notice that this code won't give you the outer ring's 'empty' slots, since we decided against them in the end. The inner circle will have both the empty and filled slots. The whole class can be reused, and it's responsible just for the 2 rings that are drawn, the 6/6, +3 and the red circle are parts of another view.
The most important piece of the code is the onDraw method. It contains the logic for drawing the arcs in the for loop, as well as the logic for calculating the angles and adding spaces between them. Everything is rotated by -90 degrees, because I needed it to start at the top, rather than on the right, as it is the 0-degree angle in Android. It's not that complex, and you can modify it to fit your needs better should you need to.
I find it easier to do math for drawArc(operating on angle values based on number of sections) rather than computing the arc length.
Here's a quick idea, with a lot of hard-coded properties, but you should be able to get the idea:
public class MyStrokeCircleView extends View {
private Paint mPaint;
private RectF mRect;
private int mPadding;
private int mSections;
private int mFullArcSliceLength;
private int mColorArcLineLength;
private int mArcSectionGap;
public MyStrokeCircleView(Context context) {
super(context);
init(null, 0);
}
public MyStrokeCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MyStrokeCircleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
mPaint = new Paint();
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
mPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.darker_gray));
mPadding = 5;
mRect = new RectF(mPadding, mPadding, mPadding, mPadding);
mSections = 4;
mFullArcSliceLength = 360 / mSections;
mArcSectionGap = mFullArcSliceLength / 10;
mColorArcLineLength = mFullArcSliceLength - 2 * mArcSectionGap;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mRect.right = getWidth() - mPadding;
mRect.bottom = getHeight() - mPadding;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mSections; i++) {
canvas.drawArc(mRect, i * mFullArcSliceLength + mArcSectionGap, mColorArcLineLength, false, mPaint);
}
}
}
I was taking a look at the notepad sample in the android SDK see here: http://developer.android.com/resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html
Thing is it only draws the current line the cursor is on e.g http://cdn2.staztic.com/screenshots/simple-notepad-app-al-1.jpg
But I'd like to display lines that fill up the screen e.g. http://www.itismyworld.info/wp-content/uploads/2010/03/AK-notebook.png
Any suggestions would be great. The relevent bit of code seems to be here:
protected void onDraw(Canvas canvas) {
// Gets the number of lines of text in the View.
int count = getLineCount();
// Gets the global Rect and Paint objects
Rect r = mRect;
Paint paint = mPaint;
/*
* Draws one line in the rectangle for every line of text in the EditText
*/
for (int i = 0; i < count; i++) {
// Gets the baseline coordinates for the current line of text
int baseline = getLineBounds(i, r);
/*
* Draws a line in the background from the left of the rectangle to the right,
* at a vertical position one dip below the baseline, using the "paint" object
* for details.
*/
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
// Finishes up by calling the parent method
super.onDraw(canvas);
}
This is the code, based on jkhouws1's suggestion and google's note editor
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(R.color.edit_note_line); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
In Eclipse IDE press Ctrl+Shift+O to add all needed imports
I think this is what you need:
public class LinedEditText extends EditText {
private static Paint linePaint;
static {
linePaint = new Paint();
linePaint.setColor(Color.BLACK);
linePaint.setStyle(Style.STROKE);
}
public LinedEditText(Context context, AttributeSet attributes) {
super(context, attributes);
}
#Override
protected void onDraw(Canvas canvas) {
Rect bounds = new Rect();
int firstLineY = getLineBounds(0, bounds);
int lineHeight = getLineHeight();
int totalLines = Math.max(getLineCount(), getHeight() / lineHeight);
for (int i = 0; i < totalLines; i++) {
int lineY = firstLineY + i * lineHeight;
canvas.drawLine(bounds.left, lineY, bounds.right, lineY, linePaint);
}
super.onDraw(canvas);
}
}
maybe after that for loop, you draw estimated* additional lines.
getHeight() will return EditText's height in pixels
getLineHeight() will height of one standard line
so getHeight/getlineHeight-getCount will be number of lines left to draw.
you can't use getLineBounds, using the above functions you could calculate the position of the remaining lines to draw.
*Estimated since formatting of text could change the line height, but since there is no text in these lines yet that shouldnt be an issue. But for that same reason you should only draw the remaining lines, and not use this to draw all the lines.
<com.example.goh2.pronoornotepad.LinedEditText
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffcc4b"
android:gravity="top|left"
android:singleLine="false"
android:text=""
/>
The above XML works with the code from Max4ever's answer:
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// we need this constructor for LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(R.color.edit_note_line); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();//for long text with scrolling
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
}
Is there a method to draw text with in a specified rectangle?
I am drawing directly to a canvas(ImageView) using
canvas.drawText(text,x,y,paint)
But this drew the entire text in a single line. I want to wrap the text with in the specified (x,y) ,(x1,y1) limit. I don't want to use textviews or any other views.
I just want to draw the text over an image.
Is there any method to do this?
Thanks in Advance
Firstly you have to determine the text size. The width of each character could be get by getTextWidths(), the height is same with text size. Try to estimate a initial text size, and use the height and width of text to adjust the final value.
Secondly you need to break lines. Paint.getTextWidths() or Paint.breakText() can all achieve this target.
Edit: add the code example.
public static class RectTextView extends View {
private int mWidth = 200;
private int mHeight = 100;
private String mText = "Hello world. Don't you know why, why you and I.";
private Paint mPaint;
private List<Integer> mTextBreakPoints;
public RectTextView(Context context) {
this(context, null);
}
public RectTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPaint.setAntiAlias(true);
setSuitableTextSize();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mWidth, mHeight);
}
#Override
protected void onDraw(Canvas canvas) {
int start = 0;
int x = 0;
int y = 0;
for (int point : mTextBreakPoints) {
y += mPaint.getTextSize();
canvas.drawText(mText, start, point, x, y, mPaint);
start = point;
}
}
private void setSuitableTextSize() {
int textSize = getEstimateTextSize();
for (; textSize > 0; textSize--) {
if (isTextSizeSuitable(textSize))
return;
}
}
private boolean isTextSizeSuitable(int size) {
mTextBreakPoints = new ArrayList<Integer>();
mPaint.setTextSize(size);
int start = 0;
int end = mText.length();
while (start < end) {
int len = mPaint.breakText(mText, start, end, true, mWidth,
null);
start += len;
mTextBreakPoints.add(start);
}
return mTextBreakPoints.size() * size < mHeight;
}
private int getEstimateTextSize() {
return (int) Math.sqrt(mWidth * mHeight / mText.length() * 2);
}
}
I am creating an application, in which a camera is showing with an option on screen to hide and show grids on the screen. I created this grid by CustomView and it was working fine. but when I hide the grid and click show then horizontal lines are not showing... please help...
custom view
<com.***.GridLinesView
android:id="#+id/cameraView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
OnClick Hide/Unhide Button
#Override
public void onClick(View v) {
// get an image from the camera
View grid = (View)findViewById(R.id.cameraView1);
if(grid.getVisibility() == View.VISIBLE)
grid.setVisibility(View.INVISIBLE);
else
grid.setVisibility(View.VISIBLE);
}
Custom View
public class GridLinesView extends View {
private Paint p;
int width = 0;
int height = 0;
int pass = 0;
int xpos = 0;
int ypos = 0;
public GridLinesView(Context context) {
super(context);
// TODO Auto-generated constructor stub
p = new Paint();
p.setColor(Color.WHITE);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1);
p.setAntiAlias(true);
}
public GridLinesView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
p = new Paint();
p.setColor(Color.WHITE);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1);
p.setAntiAlias(true);
}
public GridLinesView(Context context, AttributeSet attrs) {
super(context, attrs);
p = new Paint();
p.setColor(Color.WHITE);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(1);
p.setAntiAlias(true);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
width = getWidth();
height = getHeight();
int ratio = 5;
if(width < 300 || height < 300){
ratio = 3;
}
xpos = width / ratio;
ypos = height / ratio;
p.setStyle(Style.STROKE);
for (int i = 0; i < ratio; i++) {
p.setColor(Color.argb(100, 255, 255, 255));
canvas.drawLine(0, (ypos * pass) + ratio, width, (ypos * pass) + ratio, p);
pass++;
}
for (int i = 0; i < ratio; i++) {
p.setColor(Color.argb(100, 255, 255, 255));
canvas.drawLine(xpos + (xpos * i), 0, xpos + (xpos * i), height, p);
}
}
}
Any help will be appreciable... Thank you.
I want to make a note editor. It will have an EditText with lines. Here is my code:
LinedEditText.java
public class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
public LinedEditText(Context context) {
super(context);
// TODO Auto-generated constructor stub
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLUE); //SET YOUR OWN COLOR HERE
}
#Override
protected void onDraw(Canvas canvas) {
//int count = getLineCount();
int height = getHeight();
int line_height = getLineHeight();
int count = height / line_height;
if (getLineCount() > count)
count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
int baseline = getLineBounds(0, r);//first line
for (int i = 0; i < count; i++) {
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
baseline += getLineHeight();//next line
}
super.onDraw(canvas);
}
TextEditorActivity.java
public class TextEditorActivity extends Activity {
private LinedEditText text;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.text=new LinedEditText(getApplicationContext());
this.setContentView(text);
}
}
And result:
result http://bekirmavus.com/k-resimler/image/android/device-2012-07-17-222950.png
What mistake am i making?
Thanks...
The line are being drawn from the position of the cursor on down. Simply move the cursor's default position from vertically centered, to the top-left, with Gravity:
text.setGravity(Gravity.NO_GRAVITY);
Or set it by default for all LinedEditTexts:
public LinedEditTexts(Context context) {
super(context);
setGravity(Gravity.NO_GRAVITY); // or Gravity.TOP | Gravity.LEFT
...
}