Advertisement
Guest User

Untitled

a guest
Apr 26th, 2015
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.83 KB | None | 0 0
  1. package com.enginegl.mymaskededittext;
  2.  
  3. import android.content.Context;
  4. import android.text.Editable;
  5. import android.text.InputType;
  6. import android.text.TextWatcher;
  7. import android.util.AttributeSet;
  8. import android.view.KeyEvent;
  9. import android.view.View;
  10. import android.view.inputmethod.EditorInfo;
  11. import android.widget.EditText;
  12. import android.widget.TextView;
  13.  
  14. public class MaskedEditText extends EditText implements TextWatcher {
  15.  
  16. private String mask;
  17. private char maskFill = '_';
  18. private char charRepresentation = '#';
  19. private int[] rawToMask;
  20. private RawText rawText;
  21. private boolean editingBefore;
  22. private boolean editingOnChanged;
  23. private boolean editingAfter;
  24. private int[] maskToRaw;
  25. private char[] charsInMask;
  26. private int selection;
  27. private boolean initialized;
  28. private boolean ignore;
  29. protected int maxRawLength;
  30. private int lastValidMaskPosition;
  31. private boolean selectionChanged;
  32. private OnFocusChangeListener focusChangeListener;
  33.  
  34. public MaskedEditText(Context context) {
  35. super(context);
  36. init();
  37. }
  38.  
  39. public MaskedEditText(Context context, AttributeSet attrs) {
  40. super(context, attrs);
  41. init();
  42.  
  43. cleanUp();
  44.  
  45. // Ignoring enter key presses
  46. setOnEditorActionListener(new OnEditorActionListener() {
  47. @Override
  48. public boolean onEditorAction(TextView v, int actionId,KeyEvent event) {
  49. switch (actionId) {
  50. case EditorInfo.IME_ACTION_NEXT:
  51. // fixing actionNext
  52. return false;
  53. default:
  54. return true;
  55. }
  56. }
  57. });
  58. }
  59.  
  60. /** @param listener - its onFocusChange() method will be called before performing MaskedEditText operations,
  61. * related to this event. */
  62. @Override
  63. public void setOnFocusChangeListener(OnFocusChangeListener listener) {
  64. focusChangeListener = listener;
  65. }
  66.  
  67. private void cleanUp() {
  68. if (mask == null) {
  69. return;
  70. }
  71. initialized = false;
  72.  
  73. generatePositionArrays();
  74.  
  75. rawText = new RawText();
  76. selection = rawToMask[0];
  77.  
  78. editingBefore = true;
  79. editingOnChanged = true;
  80. editingAfter = true;
  81. if(hasHint()) {
  82. this.setText(null);
  83. }
  84. else {
  85. this.setText(mask.replace(charRepresentation, '_'));
  86. }
  87. editingBefore = false;
  88. editingOnChanged = false;
  89. editingAfter = false;
  90.  
  91. maxRawLength = maskToRaw[previousValidPosition(mask.length() - 1)] + 1;
  92. lastValidMaskPosition = findLastValidMaskPosition();
  93. initialized = true;
  94.  
  95. super.setOnFocusChangeListener(new OnFocusChangeListener() {
  96. @Override
  97. public void onFocusChange(View v, boolean hasFocus) {
  98. if (focusChangeListener != null) {
  99. focusChangeListener.onFocusChange(v, hasFocus);
  100. }
  101.  
  102. if(hasFocus() && (rawText.length() > 0 || !hasHint())) {
  103. selectionChanged = false;
  104. MaskedEditText.this.setSelection(lastValidPosition());
  105. }
  106. }
  107. });
  108. }
  109.  
  110. private int findLastValidMaskPosition() {
  111. for(int i = maskToRaw.length - 1; i >= 0; i--) {
  112. if(maskToRaw[i] != -1) return i;
  113. }
  114. throw new RuntimeException("Mask contains only the representation char");
  115. }
  116.  
  117. private boolean hasHint() {
  118. return getHint() != null;
  119. }
  120.  
  121. public MaskedEditText(Context context, AttributeSet attrs, int defStyle) {
  122. super(context, attrs, defStyle);
  123. init();
  124. }
  125.  
  126. public void setMask(String mask) {
  127. this.mask = mask;
  128. cleanUp();
  129. }
  130.  
  131. public String getMask() {
  132. return this.mask;
  133. }
  134.  
  135. public void setCharRepresentation(char charRepresentation) {
  136. this.charRepresentation = charRepresentation;
  137. cleanUp();
  138. }
  139.  
  140. public char getCharRepresentation() {
  141. return this.charRepresentation;
  142. }
  143.  
  144. private void generatePositionArrays() {
  145. int[] aux = new int[mask.length()];
  146. maskToRaw = new int[mask.length()];
  147. String charsInMaskAux = "";
  148.  
  149. int charIndex = 0;
  150. for(int i = 0; i < mask.length(); i++) {
  151. char currentChar = mask.charAt(i);
  152. if(currentChar == charRepresentation) {
  153. aux[charIndex] = i;
  154. maskToRaw[i] = charIndex++;
  155. }
  156. else {
  157. String charAsString = Character.toString(currentChar);
  158. if(!charsInMaskAux.contains(charAsString) && !Character.isLetter(currentChar) && !Character.isDigit(currentChar)) {
  159. charsInMaskAux = charsInMaskAux.concat(charAsString);
  160. }
  161. maskToRaw[i] = -1;
  162. }
  163. }
  164. if(charsInMaskAux.indexOf(' ') < 0) {
  165. charsInMaskAux = charsInMaskAux + " ";
  166. }
  167. charsInMask = charsInMaskAux.toCharArray();
  168.  
  169. rawToMask = new int[charIndex];
  170. for (int i = 0; i < charIndex; i++) {
  171. rawToMask[i] = aux[i];
  172. }
  173. }
  174.  
  175. private void init() {
  176. this.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
  177. addTextChangedListener(this);
  178. }
  179.  
  180. @Override
  181. public void beforeTextChanged(CharSequence s, int start, int count,
  182. int after) {
  183. if (mask == null) {
  184. return;
  185. }
  186. if(!editingBefore) {
  187. editingBefore = true;
  188. if(start > lastValidMaskPosition) {
  189. ignore = true;
  190. }
  191. int rangeStart = start;
  192. if(after == 0) {
  193. rangeStart = erasingStart(start);
  194. }
  195. Range range = calculateRange(rangeStart, start + count);
  196. if(range.getStart() != -1) {
  197. rawText.subtractFromString(range);
  198. }
  199. if(count > 0) {
  200. selection = previousValidPosition(start);
  201. }
  202. }
  203. }
  204.  
  205. private int erasingStart(int start) {
  206. while(start > 0 && maskToRaw[start] == -1) {
  207. start--;
  208. }
  209. return start;
  210. }
  211.  
  212. @Override
  213. public void onTextChanged(CharSequence s, int start, int before, int count) {
  214. if (mask == null) {
  215. return;
  216. }
  217. if(!editingOnChanged && editingBefore) {
  218. editingOnChanged = true;
  219. if(ignore) {
  220. return;
  221. }
  222. if(count > 0) {
  223. int startingPosition = maskToRaw[nextValidPosition(start)];
  224. String addedString = s.subSequence(start, start + count).toString();
  225. count = rawText.addToString(clear(addedString), startingPosition, maxRawLength);
  226. if(initialized) {
  227. int currentPosition;
  228. if(startingPosition + count < rawToMask.length)
  229. currentPosition = rawToMask[startingPosition + count];
  230. else
  231. currentPosition = lastValidMaskPosition + 1;
  232. selection = nextValidPosition(currentPosition);
  233. }
  234. }
  235. }
  236. }
  237.  
  238. @Override
  239. public void afterTextChanged(Editable s) {
  240. if (mask == null) {
  241. return;
  242. }
  243. if(!editingAfter && editingBefore && editingOnChanged) {
  244. editingAfter = true;
  245. if(rawText.length() == 0 && hasHint()) {
  246. selection = 0;
  247. setText(null);
  248. }
  249. else {
  250. setText(makeMaskedText());
  251. }
  252.  
  253. selectionChanged = false;
  254. setSelection(selection);
  255.  
  256. editingBefore = false;
  257. editingOnChanged = false;
  258. editingAfter = false;
  259. ignore = false;
  260. }
  261. }
  262.  
  263. @Override
  264. protected void onSelectionChanged(int selStart, int selEnd) {
  265. // On Android 4+ this method is being called more than 1 time if there is a hint in the EditText, what moves the cursor to left
  266. // Using the boolean var selectionChanged to limit to one execution
  267. if (mask == null) {
  268. super.onSelectionChanged(selStart, selEnd);
  269. return;
  270. }
  271. if(initialized ){
  272. if(!selectionChanged) {
  273.  
  274. if(rawText.length() == 0 && hasHint()) {
  275. selStart = 0;
  276. selEnd = 0;
  277. }
  278. else {
  279. selStart = fixSelection(selStart);
  280. selEnd = fixSelection(selEnd);
  281. }
  282. setSelection(selStart, selEnd);
  283. selectionChanged = true;
  284. }
  285. else{//check to see if the current selection is outside the already entered text
  286. if(!(hasHint() && rawText.length() == 0) && selStart > rawText.length() - 1){
  287. setSelection(fixSelection(selStart),fixSelection(selEnd));
  288. }
  289. }
  290. }
  291. super.onSelectionChanged(selStart, selEnd);
  292. }
  293.  
  294. private int fixSelection(int selection) {
  295. if(selection > lastValidPosition()) {
  296. return lastValidPosition();
  297. }
  298. else {
  299. return nextValidPosition(selection);
  300. }
  301. }
  302.  
  303. private int nextValidPosition(int currentPosition) {
  304. while(currentPosition < lastValidMaskPosition && maskToRaw[currentPosition] == -1) {
  305. currentPosition++;
  306. }
  307. if(currentPosition > lastValidMaskPosition) return lastValidMaskPosition + 1;
  308. return currentPosition;
  309. }
  310.  
  311. private int previousValidPosition(int currentPosition) {
  312. while(currentPosition >= 0 && maskToRaw[currentPosition] == -1) {
  313. currentPosition--;
  314. if(currentPosition < 0) {
  315. return nextValidPosition(0);
  316. }
  317. }
  318. return currentPosition;
  319. }
  320.  
  321. private int lastValidPosition() {
  322. if(rawText.length() == maxRawLength) {
  323. return rawToMask[rawText.length() - 1] + 1;
  324. }
  325. return nextValidPosition(rawToMask[rawText.length()]);
  326. }
  327.  
  328. private String makeMaskedText() {
  329. char[] maskedText = mask.replace(charRepresentation, ' ').toCharArray();
  330. for(int i = 0; i < rawToMask.length; i++) {
  331. if(i < rawText.length()) {
  332. maskedText[rawToMask[i]] = rawText.charAt(i);
  333. }
  334. else {
  335. maskedText[rawToMask[i]] = maskFill;
  336. }
  337. }
  338. return new String(maskedText);
  339. }
  340.  
  341. private Range calculateRange(int start, int end) {
  342. Range range = new Range();
  343. for(int i = start; i <= end && i < mask.length(); i++) {
  344. if(maskToRaw[i] != -1) {
  345. if(range.getStart() == -1) {
  346. range.setStart(maskToRaw[i]);
  347. }
  348. range.setEnd(maskToRaw[i]);
  349. }
  350. }
  351. if(end == mask.length()) {
  352. range.setEnd(rawText.length());
  353. }
  354. if(range.getStart() == range.getEnd() && start < end) {
  355. int newStart = previousValidPosition(range.getStart() - 1);
  356. if(newStart < range.getStart()) {
  357. range.setStart(newStart);
  358. }
  359. }
  360. return range;
  361. }
  362.  
  363. private String clear(String string) {
  364. for(char c : charsInMask) {
  365. string = string.replace(Character.toString(c), "");
  366. }
  367. return string;
  368. }
  369.  
  370. class RawText {
  371. private String text;
  372.  
  373. public RawText() {
  374. text = "";
  375. }
  376.  
  377. public void subtractFromString(Range range) {
  378. String firstPart = "";
  379. String lastPart = "";
  380.  
  381. if(range.getStart() > 0 && range.getStart() <= text.length()) {
  382. firstPart = text.substring(0, range.getStart());
  383. }
  384. if(range.getEnd() >= 0 && range.getEnd() < text.length()) {
  385. lastPart = text.substring(range.getEnd(), text.length());
  386. }
  387. text = firstPart.concat(lastPart);
  388. }
  389.  
  390. /**
  391. *
  392. * @param newString New String to be added
  393. * @param start Position to insert newString
  394. * @param maxLength Maximum raw text length
  395. * @return Number of added characters
  396. */
  397. public int addToString(String newString, int start, int maxLength) {
  398. String firstPart = "";
  399. String lastPart = "";
  400.  
  401. if(newString == null || newString.equals("")) {
  402. return 0;
  403. }
  404. else if(start < 0) {
  405. throw new IllegalArgumentException("Start position must be non-negative");
  406. }
  407. else if(start > text.length()) {
  408. throw new IllegalArgumentException("Start position must be less than the actual text length");
  409. }
  410.  
  411. int count = newString.length();
  412.  
  413. if(start > 0) {
  414. firstPart = text.substring(0, start);
  415. }
  416. if(start >= 0 && start < text.length()) {
  417. lastPart = text.substring(start, text.length());
  418. }
  419. if(text.length() + newString.length() > maxLength) {
  420. count = maxLength - text.length();
  421. newString = newString.substring(0, count);
  422. }
  423. text = firstPart.concat(newString).concat(lastPart);
  424. return count;
  425. }
  426.  
  427. public String getText() {
  428. return text;
  429. }
  430.  
  431. public int length() {
  432. return text.length();
  433. }
  434.  
  435. public char charAt(int position) {
  436. return text.charAt(position);
  437. }
  438. }
  439.  
  440. class Range {
  441. private int start;
  442. private int end;
  443.  
  444. Range() {
  445. start = -1;
  446. end = -1;
  447. }
  448.  
  449. public int getStart() {
  450. return start;
  451. }
  452.  
  453. public void setStart(int start) {
  454. this.start = start;
  455. }
  456.  
  457. public int getEnd() {
  458. return end;
  459. }
  460.  
  461. public void setEnd(int end) {
  462. this.end = end;
  463. }
  464. }
  465. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement