我创建了一个自定义EditText以适合我的应用程序的设计。 EditText应该有一个提示,当焦点对准时,该提示应该变成彩色并向右移动。如果不再关注焦点,则提示会回到左侧,或者,如果将文本写入EditText,它将停留在右侧,只是将颜色更改回默认的提示颜色。

这是代码,它非常完美:
(LayoutWrapContentUpdater进一步说明here

public class CustomEditText extends ConstraintLayout {

    private EditText textField;
    private TextView hint;
    private float hintSize;
    private float hintSizeFocused;
    private int hintColorFocused;
    private int hintColor;

    public CustomEditText(Context context) {
        this(context, null);
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        layoutInflater.inflate(R.layout.custom_edit_text, this, true);
        setClickable(false);
        textField = findViewById(R.id.custom_edit_text_text_field);
        textField.setOnFocusChangeListener(new OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if(hasFocus) {
                    focusHint();
                } else {
                    unfocusHint();
                }
            }
        });
        hint = findViewById(R.id.custom_edit_text_hint);
        hint.setClickable(false);

        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomEditText, defStyleAttr, 0);
        hintSize = a.getDimension(R.styleable.CustomEditText_hintSize, dpToPx(DEF_HINT_SIZE));
        hint.setTextSize(hintSize);
        hintSizeFocused = a.getDimension(R.styleable.CustomEditText_hintSizeFocused, dpToPx(DEF_HINT_SIZE_FOCUSED));
        if(a.hasValue(R.styleable.CustomEditText_hint)) {
            hint.setText(a.getText(R.styleable.CustomEditText_hint));
        }
        hintColorFocused = a.getColor(R.styleable.CustomEditText_hintColorFocused, getResources().getColor(R.color.colorPrimary));
        hintColor = a.getColor(R.styleable.CustomEditText_hintColor, getResources().getColor(R.color.def_hint_color));
        if (a.hasValue(R.styleable.CustomEditText_customBackground)) {
            Drawable background = a.getDrawable(R.styleable.CustomEditText_customBackground);
            textField.setBackground(background);
        }
        if(a.getBoolean(R.styleable.CustomEditText_isPassword, false)) {
            textField.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
            textField.setTransformationMethod(PasswordTransformationMethod.getInstance());
        }
        textField.setTextSize(a.getDimension(R.styleable.CustomEditText_textSize, dpToPx(DEF_TEXT_SIZE)));

        a.recycle();
    }

    private void focusHint() {
        hint.setTextSize(hintSizeFocused);
        LayoutWrapContentUpdater.wrapContentAgain(this);
        hint.setTextColor(hintColorFocused);
        if(textField.getText().toString().isEmpty())
            moveHintToRight();
        if(textField.getPaddingRight() == 0) {
            textField.setPadding(textField.getPaddingLeft(), textField.getPaddingTop(), hint.getWidth() + dpToPx(HINT_MARGIN_SIDE), textField.getPaddingBottom());
        }
        InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showSoftInput(textField, InputMethodManager.SHOW_IMPLICIT);
    }

    private void unfocusHint() {
        hint.setTextColor(hintColor);
        if(textField.getText().toString().isEmpty()) {
            moveHintToLeft();
            hint.setTextSize(hintSize);
            LayoutWrapContentUpdater.wrapContentAgain(this);
        }
    }

    private void moveHintToRight() {
        int horizontalDistance = textField.getWidth() - hint.getWidth() - dpToPx(HINT_MARGIN_SIDE * 2);
        TranslateAnimation anim = new TranslateAnimation(0, horizontalDistance, 0, 0);
        anim.setDuration(ANIMATION_DURATION);
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {}

            @Override
            public void onAnimationEnd(Animation animation) {
                if(textField.hasFocus()) {
                    ConstraintLayout l = findViewById(R.id.custom_edit_text_constraint_layout);
                    ConstraintSet set = new ConstraintSet();
                    set.clone(l);
                    set.clear(R.id.custom_edit_text_hint, ConstraintSet.LEFT);
                    set.connect(R.id.custom_edit_text_hint, ConstraintSet.RIGHT, R.id.custom_edit_text_constraint_layout, ConstraintSet.RIGHT, dpToPx(HINT_MARGIN_SIDE));
                    set.applyTo(l);
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {}
        });
        hint.startAnimation(anim);
    }

    private void moveHintToLeft() {

        ConstraintSet set = new ConstraintSet();
        ConstraintLayout l = findViewById(R.id.custom_edit_text_constraint_layout);
        set.clone(l);
        set.clear(R.id.custom_edit_text_hint, ConstraintSet.RIGHT);
        set.connect(R.id.custom_edit_text_hint, ConstraintSet.LEFT, R.id.custom_edit_text_constraint_layout, ConstraintSet.LEFT, dpToPx(HINT_MARGIN_SIDE));
        set.applyTo(l);
    }
}


