Android - How to make all lines in an edittext underlined? - java

From the tutorial I have created the layout:
public static 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.STROKE);
mPaint.setColor(0x80000000);
}
#Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, r);
canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
}
super.onDraw(canvas);
}
}
<view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.bbbfr.mynotepad.NotepadText$LinedEditText"
android:id="#+id/note"
android:background="#ffd6e5"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:scrollbars="vertical"
android:fadingEdge="vertical"
android:gravity="top"
android:textSize="22sp"
android:textColor="#000000"
android:inputType="textMultiLine"
android:capitalize="sentences"
/>
This makes only the first line underlined. Is it possible to make all the lines underlined, even if there is only one line in the edtittext?
I tried changing the loop e.g. for (int i = 0; i < 5; i++) but then I receive this error:
04-28 08:29:05.093: E/AndroidRuntime(14398):
java.lang.IndexOutOfBoundsException: 2, 1 04-28 08:29:05.093:
E/AndroidRuntime(14398): at
android.text.PackedIntVector.getValue(PackedIntVector.java:70) 04-28
08:29:05.093: E/AndroidRuntime(14398): at
android.text.DynamicLayout.getLineTop(DynamicLayout.java:367) 04-28
08:29:05.093: E/AndroidRuntime(14398): at
android.text.Layout.getLineBottom(Layout.java:831) 04-28 08:29:05.093:
E/AndroidRuntime(14398): at
android.text.Layout.getLineBounds(Layout.java:437) 04-28 08:29:05.093:
E/AndroidRuntime(14398): at
android.widget.TextView.getLineBounds(TextView.java:4122) 04-28
08:29:05.093: E/AndroidRuntime(14398): at
com.bbbfr.mynotepad.NotepadText$LinedEditText.onDraw(NotepadText.java:56)
to this line: int baseline = getLineBounds(i, r);
I have also set android:lines="5" in the view.

If you don't mind the underline having the same colour as the text in the EditText, you should really just use the built-in UnderlineSpan, either by creating it yourself or indirectly through Html.fromHtml(...).
private void createUnderlinedText() {
String text = "I am underlined text\nLine #2\nLine #3\nLine #4\nLine #5";
EditText underlineSpanEditText = (EditText) findViewById(R.id.underlinespan_edittext);
SpannableStringBuilder sb = new SpannableStringBuilder(text);
sb.setSpan(new UnderlineSpan(), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
underlineSpanEditText.setText(sb);
EditText htmlUnderlineEditText = (EditText) findViewById(R.id.html_underline_edittext);
String html = "<u>I am underlined text</br>Line #2</br>Line #3</br>Line #4</br>Line #5</u>";
htmlUnderlineEditText.setText(Html.fromHtml(html));
}
The main difference with your current approach is that this will only underline the actual text, and not the whole text line. For example, if you run my code snippet, you will find that the underline does not extend to the end of the line when it's broken off by the \n or <br/>. However, depending on the behaviour your after, this may not be what you're looking for.
Edit: So if I understand you correctly, you basically want to keep drawing horizontal lines in your EditText, no matter wether there is text or not? The 'underline' part in your question was kind of misleading, since, as it turns out, that has little to do with it (in the traditional meaning of the word :)).
Anyways, you can't use getLineCount() since that will always return the number of lines that contain actual text. That would mean you would have to 'fill' any remaing space with new line characters to get the desired effect, which sounds kind of yucky... A better alternative is probably to base the drawing of horizontal lines on the total height of the EditText. A quick example, which you can obviously tweak to your own liking:
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);
}
}
The result looks like this:

As it is visible from your code, you are drawing underline for the edit text for lines from 0 to count in your for loop and count is set to int count = getLineCount();. So only the existing number of lines in the EditText will be incremented! By changing the number of count you can draw as many underlines as you want!

The simplest workaround is that you can invoke setText with only line seperators in the constructor, e.g. "\n\n\n" (You can dynamic build it based on how many lines you need), then your current LinedEditText will have the underlines as you want.

Related

How to prevent user for going new line when the current line is empty in edittext android?

