2013-06-03 31 views
34

Làm thế nào tôi có thể định dạng một EditText đi theo "dd/mm/yyyy" định dạng giống như cách mà chúng ta có thể định dạng sử dụng một đầu vào TextWatcher-mặt nạ người sử dụng để tìm kiếm như "0,05 €". Tôi không nói về việc hạn chế các ký tự, hoặc xác nhận một ngày tháng, chỉ cần che dấu cho định dạng trước đó.Làm thế nào để che giấu một EditText để hiển thị các định dạng dd/mm/ngày yyyy

Trả lời

84

Tôi đã viết TextWatcher cho một dự án, hy vọng nó sẽ hữu ích cho ai đó. Lưu ý rằng nó không không xác thực ngày được người dùng nhập và bạn nên xử lý điều đó khi tiêu điểm thay đổi, vì người dùng có thể chưa hoàn tất việc nhập ngày.

Cập nhật 25/06 Làm cho nó trở thành một wiki để xem chúng ta có đạt được mã cuối cùng tốt hơn hay không.

Cập nhật 07/06 Cuối cùng tôi đã thêm một số loại xác thực cho chính người theo dõi. Nó sẽ làm như sau với số ngày không hợp lệ:

  • Nếu tháng lớn hơn 12, nó sẽ là 12 (tháng)
  • Nếu ngày lớn hơn một tháng được lựa chọn, làm cho nó tối đa cho tháng đó.
  • Nếu năm không nằm trong phạm vi 1900-2100, thay đổi nó để được trong khoảng

xác nhận này phù hợp với nhu cầu của tôi, nhưng một số bạn có thể muốn thay đổi nó một chút, phạm vi có thể dễ dàng thay đổi và bạn có thể móc xác thực này tới thông báo Toast chẳng hạn, để thông báo cho người dùng rằng chúng tôi đã sửa đổi ngày của họ vì nó không hợp lệ.

Trong mã này, tôi sẽ giả định rằng chúng tôi có một tham chiếu đến EditText của chúng tôi gọi dateTextWatcher này gắn liền với nó, điều này có thể được thực hiện một cái gì đó như thế này:

EditText date; 
date = (EditText)findViewById(R.id.whichdate); 
date.addTextChangedListener(tw); 

TextWatcher tw = new TextWatcher() { 
    private String current = ""; 
    private String ddmmyyyy = "DDMMYYYY"; 
    private Calendar cal = Calendar.getInstance(); 

Khi người dùng thay đổi văn bản của EditText

@Override 
    public void onTextChanged(CharSequence s, int start, int before, int count) { 
     if (!s.toString().equals(current)) { 
      String clean = s.toString().replaceAll("[^\\d.]|\\.", ""); 
      String cleanC = current.replaceAll("[^\\d.]|\\.", ""); 

      int cl = clean.length(); 
      int sel = cl; 
      for (int i = 2; i <= cl && i < 6; i += 2) { 
       sel++; 
      } 
      //Fix for pressing delete next to a forward slash 
      if (clean.equals(cleanC)) sel--; 

      if (clean.length() < 8){ 
       clean = clean + ddmmyyyy.substring(clean.length()); 
      }else{ 
       //This part makes sure that when we finish entering numbers 
       //the date is correct, fixing it otherwise 
       int day = Integer.parseInt(clean.substring(0,2)); 
       int mon = Integer.parseInt(clean.substring(2,4)); 
       int year = Integer.parseInt(clean.substring(4,8)); 

       mon = mon < 1 ? 1 : mon > 12 ? 12 : mon; 
       cal.set(Calendar.MONTH, mon-1); 
       year = (year<1900)?1900:(year>2100)?2100:year; 
       cal.set(Calendar.YEAR, year); 
       //^first set year for the line below to work correctly 
       //with leap years - otherwise, date e.g. 29/02/2012 
       //would be automatically corrected to 28/02/2012 

       day = (day > cal.getActualMaximum(Calendar.DATE))? cal.getActualMaximum(Calendar.DATE):day; 
       clean = String.format("%02d%02d%02d",day, mon, year); 
      } 

      clean = String.format("%s/%s/%s", clean.substring(0, 2), 
       clean.substring(2, 4), 
       clean.substring(4, 8)); 

      sel = sel < 0 ? 0 : sel; 
      current = clean; 
      date.setText(current); 
      date.setSelection(sel < current.length() ? sel : current.length()); 
     } 
    } 

Chúng tôi cũng thực hiện hai chức năng khác bởi vì chúng ta phải

@Override 
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 

    @Override 
    public void afterTextChanged(Editable s) {} 
}; 

