.:: CODE SNIPPET ::.

"Your time is limited, so don't waste it living someone else's life"

Xử lý tìm kiếm tiếng việt không dấu trong android listview


Trên các thiết bị android, chúng ta thường hay sử dụng tiếng việt không dấu trong việc tìm kiếm mà nội dung tìm kiếm của chúng ta là tiếng việt có dấu. Như vậy, chúng ta cần xử lý so sánh giữa tiếng việt có dấu và không dấu.
Một trong một phương pháp đơn giản nhất đó là chuyển tiếng việt có dấu thành tiếng việt không dấu để tìm kiếm. Việc tìm kiếm này chỉ phù hợp với những nội dung tìm kiếm đơn giản, ngắn gọn nếu không chương trình sẽ chạy chậm đi rất nhiều.
Bài này có hai phần:
1. Cách chuyển đổi nội dung tiếng việt có dấu thành tiếng việt không dấu
Việc xử lý này khá đơn giản, không sử dụng framework nào cả. Chúng ta sẽ dựa vào bảng mã UNICODE và chú ý đến các ký tự có dấu trong tiếng việt. Các ký tự có dấu đều xuất phát từ một ký tự không dấu và thêm vào một hoặc hai dấu.
Ví dụ: ô ố ồ ộ ỗ ổ ơ ờ ớ ợ ờ ớ ở ỡ đều xuất phát từ ký tự o mà ra.
Như vậy, khi gặp tất cả các ký tự trong dãy này, chúng ta chỉ việc thay nó bằng ký tự ‘o’ mà thôi. Việc tương tự sẽ được làm đối với các ký tự có dấu khác.
Đầu tiên, chúng ta sẽ liệt kê ra tất cả những ký tự có dấu của tiếng việt theo những tổ hợp như trên(có cùng ký tự gốc)

	private char[] charA = { 'à', 'á', 'ạ', 'ả', 'ã',// 0->16
			'â', 'ầ', 'ấ', 'ậ', 'ẩ', 'ẫ', 'ă', 'ằ', 'ắ', 'ặ', 'ẳ', 'ẵ' };// a,// ă,// â
	private char[] charE = { 'ê', 'ề', 'ế', 'ệ', 'ể', 'ễ',// 17->27
			'è', 'é', 'ẹ', 'ẻ', 'ẽ' };// e
	private char[] charI = { 'ì', 'í', 'ị', 'ỉ', 'ĩ' };// i 28->32
	private char[] charO = { 'ò', 'ó', 'ọ', 'ỏ', 'õ',// o 33->49
			'ô', 'ồ', 'ố', 'ộ', 'ổ', 'ỗ',// ô
			'ơ', 'ờ', 'ớ', 'ợ', 'ở', 'ỡ' };// ơ
	private char[] charU = { 'ù', 'ú', 'ụ', 'ủ', 'ũ',// u 50->60
			'ư', 'ừ', 'ứ', 'ự', 'ử', 'ữ' };// ư
	private char[] charY = { 'ỳ', 'ý', 'ỵ', 'ỷ', 'ỹ' };// y 61->65
	private char[] charD = { 'đ', ' ' }; // 66-67

Đồng thời, khi khởi tạo lớp này, chúng ta sẽ tạo ra một chuỗi gồm tất cả các giá trị của tất cả các mảng trên.

String charact = String.valueOf(charA, 0, charA.length)
				+ String.valueOf(charE, 0, charE.length)
				+ String.valueOf(charI, 0, charI.length)
				+ String.valueOf(charO, 0, charO.length)
				+ String.valueOf(charU, 0, charU.length)
				+ String.valueOf(charY, 0, charY.length)
				+ String.valueOf(charD, 0, charD.length);

Bước tiếp theo chúng ta sẽ việt một hàm tìm kiếm và trả về ký tự gốc đối với một ký tự có dấu nào đấy, dựa vào chuỗi vừa tạo được.

