I have a custom widget which is basically an EditText which has a small 'clear' button to the right, which cleans the edittext.
I built it as a subclass of LinearLayout, which adds the EditText and the Button in the constructor.
Everything works the way it should, except that I want the whole LinearLayout to have a Focused style when the EditText has focus; I want the user to see it as an EditText, not as a container with an EditText.
I attempted using addStatesFromChildren, but it does not seem to work. Code attached.
ErasableField.java
public class ErasableField extends LinearLayout {
private EditText editText;
private View button;
public ErasableField(Context context) {
super(context);
init(null);
}
public ErasableField(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ErasableField(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
this.setOrientation(HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(getContext());
editText = (EditText) inflater.
inflate(R.layout.erasable_field_edittext, this, false);
button = inflater.inflate(R.layout.erasable_field_button, this, false);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ErasableField);
boolean password = a.getBoolean(R.styleable.ErasableField_password, false);
if (password) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}
editText.setFocusable(true);
editText.setClickable(true);
editText.setFocusableInTouchMode(true);
this.setClickable(false);
this.setFocusable(false);
this.setFocusableInTouchMode(false);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setBackgroundResource(android.R.drawable.editbox_background);
addView(editText);
addView(button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
editText.getText().clear();
}
});
this.addStatesFromChildren();
}
public Editable getText() {
return editText.getText();
}
public void setText(CharSequence text) {
editText.setText(text);
}
public void setText(int resId) {
editText.setText(resId);
}
public void setText(char[] text, int start, int len) {
editText.setText(text, start, len);
}
}
erasable_field_edittext.xml
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:background="#color/white"
android:singleLine="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textCursorDrawable="#color/grey"
>
</EditText>
erasable_field_button.xml
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/Button.Delete"
>
</ImageButton>
Nevermind the missing style, it's just a red button with a little cross, it looks like this:
When the user taps the edittext area, the style should change to a focused style.
I.e. (ignore the different button look ):
The style I'm trying to use a built in from Android:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/editbox_background_focus_yellow" />
<item android:drawable="#drawable/editbox_background_normal" />
</selector>
Any help?
It's fixed by changing
this.addStatesFromChildren();
to
this.setAddStatesFromChildren(true);
...
Related
problem with making edittext scrollable
there is mentioned that it is not scrollable with singleLine=true.
So i have 2 options:
I check if the user inputs "\n" and delete that, then i have to worry about things in the second line, too.
I use a custom textviews which scrolls through in a loop, could this be used here too only for the hint ?
<com.test.ScrollingTextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginTop="1dp"
android:layout_weight="0.17"
android:background="#color/black"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:paddingLeft="10dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:text="#string/editLength"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/white" />
public class ScrollingTextView extends TextView {
public ScrollingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollingTextView(Context context) {
super(context);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (focused)
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
#Override
public void onWindowFocusChanged(boolean focused) {
if (focused)
super.onWindowFocusChanged(focused);
}
#Override
public boolean isFocused() {
return true;
}
}
I just cant get this formated right. Sorry for that.
Make your EditText Scrollable like this:
EditText yourEditText = new EditText(context);
yourEditText.setScroller(new Scroller(context));
yourEditText.setMaxLines(1);
yourEditText.setVerticalScrollBarEnabled(true);
yourEditText.setMovementMethod(new ScrollingMovementMethod());
I must create EditText with button inside and assign functions which will run after pressing the button. Then I would like to make it reusable - somehow add it as an element to my Activity. I was wondering about extending EditTextView and adding button and creating functions inside of it.
Any suggestions/ tutorials?
This is what you need to do:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionHint="yourhint"
android:dropDownHeight="match_parent"
android:hint="From"
android:padding="20dp"
android:visibility="visible"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#id/image"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
</LinearLayout>
And set OnClicklistener to the button in your OnCreate method like this:
Button image = (Button) findViewById(R.id.image);
image.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//handle your on click event here
}
});
You need to Create Custom View Components
Below links has sample and will solve your problems
https://developer.android.com/training/custom-views/index.html
https://developer.android.com/guide/topics/ui/custom-components.html
If you want some icon or button inside edit text you can achieve like this,
Note: In this just I am using setCompoundDrawablesWithIntrinsicBounds,
So if you want to change the icon position you can achieve that using
setCompoundDrawablesWithIntrinsicBounds in setIcon.
Create some CustomEditText like this,
public class MKEditText extends AppCompatEditText {
public interface IconClickListener {
public void onClick();
}
private IconClickListener mIconClickListener;
private static final String TAG = MKEditText.class.getSimpleName();
private final int EXTRA_TOUCH_AREA = 50;
private Drawable mDrawable;
private boolean touchDown;
public MKEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MKEditText(Context context) {
super(context);
}
public MKEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void showRightIcon() {
mDrawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_android_black_24dp);
setIcon();
}
public void setIconClickListener(IconClickListener iconClickListener) {
mIconClickListener = iconClickListener;
}
private void setIcon() {
Drawable[] drawables = getCompoundDrawables();
setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], mDrawable, drawables[3]);
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setSelection(getText().length());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int right = getRight();
final int drawableSize = getCompoundPaddingRight();
final int x = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right + EXTRA_TOUCH_AREA) {
touchDown = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right + EXTRA_TOUCH_AREA && touchDown) {
touchDown = false;
if (mIconClickListener != null) {
mIconClickListener.onClick();
}
return true;
}
touchDown = false;
break;
}
return super.onTouchEvent(event);
}
}
If you want to change the touch area you can change the
EXTRA_TOUCH_AREA values default I gave as 50.
And for Enable the button and click listener you can call from your Activity or Fragment like this,
MKEditText mkEditText = (MKEditText) findViewById(R.id.password);
mkEditText.showRightIcon();
mkEditText.setIconClickListener(new MKEditText.IconClickListener() {
#Override
public void onClick() {
// You can do action here. ex you can start activity here like this
startActivityForResult(new Intent(MainActivity.this, SampleActivity.class), 1);
}
});
You can extend LinearLayout or RelativeLayout and add both Button and EditText in the constructor. However, you need to use a getter method to get the EditText or Button. Here is an example. I haven't tested it, but it should give you an idea. Just remember that, if you want to use xml attributes of Button or EditText, you should define them in attr.xml and then get them with TypedArray. Since the object is a LinearLayout, you cannot use EditText or Button attributes without defining them in attr.xml.
public class CustomEditText extends LinearLayout {
private AppCompatButton mButton;
private AppCompatEditText mEditText;
public CustomEditText(Context context) {
this(context, null, 0);
}
public CustomEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOrientation(HORIZONTAL);
/** Get Attributes **/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomEditText);
int lines = typedArray.getInt(R.styleable.CustomEditText_android_lines, 1);
int gravity = typedArray.getInt(R.styleable.CustomEditText_android_gravity, Gravity.CENTER_VERTICAL);
int inputType = typedArray.getInt(R.styleable.CustomEditText_android_inputType, InputType.TYPE_CLASS_TEXT);
int imeOptions = typedArray.getInt(R.styleable.CustomEditText_android_imeOptions, EditorInfo.IME_ACTION_DONE);
String hint = typedArray.getString(R.styleable.CustomEditText_hint);
typedArray.recycle();
/** Construct Button **/
mButton = new AppCompatButton(context);
addView(mButton);
/** Construct EditText **/
mEditText = new AppCompatEditText(context);
mEditText.setGravity(gravity);
mEditText.setHint(hint);
mEditText.setImeOptions(imeOptions | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mEditText.setInputType(inputType | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
if (!((inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == InputType.TYPE_TEXT_FLAG_MULTI_LINE
&& lines == 1)) mEditText.setLines(lines);
addView(mEditText);
}
public AppCompatEditText getEditText() {
return mEditText;
}
public AppCompatButton getButton() {
return mButton;
}
}
I have layout like this -
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/form_username"/>
</android.support.design.widget.TextInputLayout>
As per our UI design document, we need to have different custom font for Floating label and Edittext.
Any help is appreciated. Thank you.
you can do this by this way,
As of Design Library v23, you can use TextInputLayout#setTypeface().
This will set the typeface on both the expanded and floating hint.
Using a custom span
final SpannableString ss = new SpannableString("Error");
ss.setSpan(new FontSpan(tf), 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
til.setError(ss);
private static final class FontSpan extends MetricAffectingSpan {
private final Typeface mNewFont;
private FontSpan(Typeface newFont) {
mNewFont = newFont;
}
#Override
public void updateDrawState(TextPaint ds) {
ds.setTypeface(mNewFont);
}
#Override
public void updateMeasureState(TextPaint paint) {
paint.setTypeface(mNewFont);
}
}
For custom fonts in EditText you can use following class:
public class CustomEditText extends AppCompatEditText {
public CustomEditText (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public CustomEditText (Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomEditText (Context context) {
super(context);
init(null);
}
Typeface myTypeface;
private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.CustomTextView);
String fontName = "Orkney Medium.otf";
if (fontName != null && !isInEditMode()) {
myTypeface = Typeface.createFromAsset(getContext().getAssets(),
fontName);
}
setTypeface(myTypeface);
a.recycle();
}
}
}
XML Code :
<com.utils.CustomEditText
android:id="#+id/edt_first_name_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#null"
android:hint="#string/hint_first_name"
android:inputType="textCapWords"
android:imeOptions="actionNext"
android:singleLine="true"
android:digits="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:paddingTop="#dimen/15dp"
android:textColor="#color/black"
android:textSize="20sp" />
For TextInputLayout custom classes does not work in all cases, But you can do something like this for optimized solution.
Put this in your Java Class :
public static void setTextInputLayoutTypeFace(Context mContext, TextInputLayout... textInputLayout) {
for (TextInputLayout til : textInputLayout) {
Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "Orkney Regular.otf");
til.setTypeface(typeface);
}
}
Call the above method using :
setTextInputLayoutTypeFace(mContext, tlFirstNameRegister, tlLastNameRegister,
tlUsernameRegister, tlEmailIdRegister, tlDateOfBirthRegister, tlMobileRegister, tlPasswordRegister,
tlConfPasswordRegister);
Its not the best solution. But it will work.
I'm trying to make a custom EditText for currency which means I need to have a prefix of it for the currency and I have to limit users' input to numbers only.
This is my custom EditText code
public OpenSansEditText(Context context, AttributeSet attrs) {
super(context, attrs);
paint = getPaint();
applyCustomFont(context, attrs);
}
public OpenSansEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = getPaint();
applyCustomFont(context, attrs);
}
private void applyCustomFont(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.OpenSansET);
...
// Prefix
String prefix = a.getString(R.styleable.OpenSansET_prefix);
if (prefix != null) {
mPrefix = prefix;
} else {
mPrefix = "";
}
// Prefix Color
int prefixColor = a.getColor(R.styleable.OpenSansET_prefixColor, 0);
if (prefix != null) {
mPrefixColor = prefixColor;
} else {
mPrefixColor = ContextCompat.getColor(context, R.color.miBlack);
}
a.recycle();
}
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mPrefix.equals("")) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mPrefix.equals("")) {
paint.setColor(mPrefixColor);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), paint);
}
}
#Override
public int getCompoundPaddingLeft() {
return mPrefix.equals("") ? super.getCompoundPaddingLeft()
: super.getCompoundPaddingLeft() + mPrefixRect.width();
}
This is how I use it in xml :
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMinRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/min"
android:textColor="#color/miBlack"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMaxRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/max"
android:inputType="number"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
Which yields :
Only the first one without inputType as number has the currency sign shown, whereas the second ET doesn't have its currency sign shown.
How to achieve currency prefix as text and still keeping inputType to numbers only for user? And I don't want to use two views, namely EditText and TextView to left of it, both inside a ViewGroup to achieve that.
For this type of scenarios I use Compound views. Please see below code for more information.
First create a layout for your custom view like below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txt_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$" />
<EditText
android:id="#+id/et_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number" />
</LinearLayout>
Now create a new class which should extends the LinearLayout. See below code.
public class OpenSansEditText extends LinearLayout {
private TextView txtPrefix;
private EditText etValue;
private String prefix = "$";
private int prefixColor = Color.BLACK;
public OpenSansEditText(Context context) {
super(context);
initializeViews(context, null);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.opensansedittext_view, this,true);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.OpenSansEditText);
prefix = a.getString(R.styleable.OpenSansEditText_prefix);
prefixColor = a.getColor(R.styleable.OpenSansEditText_prefixColor, Color.BLACK);
}
}
public CharSequence getValue(){
return etValue.getText();
}
public CharSequence getPrefix(){
return txtPrefix.getText();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
txtPrefix = (TextView) findViewById(R.id.txt_prefix);
etValue = (EditText) findViewById(R.id.et_value);
txtPrefix.setText(prefix);
txtPrefix.setTextColor(prefixColor);
}
}
And then add your attributes to attribute xml file Ex: (attrs.xml in my case)
<resources>
<declare-styleable name="OpenSansEditText">
<attr name="prefix" format="string"/>
<attr name="prefixColor" format="color"/>
</declare-styleable>
</resources>
Now you can use it anywhere in the project as below
<com.asta.www.classes.OpenSansEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:prefix="$"
app:prefixColor="#f00"/>
Hope this will help you to solve your problem. Thanks...
In the end I found this link https://gist.github.com/kennydude/5407963 which helps me in the right direction. So what it does is I think making the prefix as Drawable using this class :
private class TagDrawable extends Drawable {
public String text = "";
public void setText(String s){
text = s;
// Tell it we need to be as big as we want to be!
setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
invalidateSelf();
}
#Override
public void draw(#NonNull Canvas canvas) {
// I don't know why this y works here, but it does :)
// (aka if you are from Google/are Jake Wharton and I have done it wrong, please tell me!)
canvas.drawText( text, 0, mLine0Baseline + canvas.getClipBounds().top, mTextPaint );
}
#Override public void setAlpha(int i) {}
#Override public void setColorFilter(ColorFilter colorFilter) {}
#Override public int getOpacity() {return PixelFormat.UNKNOWN;}
#Override public int getIntrinsicHeight (){
return (int)mFontHeight;
}
#Override public int getIntrinsicWidth(){
return (int)mTextPaint.measureText( text );
}
}
And draw it to the left of the TextView like
TagDrawable left = new TagDrawable();
left.setText("$");
setCompoundDrawablesRelative(left, null, null, null);
The link I supplied even has suffix support which I haven't tried.
problem with making edittext scrollable
there is mentioned that it is not scrollable with singleLine=true.
So i have 2 options:
I check if the user inputs "\n" and delete that, then i have to worry about things in the second line, too.
I use a custom textviews which scrolls through in a loop, could this be used here too only for the hint ?
<com.test.ScrollingTextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginTop="1dp"
android:layout_weight="0.17"
android:background="#color/black"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:paddingLeft="10dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:text="#string/editLength"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/white" />
public class ScrollingTextView extends TextView {
public ScrollingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollingTextView(Context context) {
super(context);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (focused)
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
#Override
public void onWindowFocusChanged(boolean focused) {
if (focused)
super.onWindowFocusChanged(focused);
}
#Override
public boolean isFocused() {
return true;
}
}
I just cant get this formated right. Sorry for that.
Make your EditText Scrollable like this:
EditText yourEditText = new EditText(context);
yourEditText.setScroller(new Scroller(context));
yourEditText.setMaxLines(1);
yourEditText.setVerticalScrollBarEnabled(true);
yourEditText.setMovementMethod(new ScrollingMovementMethod());