移動開發----biu,biu,一個有趣的EditText
阿新 • • 發佈:2018-12-13
BiuEditText
biu,biu,一個有趣的EditText
直接看效果
and
Usage
Step 1
三個類: ONE(主VIEW):package me.james.biuedittext; import static android.content.ContentValues.TAG; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Random; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.EditText; import android.widget.TextView; import com.example.bigsmalltext.R; /** * Created by james on 22/11/15. */ public class BiuEditText extends EditText { private ViewGroup contentContainer; private int height; private String cacheStr = ""; private static final int ANIMATION_DEFAULT = 0; private static final int ANIMATION_DROPOUT = 1; private static final int DEFAULT_DURATION = 600; private static final float DEFAULT_SCALE = 1.2f; private int biuTextColor; private float biuTextStartSize; private float biuTextScale; private int biuDuration; private int biuType; public BiuEditText(Context context) { super(context); } public BiuEditText(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); setlistener(); } public BiuEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { if (isInEditMode()) return; if (null == attrs) { throw new IllegalArgumentException("Attributes should be provided to this view,"); } final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BiuEditStyle); biuTextColor = typedArray.getColor(R.styleable.BiuEditStyle_biu_text_color, getResources().getColor(R.color.white)); biuTextStartSize = typedArray.getDimension(R.styleable.BiuEditStyle_biu_text_start_size, getResources().getDimension(R.dimen.biu_text_start_size)); biuTextScale = typedArray.getFloat(R.styleable.BiuEditStyle_biu_text_scale, DEFAULT_SCALE); biuDuration = typedArray.getInt(R.styleable.BiuEditStyle_biu_duration, DEFAULT_DURATION); biuType = typedArray.getInt(R.styleable.BiuEditStyle_biu_type, 0); typedArray.recycle(); contentContainer = (ViewGroup) ((Activity) getContext()).findViewById(android.R.id.content); WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); height = windowManager.getDefaultDisplay().getHeight(); } private void setlistener() { addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (cacheStr.length() < s.length()) { char last = s.charAt(s.length() - 1); update(last, false); } else if (cacheStr.length() >= 1) { char last = cacheStr.charAt(cacheStr.length() - 1); update(last, true); } cacheStr = s.toString(); } @Override public void afterTextChanged(Editable s) { } }); } private void update(char last, boolean isOpposite) { final TextView textView = new TextView(getContext()); textView.setTextColor(biuTextColor); textView.setTextSize(biuTextStartSize); textView.setText(String.valueOf(last)); textView.setGravity(Gravity.CENTER); contentContainer.addView(textView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); textView.measure(0, 0); playAnaimator(textView, isOpposite, new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { contentContainer.removeView(textView); } }); } private void playAnaimator(TextView textView, boolean isOpposite, AnimatorListenerAdapter listenerAdapter) { switch (biuType) { case ANIMATION_DEFAULT: playFlyUp(textView, isOpposite, listenerAdapter); break; case ANIMATION_DROPOUT: playFlyDown(textView, isOpposite, listenerAdapter); break; default: break; } } private void playFlyDown(TextView textView, boolean isOpposite, AnimatorListenerAdapter listenerAdapter) { float startX = 0; float startY = 0; float endX = 0; float endY = 0; float[] coordinate = getCursorCoordinate(); Log.i("測試資料1", "X" + coordinate[0] + "Y" + coordinate[1]); if (isOpposite) { endX = new Random().nextInt(contentContainer.getWidth()); endY = 0; startX = coordinate[0]; startY = coordinate[1]; } else { startX = coordinate[0]; startY = -100; endX = startX; endY = coordinate[1]; } final AnimatorSet animSet = new AnimatorSet(); ObjectAnimator animX = ObjectAnimator.ofFloat(textView, "translationX", startX, endX); ObjectAnimator translationY = ObjectAnimator.ofFloat(textView, "translationY", startY, endY); translationY.setEvaluator(new BounceEaseOut(biuDuration)); animSet.setDuration(biuDuration); animSet.addListener(listenerAdapter); animSet.playTogether(translationY, animX); animSet.start(); } private void playFlyUp(TextView textView, boolean isOpposite, AnimatorListenerAdapter listenerAdapter) { float startX = 0; float startY = 0; float endX = 0; float endY = 0; float[] coordinate = getCursorCoordinate(); if (isOpposite) { endX = new Random().nextInt(contentContainer.getWidth()); endY = height / 3 * 2; startX = coordinate[0]; startY = coordinate[1]; } else { startX = coordinate[0]; startY = height / 3 * 2; endX = startX; endY = coordinate[1]; } final AnimatorSet animSet = new AnimatorSet(); ObjectAnimator animX = ObjectAnimator.ofFloat(textView, "translationX", startX, endX); ObjectAnimator animY = ObjectAnimator.ofFloat(textView, "translationY", startY, endY); ObjectAnimator scaleX = ObjectAnimator.ofFloat(textView, "scaleX", 1f, biuTextScale); ObjectAnimator scaleY = ObjectAnimator.ofFloat(textView, "scaleY", 1f, biuTextScale); animY.setInterpolator(new DecelerateInterpolator()); animSet.setDuration(biuDuration); animSet.addListener(listenerAdapter); animSet.playTogether(animX, animY, scaleX, scaleY); animSet.start(); } /** * @return the coordinate of cursor. x=float[0]; y=float[1]; * * thanks @covetcode for this beautiful method */ private float[] getCursorCoordinate() { /* *以下通過反射獲取游標cursor的座標。 * 首先觀察到TextView的invalidateCursorPath()方法,它是游標閃動時重繪的方法。 * 方法的最後有個invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, bounds.right + horizontalPadding, bounds.bottom + verticalPadding); *即游標重繪的區域,由此可得到游標的座標 * 具體的座標在TextView.mEditor.mCursorDrawable裡,獲得Drawable之後用getBounds()得到Rect。 * 之後還要獲得偏移量修正,通過以下三個方法獲得: * getVerticalOffset(),getCompoundPaddingLeft(),getExtendedPaddingTop()。 * */ int xOffset = 0; int yOffset = 0; Class<?> clazz = EditText.class; clazz = clazz.getSuperclass(); try { Field editor = clazz.getDeclaredField("mEditor"); editor.setAccessible(true); Object mEditor = editor.get(this); Class<?> editorClazz = Class.forName("android.widget.Editor"); Field drawables = editorClazz.getDeclaredField("mCursorDrawable"); drawables.setAccessible(true); Drawable[] drawable = (Drawable[]) drawables.get(mEditor); Method getVerticalOffset = clazz.getDeclaredMethod("getVerticalOffset", boolean.class); Method getCompoundPaddingLeft = clazz.getDeclaredMethod("getCompoundPaddingLeft"); Method getExtendedPaddingTop = clazz.getDeclaredMethod("getExtendedPaddingTop"); getVerticalOffset.setAccessible(true); getCompoundPaddingLeft.setAccessible(true); getExtendedPaddingTop.setAccessible(true); if (drawable != null) { Rect bounds = drawable[0].getBounds(); Log.d(TAG, bounds.toString()); xOffset = (int) getCompoundPaddingLeft.invoke(this) + bounds.left; } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } float x = this.getX() + xOffset; float y = this.getY(); return new float[]{x, y}; } }
TWO:
package me.james.biuedittext; /** * tks daimajia's BounceEaseOut */ public class BounceEaseOut extends BaseEasingMethod { public BounceEaseOut(float duration) { super(duration); } public Float calculate(float t, float b, float c, float d) { return (t /= d) < 0.36363637F?Float.valueOf(c * 7.5625F * t * t + b):(t < 0.72727275F?Float.valueOf(c * (7.5625F * (t -= 0.54545456F) * t + 0.75F) + b):((double)t < 0.9090909090909091D?Float.valueOf(c * (7.5625F * (t -= 0.8181818F) * t + 0.9375F) + b):Float.valueOf(c * (7.5625F * (t -= 0.95454544F) * t + 0.984375F) + b))); } }
THREE:
package me.james.biuedittext; import android.animation.TypeEvaluator; import java.util.ArrayList; import java.util.Iterator; /** * tks daimajia's BaseEasingMethod */ public abstract class BaseEasingMethod implements TypeEvaluator<Number> { protected float mDuration; private ArrayList<EasingListener> mListeners = new ArrayList(); public void addEasingListener(BaseEasingMethod.EasingListener l) { this.mListeners.add(l); } public void addEasingListeners(BaseEasingMethod.EasingListener... ls) { BaseEasingMethod.EasingListener[] arr$ = ls; int len$ = ls.length; for(int i$ = 0; i$ < len$; ++i$) { BaseEasingMethod.EasingListener l = arr$[i$]; this.mListeners.add(l); } } public void removeEasingListener(BaseEasingMethod.EasingListener l) { this.mListeners.remove(l); } public void clearEasingListeners() { this.mListeners.clear(); } public BaseEasingMethod(float duration) { this.mDuration = duration; } public void setDuration(float duration) { this.mDuration = duration; } public final Float evaluate(float fraction, Number startValue, Number endValue) { float t = this.mDuration * fraction; float b = startValue.floatValue(); float c = endValue.floatValue() - startValue.floatValue(); float d = this.mDuration; float result = this.calculate(t, b, c, d).floatValue(); Iterator i$ = this.mListeners.iterator(); while(i$.hasNext()) { BaseEasingMethod.EasingListener l = (BaseEasingMethod.EasingListener)i$.next(); l.on(t, result, b, c, d); } return Float.valueOf(result); } public abstract Float calculate(float var1, float var2, float var3, float var4); public interface EasingListener { void on(float var1, float var2, float var3, float var4, float var5); } }
Step 2
<me.james.biuedittext.BiuEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="biu,biu,發射1號"
android:textColor="@android:color/white"
app:biu_duration="800"
app:biu_text_color="@android:color/white"
app:biu_text_scale="1.5"
app:biu_type="flydown"
app:biu_text_start_size="12sp" />
####attrs:
<declare-styleable name="BiuEditStyle">
<attr name="biu_text_color" format="color" />
<attr name="biu_text_start_size" format="dimension" />
<attr name="biu_text_scale" format="float" />
<attr name="biu_duration" format="integer" />
<attr name="biu_type" format="enum">
<enum name="flyup" value="0" />
<enum name="flydown" value="1" />
</attr>
</declare-styleable>
引數 | 型別 | 含義 |
---|---|---|
biu_duration | int | 動畫時長 |
biu_text_color | color | 飛來飛去的文字顏色 |
biu_text_start_size | dimension | 文字原來大小 |
biu_text_scale | float | 文字放大倍數 |
biu_type | String | 動畫型別:flyDown,flyup |
Step 3
public class MainActivity extends AppCompatActivity {
private BiuEditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (BiuEditText) findViewById(R.id.biucontainer);
}
}
---------------截止----------------