này tạo ra hiệu ứng sau, nơi xóa hoặc chèn ký tự sẽ tiết lộ hoặc ẩn mặt nạ dd/mm/yyyy. Nó sẽ dễ dàng sửa đổi để phù hợp với mặt nạ định dạng khác vì tôi đã cố gắng để lại mã càng đơn giản càng tốt.

enter image description here

+1

Tôi xin lỗi. Tôi có thể truy cập mã đầy đủ bằng cách nào? Biến số cal của bạn là gì? – joao2fast4u

+3

@ joao2fast4u - biến cal là đối tượng java.util.Calendar –

+0

giải pháp tuyệt vời! thanks –

1

một cách sạch để sử dụng mã Juan Cortés được đặt nó trong một lớp:

public class DateInputMask implements TextWatcher { 

private String current = ""; 
private String ddmmyyyy = "DDMMYYYY"; 
private Calendar cal = Calendar.getInstance(); 
private EditText input; 

public DateInputMask(EditText input) { 
    this.input = input; 
    this.input.addTextChangedListener(this); 
} 

@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 (s.toString().equals(current)) { 
     return; 
    } 

    String clean = s.toString().replaceAll("[^\\d.]|\\.", ""); 
    String cleanC = current.replaceAll("[^\\d.]|\\.", ""); 

    int cl = clean.length(); 
    int sel = cl; 
    for (int i = 2; i <= cl && i < 6; i += 2) { 
     sel++; 
    } 
    //Fix for pressing delete next to a forward slash 
    if (clean.equals(cleanC)) sel--; 

    if (clean.length() < 8){ 
     clean = clean + ddmmyyyy.substring(clean.length()); 
    }else{ 
     //This part makes sure that when we finish entering numbers 
     //the date is correct, fixing it otherwise 
     int day = Integer.parseInt(clean.substring(0,2)); 
     int mon = Integer.parseInt(clean.substring(2,4)); 
     int year = Integer.parseInt(clean.substring(4,8)); 

     mon = mon < 1 ? 1 : mon > 12 ? 12 : mon; 
     cal.set(Calendar.MONTH, mon-1); 
     year = (year<1900)?1900:(year>2100)?2100:year; 
     cal.set(Calendar.YEAR, year); 
     //^first set year for the line below to work correctly 
     //with leap years - otherwise, date e.g. 29/02/2012 
     //would be automatically corrected to 28/02/2012 

     day = (day > cal.getActualMaximum(Calendar.DATE))? cal.getActualMaximum(Calendar.DATE):day; 
     clean = String.format("%02d%02d%02d",day, mon, year); 
    } 

    clean = String.format("%s/%s/%s", clean.substring(0, 2), 
      clean.substring(2, 4), 
      clean.substring(4, 8)); 

    sel = sel < 0 ? 0 : sel; 
    current = clean; 
    input.setText(current); 
    input.setSelection(sel < current.length() ? sel : current.length()); 
} 

@Override 
public void afterTextChanged(Editable s) { 

} 
} 

sau đó bạn có thể tái sử dụng nó

new DateInputMask(myEditTextInstance); 
1

Câu trả lời hiện nay là rất tốt và giúp hướng dẫn tôi về giải pháp của riêng tôi. Có một vài lý do khiến tôi quyết định đăng giải pháp của riêng mình mặc dù câu hỏi này đã có câu trả lời hợp lệ:

  • Tôi đang làm việc ở Kotlin chứ không phải Java. Những người thấy mình có cùng vấn đề sẽ phải dịch giải pháp hiện tại.
  • Tôi muốn viết một câu trả lời dễ đọc hơn để mọi người có thể dễ dàng thích nghi hơn với các vấn đề của riêng họ.
  • Theo đề xuất của dengue8830, tôi đã đóng gói giải pháp cho vấn đề này trong một lớp học, vì vậy bất kỳ ai cũng có thể sử dụng mà không cần lo lắng về việc triển khai.

Để sử dụng nó, chỉ cần làm một cái gì đó như:

  • DateInputMask (mEditText) .listen()

Và giải pháp được trình bày dưới đây:

