Saturday, October 18, 2014

EditText for CreditCards

Internet has tons of different implementations to add credit card support in Edit.
Most of them listen for text changes and add spaces and I don't like this approche. Mostly because user can see those space while selecting text.

So I will add mine implementation :)

Here is screenshot from Gennymotion:

And sources as well (link on GitHub):

public class CreditCardTransformation implements TransformationMethod {

  private static final Divider sDivider = new Divider();

  @Override
  public CharSequence getTransformation(final CharSequence source, View view) {
    return new SpannedWrapper(source) {
      @Override
      @SuppressWarnings("unchecked")
      public  T[] getSpans(int start, int end, Class type) {
        T[] array = super.getSpans(start, end, type);
        if (!type.isAssignableFrom(Divider.class)) {
          return array;
        }
        Object[] result = (Object[]) Array.newInstance(type, array.length + 1);
        result[0] = sDivider;
        System.arraycopy(array, 0, result, 1, array.length);
        return (T[]) result;
      }

      @Override
      public int getSpanStart(Object tag) {
        if (tag instanceof Divider) {
          return 0;
        }
        return super.getSpanStart(tag);
      }

      @Override
      public int getSpanEnd(Object tag) {
        if (tag instanceof Divider) {
          return length();
        }
        return super.getSpanEnd(tag);
      }
    };
  }

  @Override
  public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Rect previouslyFocusedRect) {
  }

  static class SpannedWrapper implements Spanned {
    private Spanned mSpanned;
    private CharSequence mSource;

    SpannedWrapper(CharSequence source) {
      mSource = source;
      if (mSource instanceof Spanned) {
        mSpanned = (Spanned) source;
      }
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T[] getSpans(int start, int end, Class type) {
      if (mSpanned != null) {
        return mSpanned.getSpans(start, end, type);
      }
      return (T[]) Array.newInstance(type, 0);
    }

    @Override
    public int getSpanStart(Object tag) {
      if (mSpanned != null) {
        return mSpanned.getSpanStart(tag);
      }
      return 0;
    }

    @Override
    public int getSpanEnd(Object tag) {
      if (mSpanned != null) {
        return mSpanned.getSpanEnd(tag);
      }
      return 0;
    }

    @Override
    public int getSpanFlags(Object tag) {
      if (mSpanned != null) {
        return mSpanned.getSpanFlags(tag);
      }
      return 0;
    }

    @Override
    public int nextSpanTransition(int start, int limit, Class type) {
      if (mSpanned != null) {
        return mSpanned.nextSpanTransition(start, limit, type);
      }
      return 0;
    }

    @Override
    public int length() {
      return mSource.length();
    }

    @Override
    public char charAt(int index) {
      return mSource.charAt(index);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
      return mSource.subSequence(start, end);
    }

    @Override
    public String toString() {
      return mSource.toString();
    }
  }

  static class Divider extends ReplacementSpan {
    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
      int groups = Math.min(3, (end - start) / 4);
      return (int) (paint.measureText(text, start, end) + paint.getTextSize() * groups);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
      int offset = 0;
      int groupStart = start;
      while (groupStart < end) {
        int groupEnd = Math.min(groupStart + 4, end);
        canvas.drawText(text, groupStart, groupEnd, x + offset, y, paint);
        offset += paint.measureText(text, groupStart, groupEnd) + paint.getTextSize();
        groupStart = groupEnd;
      }
    }
  }
}

No comments:

Post a Comment