I have created below class to set the custom fonts or any otf fonts in my normal EditText.
Am working with kotlin and created below class for the generalization of using CustomEditText.
I have added fonts file in asset and created below class named CustomEditText:
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Typeface
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatEditText
class CustomEditText : AppCompatEditText{
constructor(context: Context) : this(context, null) {
init(null)
}
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
init(attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
CustomEditText(context, attrs, 0)
}
/**
* #param context:This is an abstract class whose implementation is provided by Android Operating System.
* #param attrs:A collection of attributes, as found associated with a tag in an XML document.
* #param defStyle:
*/
fun CustomEditText(
context: Context,
attrs: AttributeSet?,
defStyle: Int
) {
try {
val a: TypedArray =
context.obtainStyledAttributes(attrs, R.styleable.CustomEditText, defStyle, 0)
val customEnumValue: CustomEnumBrandonNew.CustomFontType =
CustomEnumBrandonNew.CustomFontType.fromId(
a.getInt(
R.styleable.CustomEditText_font_type,
0
)
)
a.recycle()
typeface = when (customEnumValue) {
CustomEnumBrandonNew.CustomFontType.BLACK ->
Typeface.createFromAsset(context.assets, "BrandonText-Black.otf") // BrandonTextBlack().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.BLACK_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-BlackItalic.otf") // BrandonTextBlackItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.BOLD ->
Typeface.createFromAsset(context.assets, "BrandonText-Bold.otf") // BrandonTextBold().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.BOLD_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-BoldItalic.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.LIGHT ->
Typeface.createFromAsset(context.assets, "BrandonText-Light.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.LIGHT_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-LightItalic.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.MEDIUM ->
Typeface.createFromAsset(context.assets, "BrandonText-Medium.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.MEDIUM_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-MediumItalic.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.REGULAR ->
Typeface.createFromAsset(context.assets, "BrandonText-Regular.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.REGULAR_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-RegularItalic.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.THIN ->
Typeface.createFromAsset(context.assets, "BrandonText-Thin.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
CustomEnumBrandonNew.CustomFontType.THIN_ITALIC ->
Typeface.createFromAsset(context.assets, "BrandonText-ThinItalic.otf") // BrandonTextBoldItalic().getInstance(context)?.getTypeFace()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
Before it, I have also created seperate classes i.e. BrandonTextBlack, BrandonTextBlackItalic, BrandonTextBold etc. as below :
class BrandonTextBold{
private var instance: BrandonTextBold? = null
private var typeface: Typeface? = null
fun getInstance(context: Context): BrandonTextBold? {
synchronized(BrandonTextBold::class.java) {
if (instance == null) {
typeface = Typeface.createFromAsset(context.resources.assets, "BrandonText-Bold.otf")
}
return instance
}
}
fun getTypeFace(): Typeface? {
return typeface
}
}
Now, in layout xml file, using it as below :
<CustomEditText
android:id="#+id/edt_mobile_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="#dimen/dimen_40"
android:background="#drawable/drawable_round_button_transperent"
android:hint="#string/str_hint_enter_your_mobile_number"
android:imeOptions="actionDone"
android:inputType="number|phone"
android:maxLength="10"
android:textColorHint="#color/colorHintAndBorder"
android:maxLines="1"
android:paddingBottom="#dimen/dimen_8"
android:paddingTop="#dimen/dimen_8"
android:paddingRight="#dimen/dimen_15"
android:paddingLeft="#dimen/dimen_15"
android:textColor="#color/colorBlack"
android:textSize="#dimen/font_dimen_20"
app:font_type="regular" />
Here notice that I have used app:font_type="regular". So, I can have normal font type in my edittext. It's done.
But, the issue is softkeyboard not opening when I press or try to write something in it.
What might be the issue?
Use default style as EditText style in your all constructors instead of 0
From
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
init(attrs)
}
To
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, android.R.attr.editTextStyle) {
init(attrs)
}
Related
HI I m trying show progress bar with text view for some time like . we have to show text view text for 3 second in that 3 second each second we have to display one by one with one ,two and three dot.
expected :
whole textview should show 3 second
Hello . in one second
Hello.. in 2 second
Hello...in 3 second
after that it should be hide .
I have tried with handler but unable to do this .
private fun blink() {
var iCount = 0;
val handler = Handler()
Thread {
val timeToBlink = 1000
try {
Thread.sleep(timeToBlink.toLong())
} catch (e: Exception) {
}
handler.post {
if(iCount==0)
{
iCount = 1
text = "Hello."
}
else if(iCount==1){
iCount = 2
text = "Hello.."
}
else {
Toast.makeText(context,"...",Toast.LENGTH_LONG).show()
iCount=0
text = "Hello..."
}
}
}.start()
}
Please help me with this .
Try this
class MainActivity : AppCompatActivity() {
private lateinit var tvOne: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvOne = findViewById<TextView>(R.id.tvOne)
}
override fun onResume() {
super.onResume()
tvOne.text = "Hello"
Handler(Looper.getMainLooper()).postDelayed({
tvOne.text = "Hello."
}, 1000)
Handler(Looper.getMainLooper()).postDelayed({
tvOne.text = "Hello.."
}, 2000)
Handler(Looper.getMainLooper()).postDelayed({
tvOne.text = "Hello..."
}, 3000)
Handler(Looper.getMainLooper()).postDelayed({
tvOne.visibility = View.GONE
}, 4000)
}
}
And the XML
<TextView
android:id="#+id/tvOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
TRY This once.
STEP 1 Create class TextAndAnimationView
class TextAndAnimationView : LinearLayout {
lateinit var textToShow: TextView
lateinit var animatedTextView: DotAnimatedTextView
constructor(context: Context) : super(context) {
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
showTextAndAnimation(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
showTextAndAnimation(context, attrs)
}
private fun showTextAndAnimation(context: Context, attrs: AttributeSet) {
inflate(context, R.layout.layout, this)
textToShow = this.findViewById(R.id.text_to_show)
animatedTextView = this.findViewById(R.id.progress_dots_txt)
val ta = context.obtainStyledAttributes(attrs, R.styleable.TextAndAnimationView, 0, 0)
try {
val text = ta.getText(R.styleable.TextAndAnimationView_setText)
val textHint = ta.getText(R.styleable.TextAndAnimationView_setTextHint)
val color = ta.getInt(R.styleable.TextAndAnimationView_setTextColor, 0)
val textSize = ta.getFloat(R.styleable.TextAndAnimationView_setTextSize, 0f)
val dotsCount = ta.getInt(R.styleable.TextAndAnimationView_numberOfDots, 0)
if (text != null)
setText(text)
if (textHint != null)
setTextHint(textHint)
if (color != 0)
setTextColor(color)
if (textSize != 0f)
setTextSize(textSize)
if (dotsCount != 0)
noOfDots(dotsCount)
} finally {
ta.recycle()
}
animatedTextView.showDotsAnimation()
}
fun setText(text: CharSequence) {
textToShow.text = text
}
fun setTextSize(size: Float) {
textToShow.textSize = size
animatedTextView.textSize = size
}
fun setTextHint(textHint: CharSequence) {
textToShow.setHint(textHint)
}
fun setTextColor(color: Int) {
textToShow.setTextColor(color)
animatedTextView.setTextColor(color)
}
fun stopAnimation() {
animatedTextView.stopAnimation()
}
fun noOfDots(dotsCount: Int) {
animatedTextView.noOfDots(dotsCount)
}
fun animationDelay(animationDelayTime: Long) {
animatedTextView.animationDelay(animationDelayTime)
}
}
STEP 2- Create layout.xml in res/layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/text_to_show"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end|center"
android:text="Progress"
android:textSize="12sp" />
<com.DotAnimatedTextView //ADD YOU Package NAME
android:id="#+id/progress_dots_txt"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="2dp"
android:layout_toRightOf="#id/text_to_show"
android:gravity="start|bottom"
android:maxLines="1"
android:text="..." />
</LinearLayout>
STEP 3 Create attrs.xml in res/values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TextAndAnimationView">
<attr name="setText" format="string"/>
<attr name="setTextSize" format="float"/>
<attr name="setTextHint" format="string"/>
<attr name="setTextColor" format="integer"/>
<attr name="numberOfDots" format="integer"/>
</declare-styleable>
</resources>
STEP 4 Create class- TextAndAnimationView
class TextAndAnimationView : LinearLayout {
lateinit var textToShow: TextView
lateinit var animatedTextView: DotAnimatedTextView
constructor(context: Context) : super(context) {
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
showTextAndAnimation(context, attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
showTextAndAnimation(context, attrs)
}
private fun showTextAndAnimation(context: Context, attrs: AttributeSet) {
inflate(context, R.layout.layout, this)
textToShow = this.findViewById(R.id.text_to_show)
animatedTextView = this.findViewById(R.id.progress_dots_txt)
val ta = context.obtainStyledAttributes(attrs, R.styleable.TextAndAnimationView, 0, 0)
try {
val text = ta.getText(R.styleable.TextAndAnimationView_setText)
val textHint = ta.getText(R.styleable.TextAndAnimationView_setTextHint)
val color = ta.getInt(R.styleable.TextAndAnimationView_setTextColor, 0)
val textSize = ta.getFloat(R.styleable.TextAndAnimationView_setTextSize, 0f)
val dotsCount = ta.getInt(R.styleable.TextAndAnimationView_numberOfDots, 0)
if (text != null)
setText(text)
if (textHint != null)
setTextHint(textHint)
if (color != 0)
setTextColor(color)
if (textSize != 0f)
setTextSize(textSize)
if (dotsCount != 0)
noOfDots(dotsCount)
} finally {
ta.recycle()
}
animatedTextView.showDotsAnimation()
}
fun setText(text: CharSequence) {
textToShow.text = text
}
fun setTextSize(size: Float) {
textToShow.textSize = size
animatedTextView.textSize = size
}
fun setTextHint(textHint: CharSequence) {
textToShow.setHint(textHint)
}
fun setTextColor(color: Int) {
textToShow.setTextColor(color)
animatedTextView.setTextColor(color)
}
fun stopAnimation() {
animatedTextView.stopAnimation()
}
fun noOfDots(dotsCount: Int) {
animatedTextView.noOfDots(dotsCount)
}
fun animationDelay(animationDelayTime: Long) {
animatedTextView.animationDelay(animationDelayTime)
}
}
STEP 5 - Add this code in your XML code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
<com.TextAndAnimationView //ADD YOU Package name first
android:id="#+id/bmi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
STEP 6 Add in your Activity class
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.bmi.setText("HELLO")
}
override fun onStop() {
super.onStop()
binding.bmi.stopAnimation()
}
}
This is the simplest and easiest way to achieve this.
Run this function where you want this will add a new . (dot) every second to TextView. And if the you want to terminate the process just break the operation as shown in condition
fun addDotEverySecond() {
Handler(Looper.getMainLooper()).postDelayed({
tvOne.text = tvOne.text + "."
//condition to check needs to terminate or not.
if(your condition here){
// just return from here to terminate this.
}else{
addDotEverySecond()
}
}, 1000)
}
I have a Custom View that draws a gray rectangle and a black line inside it:
class CustomViewDemo: View {
private val mRectangleGray = Path()
private val mLineBlack = Path()
private lateinit var mRectanglePaint: Paint
private lateinit var mBarPaint: Paint
private var marginLeft: Float = 0F
private var marginBottom: Float = 0F
private var marginRight: Float = 0F
private var marginTop: Float = 0F
private lateinit var mRectSize: RectF
private lateinit var mLineSize: RectF
constructor(context: Context?) : super(context) {
init(null)
}
constructor(context: Context?, list: MutableList<String>, timelineWidth: Int) : super(context) {
init(null)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(attrs)
}
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
mRectanglePaint = Paint()
mRectanglePaint.isAntiAlias = true
mRectanglePaint.color = Color.parseColor("#dedede")
mBarPaint = Paint()
mBarPaint.style = Paint.Style.FILL
mBarPaint.color = Color.BLACK
}
#SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
drawRectangle(canvas)
drawVerticalLine(canvas)
}
private fun drawRectangle(canvas: Canvas) {
marginLeft = 50F
marginRight = width - 50F
marginTop = 300F
marginBottom = 450F
mRectSize = RectF(marginLeft, marginTop, marginRight, marginBottom)
mRectangleGray.addRect(mRectSize, Path.Direction.CW)
canvas.drawPath(mRectangleGray, mRectanglePaint)
}
private fun drawVerticalLine(canvas: Canvas) {
marginLeft = 50F
marginRight = width - 50F
marginTop = 300F
marginBottom = 450F
mLineSize = RectF(marginRight - 7, marginTop, marginRight, marginBottom)
mLineBlack.addRect(mLineSize, Path.Direction.CW)
canvas.drawPath(mLineBlack, mBarPaint)
}
}
Output:
I want to move black line to right and left by touching within the borders of the gray rectangle.
However, I couldn't do this with onTouchEvent. , Drag and move a circle drawn on canvas I tried to adapt it according to the answer to this question, but instead of lines, I started drawing different black dots. Maybe I did something wrong I don't know but I tried so many times.
How can I move a Path object drawn on the canvas to the right and left in the rectangle by touching it? Thank you.
This won't give you exactly what you need, but it should be enough to get you on the right track.
Add the following onTouchEvent handler to your custom view:
private var linePositionOffset = 0f
private var startX = width - 7
override fun onTouchEvent(event: MotionEvent): Boolean {
val eventAction = event.action
val x = event.x.toInt()
when (eventAction) {
MotionEvent.ACTION_DOWN -> {
startX = x
}
MotionEvent.ACTION_UP -> {}
MotionEvent.ACTION_MOVE -> {
linePositionOffset = (startX - x).toFloat()
}
}
// tell the View to redraw the Canvas
invalidate()
// tell the View that we handled the event
return true
}
And change onDraw() to this:
override fun onDraw(canvas: Canvas) {
drawRectangle(canvas)
canvas.withTranslation(-linePositionOffset, 0f) {
drawVerticalLine(canvas)
}
}
i get this error on some devices but on others everything works.
Caused by java.lang.ClassNotFoundException
Didn't find class "android.widget.Magnifier$Builder" on path: DexPathList[[zip file
"/data/app/android.myproject-y_q4TlFhfWP/base.apk"],
nativeLibraryDirectories=[/data/app/android.myproject-y_q4TlFhfWP/lib/arm64,
/data/app/android.myproject-y_q4TlFhfWPN/base.apk!/lib/arm64-v8a, /system/lib64,
/system/vendor/lib64]]
I use custom view, crashes on initialization Magnifier
class CustomView : FrameLayout {
private var magnifier: Magnifier? = null
constructor(context: Context) : super(context) {
init(context)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(context)
}
private fun init(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
var magnifier = Magnifier.Builder(this)
.setInitialZoom(2f)
.build()
}
}
Possibly some of your test device's api version below Q(Api 29)
According to the document; https://developer.android.com/reference/android/widget/Magnifier.Builder magnifier builder added in api version 29(Q). So, either change your code to
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
or remove equality
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
and make sure your test devices api version at least 29(Q).
I'm developing an app with server-localized strings, and I want to use this localized string across my entire app.
So I came up with the following approach and would like to know if it will cause me any issues, or is there any better practice I can follow.
My Approach:
Let my android Application class implements Localizer interface defined as follows:
interface Localizer {
// get the localized string version for a given string key
fun getLocalizedValue(key: String): String?
}
class MyApplication : Application(), Localizer {
var localizedStrings:Map<String,String> = emptyMap()
override fun getLocalizedValue(key: String): String? =
localizedStrings[key]
}
In Splash Logic: fetch my localized strings from server, and fill localizedStrings with the fetched (key,value) items.
And then: use MyApplication class as a Localizer object across the app.
Following is a sample code for a custom LocalizedTextView that utilizes the 'Localizer' interface the way I mentioned:
class LocalizedTextView : AppCompatTextView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initAttrs(attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,
attrs,
defStyleAttr) {
initAttrs(attrs)
}
private fun initAttrs(attrs: AttributeSet?) {
if (attrs == null) return
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.LocalizedTextView)
val textKey = styledAttrs.getText(R.styleable.LocalizedTextView_localized_text)
text =
if (context.applicationContext is Localizer)
(context.applicationContext as Localizer).getLocalizedValue(textKey.toString())
else textKey.toString()
styledAttrs.recycle()
}
}
I've an EditText enclosed within a TextInputLayout. I wish to display errors under the EditText, but aligned to the right end of the screen.
This is what I currently have:
The error is displayed like this:
What I want it to look like:
My XML is this:
<android.support.design.widget.TextInputLayout
android:id="#+id/text_input_email"
style="#style/forgot_pass_text_inputlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tv_enter_email_message"
android:layout_marginTop="#dimen/email_padding_top"
android:hintTextAppearance="#{forgotPasswordVM.hintTextAppearance}"
android:theme="#style/forgot_pass_til_state"
app:error="#{forgotPasswordVM.email.textInputError}">
<EditText
android:id="#+id/actv_email"
style="#style/forgot_pass_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/email_address"
android:imeActionId="#+id/btn_submit"
android:imeOptions="actionSend"
android:inputType="textEmailAddress"
android:maxLines="1"
android:onEditorAction="#{forgotPasswordVM.onEditorAction}"
android:singleLine="true"
app:binding="#{forgotPasswordVM.email.text}" />
</android.support.design.widget.TextInputLayout>
I'm using data-binding.
I've tried setting android:gravity="right" and android:layout_gravity="right" on both the EditText and the TextInputLayout. Neither works the way I want it to.
I've tried setting right gravity in the theme as well as the style. Neither has any effect on the error.
I've tried right gravity on the style that is applied within app:errorTextAppearance="#style/right_error". Even this doesn't work.
I tried programmatically shifting the Error to the right by using a SpannableStringBuilder with an AlignmentSpan following this link. Even that doesn't work for me.
I'm not sure what else to try. Please suggest.
So thanks to Mike's answer I was able to figure out the solution.
I created a custom class:
public class CustomTextInputLayout extends TextInputLayout {
public CustomTextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setErrorEnabled(boolean enabled) {
super.setErrorEnabled(enabled);
if (!enabled) {
return;
}
try {
Field errorViewField = TextInputLayout.class.getDeclaredField("mErrorView");
errorViewField.setAccessible(true);
TextView errorView = (TextView) errorViewField.get(this);
if (errorView != null) {
errorView.setGravity(Gravity.RIGHT);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.END;
errorView.setLayoutParams(params);
}
}
catch (Exception e) {
// At least log what went wrong
e.printStackTrace();
}
}
}
Now I simply replaced my TextInputLayout with the CustomTextInputLayout.
Works like a charm. Thanks a lot Mike. I wish I could credit you more for this answer.
Here's a hack in Kotlin that doesn't depend on view hierarchy:
class CustomTextInputLayout #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextInputLayout(context, attrs, defStyleAttr) {
override fun setError(errorText: CharSequence?) {
super.setError(errorText)
with(findViewById<TextView>(R.id.textinput_error)) {
// this will work as long as errorView's layout width is
// MATCH_PARENT -- it is, at least now in material:1.2.0-alpha06
textAlignment = TextView.TEXT_ALIGNMENT_VIEW_END
}
}
}
My custom class is in Kotlin
class CustomTextInputLayout(context: Context, attrs: AttributeSet) :
TextInputLayout(context, attrs) {
override fun setErrorEnabled(enabled: Boolean) {
super.setErrorEnabled(enabled)
if (!enabled) {
return
}
try {
val layout = this
val errorView : TextView = ((this.getChildAt(1) as ViewGroup).getChildAt(0) as ViewGroup).getChildAt(0) as TextView
(layout.getChildAt(1) as ViewGroup).layoutParams.width = LayoutParams.MATCH_PARENT
(layout.getChildAt(1) as ViewGroup).getChildAt(0).layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT
errorView.gravity = Gravity.END
} catch (e: Exception) {
e.printStackTrace()
}
}
}
With material-componets version 1.2.0-rc01, you can create a CustomTextInputLayout that inherits from TextInputLayout to change the text alignment to the end (or where you want it to be) like this:
class CustomTextInputLayout : TextInputLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttrs: Int
) : super(context, attrs, defStyleAttrs)
override fun setErrorEnabled(enabled: Boolean) {
super.setErrorEnabled(enabled)
if (!enabled) {
return
}
try {
changeTextAlignment(
com.google.android.material.R.id.textinput_error,
View.TEXT_ALIGNMENT_VIEW_END
)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun changeTextAlignment(textViewId: Int, alignment: Int) {
val textView = findViewById<TextView>(textViewId)
textView.textAlignment = alignment
}
}
The alignment is specified in the IndicatorViewController class to always be View.TEXT_ALIGNMENT_VIEW_START. This class is used by the TextInputLayout.
From the IndicatorViewController class
void setErrorEnabled(boolean enabled) {
if (errorEnabled == enabled) {
return;
}
cancelCaptionAnimator();
if (enabled) {
errorView = new AppCompatTextView(context);
errorView.setId(R.id.textinput_error);
if (VERSION.SDK_INT >= 17) {
errorView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
}
//More code...
If selected answer not works. Another solution is below:
Create custom class as below:
public class CustomTextInputLayout extends TextInputLayout
{
public CustomTextInputLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
public void setErrorEnabled(boolean enabled)
{
super.setErrorEnabled(enabled);
if (!enabled)
{
return;
}
try
{
TextView errorView = this.findViewById(R.id.textinput_error);
FrameLayout errorViewParent = (FrameLayout) errorView.getParent();
errorViewParent.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT));
errorView.setGravity(Gravity.CENTER); // replace CENTER with END or RIGHT
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Then create your custom TextInputLayout as:
<CustomTextInputLayout
android:id="#+id/til_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/activity_horizontal_margin"
app:layout_constraintBottom_toTopOf="#+id/til_last_name">
<EditText
android:id="#+id/et_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/first_name"
android:gravity="right"
/>
</CustomTextInputLayout>
After that initialize in your code as:
private CustomTextInputLayout tilFirstName;
tilFirstName = view.findViewById(R.id.til_first_name);
Congratulations! You have done what you have required.
Based on #Emmanuel Guerra's answer. I've applied it on helperText too:
class CustomTextInputLayout #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = com.google.android.material.R.attr.textInputStyle
) : TextInputLayout(context, attrs, defStyleAttr) {
override fun setErrorEnabled(enabled: Boolean) {
super.setErrorEnabled(enabled)
if (!enabled) return
centerMiniText(com.google.android.material.R.id.textinput_error)
}
override fun setHelperTextEnabled(enabled: Boolean) {
super.setHelperTextEnabled(enabled)
if (!enabled) return
centerMiniText(com.google.android.material.R.id.textinput_helper_text)
}
private fun centerMiniText(textViewId: Int) {
try {
changeTextAlignment(textViewId, View.TEXT_ALIGNMENT_CENTER)
} catch (e: Exception) {
e.printStackTrace()
}
}
#Suppress("SameParameterValue")
private fun changeTextAlignment(textViewId: Int, alignment: Int) {
findViewById<TextView>(textViewId)?.textAlignment = alignment
}
}