I have this code for printing bullet and number in my edit text
XML File
<com.example.nutrifood.myEditText
android:id="#+id/mealIngredientsInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="true"
android:fontFamily="#font/hindmadurai_regular"
android:gravity="top"
android:inputType="textMultiLine"
android:minLines="4"
android:paddingStart="25dp"
android:paddingEnd="20dp"
android:textColor="#color/light"
android:textSize="16sp" />
myEditText.java
public class myEditText extends androidx.appcompat.widget.AppCompatEditText {
public Rect rect = new Rect();
public Paint paint = new Paint();
private myEditText editTextinput = findViewById(R.id.editTextinput);
boolean isTyping;
public myEditText(Context context, AttributeSet attrs) {
super(context, attrs);
...
editTextInput.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
if (text.length() == 0) {
isTyping = false;
return;
}
isTyping = true;
}
...
});
}
#Override
protected void onDraw(Canvas canvas) {
if (isTyping) {
int baseline = getBaseline();
for (int i = 0; i < getLineCount(); i++) {
canvas.drawText(" •", rect.left, baseline, paint);
baseline += getLineHeight();
}
}
super.onDraw(canvas);
}
}
This will give me the output like this.
• One
• Two
•
•
•
Is there a way to disable the new line in keyboard when the current line is empty?
There is no way to disable keys in the keyboard, especially not conditionally. You can use digits in the xml, but all that does is apply a textwatcher which eliminates changes that aren't what it expects, and its not conditional.
You can get what you want fairly easily though. Create a new TextWatcher with the following function:
void onTextChanged (CharSequence s,
int start,
int before,
int count) {
String newText = s.toString().replace("\n\n","\n");
if(newText.charAt(0) == '\n') {
newText = newText.substring(1);
}
if(!s.toString().equals(newText)) {
setText(newText);
}
}
Basically, that looks for any double newlines an replaces it with single newlines. Then it chops off any leading newlines. If either of those things changes the string (if any of them were found) it sets the text to that new string formed by fixing them. If not, it exits without changing the text.

Coloring TextView during a certain time period Android

Is there a way to color text in TextView or something else in Android during certain time period. So it would start off with fully white text and then the coloring would move from left to right and fill it up during a certain duration.
So for example if the duration would be 10 then the whole line should be color in 10 seconds, but it should also move with the same pace.
Which would look something like this:
There is a way to do it on IOS with CATextLayer, but I haven't yet found a way to do it on Android.
I'm just creating a self-defined TextView in last year,here is my class
public class AKChangeColorTextView extends TextView {
public AKChangeColorTextView(Context context) {
this(context,null);
}
String TAG = "AKChangeColorTextView";
public AKChangeColorTextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
RectF mRectF;
float mX;
float mY;
public AKChangeColorTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint.setXfermode(mode);
float x = 60;
float y = 10;
mY = 0;
mRectF = new RectF(x, y, x + 50, y + 50);
mTPaint = getPaint();
mX = 0;
}
Paint mPaint;
TextPaint mTPaint;
Bitmap shadowBitmap ;
Rect bounds = new Rect();
Canvas textCanvas;
#Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
if (shadowBitmap == null) {
shadowBitmap = Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}
if (textCanvas == null) {
textCanvas = new Canvas(shadowBitmap);
}
textCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
if (mTPaint == null) {
mTPaint = getPaint();
}
String content = getText().toString();
mTPaint.getTextBounds(content,0,content.length(),bounds);
textCanvas.drawText(content,0,bounds.height(),mTPaint);
mRectF.set(colorLeft,mY,colorRight,mY+bounds.height()+10);
textCanvas.drawRect(mRectF,mPaint);
canvas.drawBitmap(shadowBitmap,0,0,null);
}
float colorLeft;
float colorRight;
public void setXOffset(float left,float right){
colorLeft = left;
colorRight = right;
invalidate();
}
}
my demo code:
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
lateinit var countDownTimer:CountDownTimer
var currentOffsetx = 0
var textWidth = 0
var isIncrease = true
lateinit var txt:AKChangeColorTextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
(findViewById<View>(R.id.tv_hello) as AKChangeColorTextView).apply{
txt = this
}
countDownTimer = object:CountDownTimer(300000,200){
override fun onFinish() {
}
override fun onTick(millisUntilFinished: Long) {
if (textWidth == 0) {
textWidth = txt.width
}
if (currentOffsetx <= textWidth) {
if (isIncrease) {
currentOffsetx += 10
currentOffsetx = min(currentOffsetx, textWidth)
} else {
currentOffsetx -= 10
currentOffsetx = max(currentOffsetx, 0)
}
}
if (currentOffsetx == textWidth) {
isIncrease = false
}else if (currentOffsetx == 0) {
isIncrease = true
}
txt.setXOffset(0f,currentOffsetx.toFloat())
Log.w(TAG,"check current tick:$millisUntilFinished,offsetx:$currentOffsetx,txtWidth:$textWidth")
}
}
countDownTimer.start()
}
}
my xml layout :
<com.example.administrator.myapplication.AKChangeColorTextView
android:id="#+id/tv_hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I Found a Love For Aolphn"
android:textColor="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
following is effect
There is a way to do this optimized, simple, smooth and beautiful: Vector Animated Drawables (API21+).
There are a lot of tutorials out there (for example this video) that show how you can make beautiful animations. Basically the steps are following:
create two SVG vector images of your text, one with normal color, the other one with
colored letters. You can do this easily in Adobe Illustrator for example.
Import both into shapeshifter.design.
Create an animation for the second (colored layer) by your liking.
Copy the resulting xml file into your drawables and you're done!
Good luck!
You can use a text spannable and Foreground Color Span and animate one character at a time

How to make custom textView?

My TextViews xml file
<com.example.blabla.HateTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/hate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Text" />
Next code
public class HateTextView extends android.support.v7.widget.AppCompatTextView {
Paint mTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private String str = " ";
HateTextView(Context context, AttributeSet attrs){
super(context, attrs);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.HateTextView);
a.recycle();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint.FontMetrics fm = new Paint.FontMetrics();
mTxtPaint.setColor(Color.parseColor("#d00000"));
mTxtPaint.setTextSize(20 * getResources().getDisplayMetrics().density);
mTxtPaint.getFontMetrics(fm);
mTxtPaint.isAntiAlias();
int margin = 5;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawRoundRect(90 - margin, 100 + fm.top - margin,
115 + mTxtPaint.measureText(str) + margin, 100 + fm.bottom + margin,35,35, mTxtPaint);
}
mTxtPaint.setColor(Color.WHITE);
canvas.drawText(str, 100, 100, mTxtPaint);
}
public void setTextHate(String hate) {
str = hate;
}
}
I create something like this:
HateTextView h = itemView.findViewById(R.id.hate);
h.setTextHate("some text..");
h.setRotation(number);
just because of the rotation so many problems. Or rather because of antialias. Simply from xml getPaint.AntiAlia .. does not work.
the problem looks like this
Here I put "match_parent", and if I put "content" of the text that I'm drawing is not visible at all. How do I insert "Text" into my text instead, which I draw.
public void setTextHate(String hate) {
str = hate;
}
after set str = hate, you have to call invalidate(); to call onDraw(Canvas canvas) again.

Constantly update function's parameters

I need some help adjusting a function to be constantly updating (<- that may not make sense at first but hopefully a little more explanation will make it clear). This is going to be a long post but please bear with me.
I've got an Activity and a Custom Class. The Activity's Java file only contains a reference to the seekbar that's in it and the OnSeekBarChangeListener. The XML contains said seekbar, and a reference? to the Custom Class (not sure if it's actually called a reference). Here's the code for both:
Java:
public class Painting extends Activity
{
static SeekBar curveBar;
SampleView sampleView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_painting);
curveBar = (SeekBar)findViewById(R.id.curveBar);
sampleView = new SampleView(this);
curveBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress >= 50
{
sampleView.updatePath(progress);
}
else if (progress < 50)
{
sampleView.updatePath((-1) * progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(Painting.this, "Value: " + curveBar.getProgress(), Toast.LENGTH_SHORT).show();
}
});
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
tools:context="com.example.android.postvu.SampleView">
<com.example.android.postvu.SampleView
android:id="#+id/view"
android:layout_height="300dp"
android:layout_width="match_parent"/>
<SeekBar
android:id="#+id/curveBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/view"
android:layout_alignParentBottom="true"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:elevation="50dp"/>
</RelativeLayout>
My Custom Class is what actually draws the canvas with the curve on the top half. In this class I've got 2 constructors:
public SampleView(Context context, AttributeSet attrs)
{
super(context, attrs);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
makePath(mPath);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
public SampleView(Context context)
{
super(context);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
Log.d("LOGCAT", "" + value);
mPath = new Path();
makePath(mPath);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
AND FINALLY the 2 functions that I'm stuck on. The first is simply being called from the seekbar in the Activity and it's just so that I can get the value of the seekbar at all times while it's being moved.
public void updatePath(int seekbarProgress)
{
value = seekbarProgress * 6;
}
The second function is what draws the curve itself based on a Bezier Curve:
public void makePath(Path p)
{
// p.moveTo(250, -300);
p.moveTo(0,0);
// p.cubicTo(-250, -550, 750, -550, 250, -300);
p.cubicTo(0,-400,600,-400,600,0); //semi-circle?
// p.cubicTo(-600, -400, 600, -400, 0, 0); //as far as the curve probably should allow
// p.cubicTo(0, 0, 0, 0, 620,0); //flat line
}
MY ACTUAL QUESTION
How can I change the makePath function to take the value variable from the updatePath function so that I can make the p.cubicTo look like p.cubicTo(0,-400,600,-400,value,0) considering that I'm calling makePath in both of the constructors?
****EDIT****
The 2 constructors from my Custom Class (and relevant function) now look like this:
public SampleView(Context context, AttributeSet attrs)
{
super(context, attrs);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
// updatePath(value);
makePath(mPath, value);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
public SampleView(Context context)
{
super(context);
setFocusable(true);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(90);
mPaint.setTypeface(Typeface.SERIF);
mPath = new Path();
// updatePath(value);
makePath(mPath, value);
Log.d("LOGTAG2", "" + value);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setColor(Color.rgb(255,0,0));
mPathPaint.setStyle(Paint.Style.STROKE);
}
public int updatePath(int seekbarProgress)
{
return value = seekbarProgress * 6;
}
public void makePath(Path p, int number)
{
p.moveTo(0,0);
p.cubicTo(0, 0, 0, 0, number,0); //flat line
}
Android Studio is highlighting updatePath and when I hover over it, it tells me Return value of the method is never used. Unless I'm doing something wrong, I am using that value in both constructors where it says makePath(mPath, value);. I'm not sure what else to do at this point because it doesn't make any sense to me. I really want to know 1) how to fix it and 2) what's causing it (why it's telling me that I am not using it (which I think I am)).

Issue with stroke in text, Android

Here you can see what my problem is, I made a custom TextView in Android, to add stroke to some scores. But so far I'm having 2 separated texts instead of one with stroke...
Here is my code:
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/game_end_share_relative_main"
android:layout_width="#dimen/share_width"
android:layout_height="#dimen/share_height"
android:background="#000000" >
<com.sharing.StrokeTextView
android:id="#+id/user_share_points"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
android:textColor="#color/primary"
android:layout_marginRight="16dp"
style="#style/SecondaryFontFamily"
android:textSize="70dp" />
And the custom TextView:
public class StrokeTextView extends TextView {
private int mStrokeColor;
private int mStrokeWidth;
private TextPaint mStrokePaint;
public StrokeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public StrokeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StrokeTextView(Context context) {
super(context);
}
public void setStrokeColor(int color) {
mStrokeColor = color;
}
public void setStrokeWidth(int width) {
mStrokeWidth = width;
}
#Override
protected void onDraw(Canvas canvas) {
if (mStrokePaint == null) {
mStrokePaint = new TextPaint();
}
mStrokePaint.setTextSize(getTextSize());
mStrokePaint.setTypeface(getTypeface());
mStrokePaint.setFlags(getPaintFlags());
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setColor(mStrokeColor);
mStrokePaint.setStrokeJoin(Paint.Join.ROUND);
mStrokePaint.setStrokeCap(Paint.Cap.ROUND);
mStrokePaint.setStrokeWidth(mStrokeWidth);
mStrokePaint.setShadowLayer(2.0f, 5.0f, 5.0f, Color.BLACK);
mStrokePaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/MikadoBlack.otf"));
String text = getText().toString();
canvas.drawText(text, getWidth() - (mStrokePaint.measureText(text) / 2), getBaseline(), mStrokePaint);
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/MikadoBlack.otf"));
super.onDraw(canvas);
}
}
Thanks for your help in advance :)
Jose
This is because the drawText in super class is drawing at a different position than yours. Try setting the content gravity to 'center' using View.setGravity(Gravity.CENTER) this may solve your problem. Also, if you are using padding on the view then you need to factor it in while calculating the origin for drawText method
int hPadding = getPaddingLeft()+getPaddingRight();
int contentAreaWidth = getWidth() - hPadding;
canvas.drawText(text, contentAreaWidth - (mStrokePaint.measureText(text) / 2), getBaseline(), mStrokePaint);
This would help in aligning the stroked text with the normal text drawn in the super class.

Categories

Resources