属性:

<resources>
    <declare-styleable name="CustomEditText">
        <attr name="hintSize" format="dimension"/>
        <attr name="hintSizeFocused" format="dimension"/>
        <attr name="hint" format="string"/>
        <attr name="hintColorFocused" format="color"/>
        <attr name="hintColor" format="color"/>
        <attr name="customBackground" format="integer"/>
        <attr name="isPassword" format="boolean"/>
        <attr name="textSize" format="dimension"/>
    </declare-styleable>
</resources>


XML中的示例:

<com.workoutlog.workoutlog.views.CustomEditText
        android:layout_width="match_parent"
        android:layout_height="@dimen/textfield_height"
        app:hint="@string/hint"
        app:hintSize="@dimen/hint_size"
/>


如前所述,这是可行的。但是有一件事很奇怪。我在片段中使用这些CustomEditText。现在,当我在一个布局中包含更多这些文本时,在最后一个CustomEditText中键入一些文本,转到另一个片段,然后再次返回该片段(按下后退按钮或调用supportFragmentManager.popBackStack()),其中的每个CustomEditText当我打开新片段时,fragment片段已写了最后一个CustomEditText的文本,提示不再正常工作(文本被写在提示上,并且它们向右移动)。当我在另一个CustomEditText中键入一些文本,而在最后一个文本中没有键入文本时,则不会发生这种情况。我无法解释为什么会这样。

CustomEditText位于布局中,被放大为一个片段。
仅当在布局中的LAST CustomEditText中键入一些文本时,才会发生这种情况。

如果您需要其他代码,我可以对其进行编辑。

编辑

威灵的答案有效,谢谢!但是一件事仍然不起作用:我无法将提示向右移动。恢复状态后,提示再次位于左侧,并具有默认的textSize和textColor。我将此代码放入onRestoreInstanceState

public void onRestoreInstanceState(Parcelable state) {
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    for (int i = 0; i < getChildCount(); i++) {
        getChildAt(i).restoreHierarchyState(ss.childrenStates);
    }

    if(!textField.getText().toString().isEmpty()) {
        hint.setTextSize(hintSizeFocused);
        LayoutWrapContentUpdater.wrapContentAgain(this);
        hint.setTextColor(hintColorFocused);
        ConstraintSet set = new ConstraintSet();
        set.clone(layout);
        set.clear(R.id.custom_edit_text_hint, ConstraintSet.LEFT);
        set.connect(R.id.custom_edit_text_hint, ConstraintSet.RIGHT, R.id.custom_edit_text_constraint_layout, ConstraintSet.RIGHT, dpToPx(HINT_MARGIN_SIDE));
        set.applyTo(layout);
    }
}


颜色和尺寸会发生变化,但我不能将其放在右侧。

编辑2:

每当childrenState传递给方法时,我也会收到警告。例如:

@Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeSparseArray(childrenStates);
    }


在这里,我得到以下警告:

Unchecked assignment: 'android.util.SparseArray' to 'android.util.SparseArray<java.lang.Object>'

最佳答案

这是因为视图ID重复。看看http://trickyandroid.com/saving-android-view-state-correctly/

并将此代码添加到您的班级:

@Override
public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);
    ss.childrenStates = new SparseArray();
    for (int i = 0; i < getChildCount(); i++) {
        getChildAt(i).saveHierarchyState(ss.childrenStates);
    }
    return ss;
}

@Override
public void onRestoreInstanceState(Parcelable state) {
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    for (int i = 0; i < getChildCount(); i++) {
        getChildAt(i).restoreHierarchyState(ss.childrenStates);
    }
}

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    dispatchFreezeSelfOnly(container);
}

@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
    dispatchThawSelfOnly(container);
}

static class SavedState extends BaseSavedState {
    SparseArray childrenStates;

    SavedState(Parcelable superState) {
        super(superState);
    }

    private SavedState(Parcel in, ClassLoader classLoader) {
        super(in);
        childrenStates = in.readSparseArray(classLoader);
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        super.writeToParcel(out, flags);
        out.writeSparseArray(childrenStates);
    }

    public static final ClassLoaderCreator<SavedState> CREATOR
            = new ClassLoaderCreator<SavedState>() {
        @Override
        public SavedState createFromParcel(Parcel source, ClassLoader loader) {
            return new SavedState(source, loader);
        }

        @Override
        public SavedState createFromParcel(Parcel source) {
            return createFromParcel(source, null);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

关于java - CustomEditText的奇怪行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57993261/

10-09 10:05