1. 程式人生 > >移動開發----biu,biu,一個有趣的EditText

移動開發----biu,biu,一個有趣的EditText

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);
    }
}


---------------截止----------------