class DateInputMask(val input : EditText) { 

    fun listen() { 
     input.addTextChangedListener(mDateEntryWatcher) 
    } 

    private val mDateEntryWatcher = object : TextWatcher { 

     var edited = false 
     val dividerCharacter = "/" 

     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { 
      if (edited) { 
       edited = false 
       return 
      } 

      var working = getEditText() 

      working = manageDateDivider(working, 2, start, before) 
      working = manageDateDivider(working, 5, start, before) 

      edited = true 
      input.setText(working) 
      input.setSelection(input.text.length) 
     } 

     private fun manageDateDivider(working: String, position : Int, start: Int, before: Int) : String{ 
      if (working.length == position) { 
       return if (before <= position && start < position) 
        working + dividerCharacter 
       else 
        working.dropLast(1) 
      } 
      return working 
     } 

     private fun getEditText() : String { 
      return if (input.text.length >= 10) 
       input.text.toString().substring(0,10) 
      else 
       input.text.toString() 
     } 

     override fun afterTextChanged(s: Editable) {} 
     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} 
    } 
} 
0

này câu trả lời không áp dụng một mặt nạ đầy đủ cho các chữ số còn lại chưa được nhập. Tuy nhiên, nó có liên quan và là giải pháp tôi cần. Nó hoạt động tương tự như cách hoạt động của PhoneNumberFormattingTextWatcher.

Khi bạn nhập, hãy thêm dấu gạch chéo để tách ngày được định dạng như mm/dd/yyyy. Nó không thực hiện bất kỳ xác nhận nào - chỉ cần định dạng.

Không cần tham chiếu EditText. Chỉ cần đặt trình lắng nghe và nó hoạt động. myEditText.addTextChangedListener(new DateTextWatcher());

import android.text.Editable; 
import android.text.TextWatcher; 

import java.util.Locale; 

/** 
* Adds slashes to a date so that it matches mm/dd/yyyy. 
* 
* Created by Mark Miller on 12/4/17. 
*/ 
public class DateTextWatcher implements TextWatcher { 

    public static final int MAX_FORMAT_LENGTH = 8; 
    public static final int MIN_FORMAT_LENGTH = 3; 

    private String updatedText; 
    private boolean editing; 


    @Override 
    public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) { 

    } 

    @Override 
    public void onTextChanged(CharSequence text, int start, int before, int count) { 
     if (text.toString().equals(updatedText) || editing) return; 

     String digitsOnly = text.toString().replaceAll("\\D", ""); 
     int digitLen = digitsOnly.length(); 

     if (digitLen < MIN_FORMAT_LENGTH || digitLen > MAX_FORMAT_LENGTH) { 
      updatedText = digitsOnly; 
      return; 
     } 

     if (digitLen <= 4) { 
      String month = digitsOnly.substring(0, 2); 
      String day = digitsOnly.substring(2); 

      updatedText = String.format(Locale.US, "%s/%s", month, day); 
     } 
     else { 
      String month = digitsOnly.substring(0, 2); 
      String day = digitsOnly.substring(2, 4); 
      String year = digitsOnly.substring(4); 

      updatedText = String.format(Locale.US, "%s/%s/%s", month, day, year); 
     } 
    } 

    @Override 
    public void afterTextChanged(Editable editable) { 

     if (editing) return; 

     editing = true; 

     editable.clear(); 
     editable.insert(0, updatedText); 

     editing = false; 
    } 
} 
-2

Hãy thử sử dụng một thư viện mà giải quyết vấn đề này kể từ khi mặt nạ nó không có sẵn ra khỏi hộp. Có rất nhiều trường hợp góc (như thêm/xóa các ký tự ở giữa văn bản đã được che dấu) và để xử lý đúng cách này, bạn sẽ có rất nhiều mã (và lỗi).

Dưới đây là một số thư viện có sẵn:
https://github.com/egslava/edittext-mask
https://github.com/dimitar-zabaznoski/MaskedEditText
https://github.com/pinball83/Masked-Edittext
https://github.com/RedMadRobot/input-mask-android
https://github.com/santalu/mask-edittext

** tâm rằng tại thời điểm viết các thư viện này là không phải không có vấn đề, vì vậy nó là trách nhiệm của bạn để chọn cái nào phù hợp nhất với bạn và kiểm tra mã.

Các vấn đề liên quan