private char GetAlterChar(char pC) {
		if ((int) pC == 32) {
			return ' ';
		}

		char tam = pC;// Character.toLowerCase(pC);

		int i = 0;
		while (i < charact.length() && charact.charAt(i) != tam) {
			i++;
		}
		if (i < 0 || i > 67)
			return pC;

		if (i == 66) {
			return 'd';
		}
		if (i >= 0 && i <= 16) {
			return 'a';
		}
		if (i >= 17 && i <= 27) {
			return 'e';
		}
		if (i >= 28 && i <= 32) {
			return 'i';
		}
		if (i >= 33 && i <= 49) {
			return 'o';
		}
		if (i >= 50 && i <= 60) {
			return 'u';
		}
		if (i >= 61 && i <= 65) {
			return 'y';
		}
		return pC;
	}

Và cuối cùng, đối với một chuỗi nào đấy, chúng ta sẽ có thể chuyển toàn bộ chuỗi đó thành chuỗi tiếng việt không dấu tương ứng dựa vào cách chuyển đổi các ký tự có dấu trong chuối đó.

public String ConvertString(String pStr) {
		String convertString = pStr.toLowerCase();
		Character[] returnString = new Character[convertString.length()];
		for (int i = 0; i < convertString.length(); i++) {
			char temp = convertString.charAt(i);
			if ((int) temp < 97 || temp > 122) {
				char tam1 = this.GetAlterChar(temp);
				if ((int) temp != 32)
					convertString = convertString.replace(temp, tam1);
			}
		}
		return convertString;
	}

2. Cách xử lý tìm kiếm trong listview sử dụng filter trong android
Chúng ta sẽ tiến hành xử lý lọc dữ liệu trong danh sách (listview). Muốn vậy, thì đầu tiên lớp adapter của danh sách phải được implements interface Filterable.
Để bộ lọc hoạt động, cần phải tạo ra một lớp kế thừa lớp Filter:
Giả sử danh sách chứa dữ liệu cho adapter của bạn là lstAllItem, thì chúng ta sẽ không chuyển danh sách này vào adapter nữa. Nhưng, chúng ta sẽ tạo ra một danh sách phụ lstSubItem, danh sách này chứa những phần tử phù hợp điều kiện lọc. Và sẽ đưa danh sách phụ đó làm dữ liệu cho adapter.

Với mỗi lần thay bộ filter được gọi, thì chúng ta sẽ duyệt qua các item trong danh sách chính là lstAllItem, và tìm những item nào phù hợp điều kiện lọc và đưa vào lstSubItem và truyền vào cho adapter. Như vậy thì danh sách sẽ thay đổi theo đúng dữ liệu lọc.
Trong bộ lọc, chúng ta sẽ phải sử dụng lại bộ chuyển đổi ký tự đã giới thiệu ở phần đầu, để chuyển đổi dữ liệu của các item thành tiếng việt không dấu để lọc tiếng việt.
Ở ví dụ dưới, giả sử có một thực thể ProvinceDTO gồm có thuộc tính là name, và ta sẽ lọc theo thuộc tính tên này.

//Tạo ra một bộ chuyển đổi ký tự ConvertUnsigned crtUn=new ConvertUnsigned();
private class PTypeFilter extends Filter {
	@SuppressWarnings("unchecked")
	@Override
	protected void publishResults(CharSequence prefix,
			FilterResults results) {
		lstSubItem = (ArrayList<ProvincesDTO>) results.values;
		notifyDataSetChanged();
	}
	@SuppressWarnings("unchecked")
	protected FilterResults performFiltering(CharSequence prefix) {
		FilterResults results = new FilterResults();
		ArrayList<ProvincesDTO> i = new ArrayList<ProvincesDTO>();
			if (prefix != null && prefix.toString().length() > 0) {
				for (int index = 0; index < lstAllItem.size(); index++) {
				ProvincesDTO si = lstAllItem.get(index);
				if (crtUn.ConvertString(si.getName().toLowerCase())
						.contains(prefix.toString().toLowerCase()) == true) {
					i.add(si);
				}
			}
			results.values = i;
			results.count = i.size();
		} else {
			synchronized (lstAllItem) {
				results.values = lstAllItem;
				results.count = lstAllItem.size();
			}
		}
		return results;
	}
}

Ở trên ta thấy, hàm crtUn.ConvertString(si.getName().toLowerCase() để chuyển đổi thuộc tính Name của thực thể si thành chuỗi ký tự tiếng việt không dấu và sau đó, kiểm tra xem nó có chứa các ký tự đã nhập vào là prefix hay không. Nếu có thì đưa nó vào danh sách phụ lstSubItem.
Các bạn download sourcecode ở đây. Đây chỉ là phương pháp mình nghĩ ra để xử lý cho chương trình nhỏ của mình, nên rất có thể trong trường hợp khác nó hoạt động không tốt. Có phương pháp nào khác, các bạn đưa ra cho mình tham khảo với nhé.

Advertisements

27 responses to “Xử lý tìm kiếm tiếng việt không dấu trong android listview

  1. Pingback: Thêm chức năng search vào listview trong android « .:: Hoàng Minh ::.

  2. Pingback: Sử dụng ListView căn bản « .:: Lập Trình Căn Bản ::.

  3. Anonymous January 17, 2013 at 12:01 AM

    >
    &
    <
    những cái nì là gì vậy anh?em hok hiểu @@

  4. Hoàng Minh January 17, 2013 at 12:11 AM

    Cái này dùng để hỗ trợ cho chức năng filter trong ListView của android khi nội dung cần filter là tiếng Việt.
    Khi nào làm tới vấn đề này thì cảm thấy cần nó.hihi. Hồi đó mình nghĩ ra cách này để phục vụ cho cái ứng dụng của mình đó mà.

  5. Anonymous January 17, 2013 at 12:07 PM

    à, thì ra là nó lỗi html, em đọc mà hok hiểu mấy cái đó là gì hết
    mà sao mình phải dùng filter, sao k sử dụng lại code như chức năng search bình thường hả a?
    https://tranhoangminh.wordpress.com/2012/11/22/them-chuc-nang-search-vao-listview-trong-android/
    cái này nè

  6. Hoàng Minh January 17, 2013 at 12:49 PM

    Bạn đọc bài viết đó cũng sẽ thấy cái đó dùng Filter đó,. Tuy nhiên, dữ liệu trong bài viết đó thì chỉ ở dạng text không mà thôi. Còn trong trường hợp ListView của bạn chứa cả ImageView, TextView, CheckBox,… thì bạn phải extends ra một lớp Filter mới để xác định vùng cần phải filter (text) trong đó mà thôi.
    Bài viết này chỉ để phục vụ cho việc filter trong tường hợp dữ liệu là tiếng Việt, vì như bạn thấy là khi sử dụng thiết bị di động, người ta sẽ rất lười để cài một chương trình gõ tiếng Việt, và cho dù thiết bị có hỗ trợ, người dùng cũng ít khi nào gõ tiếng Việt để tìm kiếm, mà người ta sẽ gõ tiếng Việt không dấu. Thì phần code bên trên sẽ hỗ trợ cho làm việc này.
    (Cái này mình tự nghĩ ra để làm, nên chưa chắc đây là cách hay lắm, trong trường hợp ứng dụng của mình thì nó chạy mượt mà)

  7. Anonymous January 17, 2013 at 3:26 PM

    em hok hiểu đoạn code đó lắm, và cách use của nó ntn nữa anh ạ

  8. Anonymous January 17, 2013 at 4:37 PM

    em có thể hỏi anh 1 số điểm trong đoạn code filter trên được không ạ?

  9. Hoàng Minh January 18, 2013 at 11:59 AM

    Bạn cứ hỏi đi, mình sẽ giải thích cho bạn.

  10. Duong Nguyen February 3, 2013 at 6:05 PM

    Bạn ơi, mình cũng đang làm 1 phần tương tự như bạn đã giới thiệu ở trên.
    Mình có listView hiển thị tiếng việt có dấu, mà cần tìm bằng tiếng việt không dấu. mình đã đọc bài của bạn nhưng vẫn chưa hiểu. bạn có thế giúp mình đc ko

  11. Hoàng Minh February 4, 2013 at 12:07 PM

    Bạn hỏi cụ thể phần nào bạn nói rõ được ko, còn nếu không để mình lọc phần code này ra và chuyển cho bạn chạy thử xem sao nhé?

  12. Duong Nguyen February 19, 2013 at 9:42 AM

    Hi, như mình đã nói ở trên. Mình muốn nhập tiếng việt không dấu mà có thể tìm kiếm ra tiếng việt không dấu. VD: mình nhập :”ca” thì listView sẽ trả về như “cá, cạ, cà, cả, cạ”.
    Có khả thi không bạn !!! Giúp mình với nhé !!!

  13. Hoàng Minh February 19, 2013 at 10:37 AM

    Ồ, cái này được chứ, class của mình viết nhằm mục đích này mà. Bạn cứ thử đi nha.

  14. tiendat February 26, 2013 at 7:12 PM

    cho mình xin code vd này được ko bạn

  15. Hoàng Minh February 28, 2013 at 8:57 AM

    Do có nhiều bạn thắc mắc về code này, nên mình đã up code lên phần cuối của bài viết rồi nha

  16. Van Phan April 23, 2013 at 2:11 PM

    Có thể cho mình xin source code demo đầy đủ được không bạn (cách áp dụng nó vào sự kiện addTextChangedListener như bài này https://tranhoangminh.wordpress.com/2012/11/22/them-chuc-nang-search-vao-listview-trong-android/)

    dinhvanp.it@gmail.com
    Tks bạn rất nhiều!

  17. Hoàng Minh April 25, 2013 at 4:28 PM

    Code của phần này mình đã up cho các bạn rồi mà. Hiện giờ mình khá bận rộn nên chưa coi lại xem về phần ứng dụng nó như thế nào. Nhưng sơ sơ là khi bạn có thể sử dụng Filter cho sự kiện textChange, thì giờ bạn sẽ đưa phần này vào trong Filter ấy.

    Mỗi khi so sánh với dòng dữ liệu nào thì bạn chuyển nó thành tiếng Việt không dấu đi rồi so sánh thôi.

    Một cách khác là khi người dùng chuẩn bị cần search thì bạn chuyển toàn bộ dữ liệu cần search thành tiếng việt không dấu đi, rồi chơi với nó thôi.

    Tùy mỗi người áp dụng như thế nào nhé.
    Nếu không được nữa thì mình sẽ làm cho một demo.

  18. Pingback: Sử dụng ListView căn bản | Lập Trình Căn Bản

  19. Võ Minh Trí June 14, 2013 at 10:52 PM

    anh ơi tại đoạn này, em gặp một lỗi
    if (crtUn.ConvertString(si.getName().toLowerCase())
    .contains(prefix.toString().toLowerCase()) == true) {
    i.add(si);
    ConvertString là kiểu String trong khi đoạn si.getName().toLowerCase())
    .contains(prefix.toString().toLowerCase()) là boolean
    mặc dù để giá trị là == true xong nó vẫn báo lỗi
    Anh có thể giúp em được không, cảm ơn anh nhiều

  20. Võ Minh Trí June 14, 2013 at 11:37 PM

    được rồi anh ơi, quên mất là .contains là nó thành boolean, em không để ý cái dấu ngoặc, thanks anh vì bài viết

  21. Hoàng Minh June 18, 2013 at 9:11 AM

    hông có chi.hihi

  22. hung April 18, 2014 at 5:59 PM

    xin code demo đi ad ơi

  23. hung April 18, 2014 at 6:01 PM

    ad có thể demo sớm cho mình dk ko ? mình đang cần gấp lắm

  24. Minh Trần April 18, 2014 at 11:16 PM

    code có trên bài viết hết rồi mà ta!

  25. hung April 20, 2014 at 4:14 PM

    https://tranhoangminh.wordpress.com/2012/11/22/them-chuc-nang-search-vao-listview-trong-android/)
    có thể demo 1 bài giống như demo của địa chỉ trên được không ạ.

  26. None May 7, 2015 at 9:55 PM

    Dùng swish – case nhanh hơn đó bồ

  27. trunghieu August 29, 2017 at 10:16 AM

    cho mình hỏi chút phần code này có tác dụng gì bạn nhỉ ? bạn giải thích hộ mình với

    private char GetAlterChar(char pC) {
    if ((int) pC == 32) {
    return ‘ ‘;
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: