Last active
June 24, 2017 01:00
-
-
Save Palatis/6282ec4441c475906e6f4279ba995e78 to your computer and use it in GitHub Desktop.
MultilineHintTextInputLayout :-D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="MultilineHintTextInputLayout"> | |
<!-- the "label" to be displayed when the child is focused --> | |
<!-- will be using the hint if not set. --> | |
<attr name="mlhtil_label" format="string" /> | |
<attr name="android:hint" format="string" /> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Canvas; | |
import android.graphics.Paint; | |
import android.os.Build; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.support.annotation.RequiresApi; | |
import android.support.annotation.StringRes; | |
import android.support.design.widget.TextInputLayout; | |
import android.text.TextUtils; | |
import android.util.AttributeSet; | |
import android.widget.EditText; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import tw.com.wusa.smartwatch.R; | |
/** | |
* A TextInputLayout that draws multiline text. | |
* <p> | |
* api18+ is required, because versions below use its own texture for drawing, and it calls | |
* {@code new Canvas()} which is too difficult to git rid of. | |
*/ | |
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2) | |
public class MultilineHintTextInputLayout extends TextInputLayout { | |
private static final String TAG = "MlHintTextInputLayout"; | |
private CanvasWrapper mCanvasWrapper = new CanvasWrapper(); | |
private Object mCollapsingTextHelper = null; | |
private Method mCollapsingTextHelper_draw = null; | |
private Field mHintEnabledField = null; | |
private Field mCollapsingTextHelper_mTextToDraw = null; | |
private boolean mFocused = false; | |
private CharSequence mHint = null; | |
private CharSequence mHintLabel = null; | |
public MultilineHintTextInputLayout(@NonNull Context context) { | |
this(context, null); | |
} | |
public MultilineHintTextInputLayout(@NonNull Context context, @Nullable AttributeSet attrs) { | |
this(context, attrs, 0); | |
} | |
public MultilineHintTextInputLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultilineHintTextInputLayout, defStyleAttr, 0); | |
try { | |
setHint(a.getText(R.styleable.MultilineHintTextInputLayout_android_hint)); | |
mHintLabel = a.getText(R.styleable.MultilineHintTextInputLayout_mlhtil_label); | |
} finally { | |
a.recycle(); | |
} | |
try { | |
mHintEnabledField = TextInputLayout.class.getDeclaredField("mHintEnabled"); | |
mHintEnabledField.setAccessible(true); | |
final Field field = TextInputLayout.class.getDeclaredField("mCollapsingTextHelper"); | |
field.setAccessible(true); | |
mCollapsingTextHelper = field.get(this); | |
final Class klassCollapsingTextHelper = Class.forName("android.support.design.widget.CollapsingTextHelper"); | |
//noinspection unchecked | |
mCollapsingTextHelper_draw = klassCollapsingTextHelper.getDeclaredMethod("draw", Canvas.class); | |
mCollapsingTextHelper_mTextToDraw = klassCollapsingTextHelper.getDeclaredField("mTextToDraw"); | |
mCollapsingTextHelper_mTextToDraw.setAccessible(true); | |
} catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException | ClassNotFoundException ex) { | |
mCanvasWrapper = null; | |
mCollapsingTextHelper = null; | |
mCollapsingTextHelper_draw = null; | |
mHintEnabledField = null; | |
mCollapsingTextHelper_mTextToDraw = null; | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public void setHint(@Nullable CharSequence hint) { | |
mHint = hint; | |
super.setHint(hint); | |
} | |
/** | |
* set the label to be displayed when the hint shifts to the top | |
* | |
* @param label label id from string resources | |
* @see #setLabel(int) | |
* @see #getLabel() | |
* @see #getHint() | |
* @see #setHint(CharSequence) | |
*/ | |
public void setLabel(@StringRes int label) { | |
mHintLabel = getResources().getText(label); | |
postInvalidateOnAnimation(); | |
} | |
/** | |
* set the label to be displayed when the hint shifts to the top | |
* | |
* @param label the label, null to use the same for both. | |
* @see #setLabel(int) | |
* @see #getLabel() | |
* @see #getHint() | |
* @see #setHint(CharSequence) | |
*/ | |
public void setLabel(@Nullable CharSequence label) { | |
mHintLabel = label; | |
postInvalidateOnAnimation(); | |
} | |
/** | |
* get the label that will be displayed when the hint shifts to the top | |
* | |
* @return the label, null if using the the same for both. | |
* @see #setLabel(int) | |
* @see #setLabel(CharSequence) | |
* @see #getHint() | |
* @see #setHint(CharSequence) | |
*/ | |
@Nullable | |
public CharSequence getLabel() { | |
return mHintLabel; | |
} | |
@Override | |
public void draw(Canvas canvas) { | |
if (mCollapsingTextHelper == null) { | |
super.draw(canvas); | |
return; | |
} | |
try { | |
final boolean hint = (boolean) mHintEnabledField.get(this); | |
if (hint) | |
mHintEnabledField.set(this, false); | |
super.draw(canvas); | |
if (hint) { | |
if (mHintLabel == null) { | |
mCollapsingTextHelper_mTextToDraw.set(mCollapsingTextHelper, mHint); | |
} else { | |
final EditText child = getEditText(); | |
if (child.isFocused() || !TextUtils.isEmpty(child.getText())) | |
mCollapsingTextHelper_mTextToDraw.set(mCollapsingTextHelper, mHintLabel); | |
else | |
mCollapsingTextHelper_mTextToDraw.set(mCollapsingTextHelper, mHint); | |
} | |
mHintEnabledField.set(this, true); | |
mCanvasWrapper.setCanvas(canvas); | |
mCollapsingTextHelper_draw.invoke(mCollapsingTextHelper, mCanvasWrapper); | |
mCanvasWrapper.setCanvas(null); // do not hold a ref | |
} | |
} catch (IllegalAccessException | InvocationTargetException ex) { | |
mCanvasWrapper = null; | |
mCollapsingTextHelper = null; | |
mCollapsingTextHelper_draw = null; | |
mHintEnabledField = null; | |
mCollapsingTextHelper_mTextToDraw = null; | |
// failed, try again with super only. | |
postInvalidate(); | |
} | |
} | |
private static int nextNewLineAt(@NonNull CharSequence text, int start, int end) { | |
while (start < end) { | |
if (text.charAt(start) == '\n') | |
return start; | |
++start; | |
} | |
return end; | |
} | |
private static final class CanvasWrapper extends Canvas { | |
private Canvas mCanvas = null; | |
CanvasWrapper() { | |
super(); | |
} | |
void setCanvas(Canvas canvas) { | |
mCanvas = canvas; | |
} | |
// only these one is used | |
@Override | |
public void drawText(@NonNull CharSequence text, int start, int end, float x, float y, @NonNull Paint paint) { | |
final float spacing = paint.getFontSpacing(); | |
while (start < end) { | |
final int newline = nextNewLineAt(text, start, end); | |
mCanvas.drawText(text, start, newline, x, y, paint); | |
y += spacing; | |
start = newline + 1; | |
} | |
} | |
@Override | |
public boolean isHardwareAccelerated() { | |
// if we don't check this it will crash in super()... | |
return mCanvas != null ? mCanvas.isHardwareAccelerated() : super.isHardwareAccelerated(); | |
} | |
@Override | |
public int save() { | |
return mCanvas.save(); | |
} | |
@Override | |
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { | |
mCanvas.drawRect(left, top, right, bottom, paint); | |
} | |
@Override | |
public void scale(float sx, float sy) { | |
mCanvas.scale(sx, sy); | |
} | |
@Override | |
public void restoreToCount(int saveCount) { | |
mCanvas.restoreToCount(saveCount); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment