25 tháng 2, 2013

Tối ưu hóa layout trong Android (1)

Ngày nay việc phát triển ứng dụng trên di động không còn là gì quá mới mẻ, tuy nhiên, việc làm một ứng dụng thực sự tốt vẫn không phải là việc đơn giản, với những ứng dụng có giao diện phức tạp thì việc thiết kế layout sẽ ảnh hưởng khá lớn tới độ mượt của ứng dụng đó. Do vậy, tui sẽ làm một số bài viết về tối ưu hóa layout.
Trước tiên, các bạn cần nắm được sơ bộ quá trình tính toán và vẽ layout lên màn hình với Android diễn ra như thế nào (đọc topic How Android Draws Views) và những topic về tăng hiệu quả layout cũng là cực kỳ cần thiết phải đọc (xem link này). Đây là những kiến thức căn bản nhưng rất quan trọng để bạn có thể hiểu rõ về UI framework của Android, tới đây, tui mặc định là các bạn đã nắm được sơ bộ những kiến thức trên rồi nhé.
Bài viết này sẽ đề cập tới sự ảnh hưởng của việc qui định kích thước các child view tới tốc độ cập nhật layout khi có thay đổi trong nội dung hiển thị. Ta có một activity với layout định nghĩ như sau:


<tvtbinh.bino.demo.drawsviewsflow.LoggerLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/tvtbinh.bino.demo.drawsviewsflow"
    android:id="@+id/root"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:log_tag_linear="Layout_Root" >

    <tvtbinh.bino.demo.drawsviewsflow.LoggerLinearLayout
        android:id="@+id/layout_1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        app:log_tag_linear="Layout_1" >

        <tvtbinh.bino.demo.drawsviewsflow.LoggerTextView
            android:id="@+id/textview_1_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="5dip"
            android:text="TextView 1_2"
            app:log_tag_textview="TextView_1_2" />

        <tvtbinh.bino.demo.drawsviewsflow.LoggerTextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:text="TextView 1_3"
            app:log_tag_textview="TextView_1_3" />

    </tvtbinh.bino.demo.drawsviewsflow.LoggerLinearLayout>

    <tvtbinh.bino.demo.drawsviewsflow.LoggerButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onButtonClicked"
        android:text="Button 1_1"
        app:log_tag_button="Button_1_1" />

</tvtbinh.bino.demo.drawsviewsflow.LoggerLinearLayout>

(Mấy cái class được kế thừa với cái tên Logger thêm vào đầu chỉ có một chức năng duy nhất là mỗi lần phương thức onLayout, onMeasure, onDraw được gọi thì nó xuất log ra các thông số liên quan thôi, ngoài ra nó ko khác gì class gốc hết)

Activity này làm một việc khá đơn giản là mỗi khi bấm vào cai nút trên màn hình thì nó thay đổi nội dung của cái TextView đầu tiên, lý do vì sao bạn sẽ hiểu vào cuối bài viết. Để tính chính xác thì tui bấm vào, giữ nút đó một tí sau đó thả tay ra (lúc này sự kiện onLick mới được bắt đầu tính) đợi tới khi tất cả các view trong layout được vẽ lại hết (hàm onDraw được gọi xong).
Với layout này, thống kê trên máy nexus one chạy Android 2.3.6 thì thời gian để vẽ lai layout sau khi bấm nút vào khoảng 20 millisecond.

Sau đó, tui chỉnh lại layout một chút ở 2 cái TextView như sau:

<tvtbinh.bino.demo.drawsviewsflow.LoggerTextView
            android:id="@+id/textview_1_2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:paddingLeft="5dip"
            android:text="TextView 1_2"
            app:log_tag_textview="TextView_1_2" />

<tvtbinh.bino.demo.drawsviewsflow.LoggerTextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:text="TextView 1_3"
            app:log_tag_textview="TextView_1_3" />

Lặp lại các bước tương tự như trên thì thời gian để vẽ lại layout sau khi bấm nút vào khoảng 10 millisecond  Wow, xem ra hiệu quả hơn khá nhiều, vậy sự khác biết này ở đâu mà có?
Khi kiểm tra log kỹ càng thì thấy chênh lệch 10 millisecond này chính là khoảng thời gian dành cho các thao tác tính lại kích thước của các child view và sắp đặt lại chúng trên màn hình, với cách thứ 2, các kích thước và vị trí không có sự thay đổi và layout cha dựa vào các thuộc tính chúng ta khai báo để hiểu được điều đó, khi cần cập nhật lại chỉ có mỗi việc gọi hàm onDraw của các child view. Các bạn xem log:

02-25 10:17:47.087: E/DrawFlow(4321): Clicked
02-25 10:17:47.087: I/Layout_Root(4321): onMeasure(MeasureSpec: EXACTLY 320, MeasureSpec: AT_MOST 533)
02-25 10:17:47.087: I/Layout_Root(4321): onMeasure( width = 320, height = 67) start {
02-25 10:17:47.087: I/Layout_1(4321): onMeasure(MeasureSpec: EXACTLY 320, MeasureSpec: AT_MOST 533)
02-25 10:17:47.087: I/Layout_1(4321): onMeasure( width = 320, height = 19) start {
02-25 10:17:47.087: I/TextView_1_2(4321): onMeasure(MeasureSpec: AT_MOST 320, MeasureSpec: AT_MOST 533)
02-25 10:17:47.087: I/TextView_1_2(4321): onMeasure( width = 77, height = 19) start {
02-25 10:17:47.087: I/TextView_1_2(4321): onMeasure( width = 128, height = 19) end }
02-25 10:17:47.087: I/TextView_1_2(4321): onMeasure --- finished
02-25 10:17:47.087: I/TextView_1_3(4321): onMeasure(MeasureSpec: EXACTLY 192, MeasureSpec: AT_MOST 533)
02-25 10:17:47.087: I/TextView_1_3(4321): onMeasure( width = 243, height = 19) start {
02-25 10:17:47.087: I/TextView_1_3(4321): onMeasure( width = 192, height = 19) end }
02-25 10:17:47.087: I/TextView_1_3(4321): onMeasure --- finished
02-25 10:17:47.087: I/Layout_1(4321): onMeasure( width = 320, height = 19) end }
02-25 10:17:47.087: I/Layout_1(4321): onMeasure --- finished
02-25 10:17:47.097: I/Layout_Root(4321): onMeasure( width = 320, height = 67) end }
02-25 10:17:47.097: I/Layout_Root(4321): onMeasure --- finished
02-25 10:17:47.097: I/Layout_Root(4321): onLayout(changed = false, l = 0, t = + 0, r = 320, b = + 67)
02-25 10:17:47.097: I/Layout_1(4321): onLayout(changed = false, l = 0, t = + 0, r = 320, b = + 19)
02-25 10:17:47.097: I/TextView_1_2(4321): onLayout(changed = true, l = 0, t = + 0, r = 128, b = + 19)
02-25 10:17:47.097: I/TextView_1_3(4321): onLayout(changed = true, l = 128, t = + 0, r = 320, b = + 19)
02-25 10:17:47.097: I/TextView_1_2(4321): onDraw()
02-25 10:17:47.107: I/TextView_1_3(4321): onDraw()
02-25 10:17:47.107: I/Button_1_1(4321): onDraw()

Như vậy, với cách thiết kế layout ban đầu, ta khai báo chiều rộng của TextView đầu tiên là wrap_content và TextView kế bên là fill_parent, đồng nghĩa với việc mỗi lần TextView đầu tiên có sự thay đổi nội dung thì kích thước của nó thay đổi theo nội dung và kích thước của TextView kế bên cũng thay đổi theo tương ứng, cũng thấy là vì chiều cao không đổi nên sau khi có kích thước của TextView thì layout cha ko yêu cầu tính lại kích thước của button vì ko có sự ảnh hưởng.
Với một layout đơn giản đã vậy thì với một layout có nhiều nội dung cần hiển thị và nhiều control để tương tác hơn thì thời gian để tính toán lại layout trước khi vẽ càng dài hơn, đôi khi dẫn đến việc layout ko được mượt nếu thiết kế ko tốt. Ở đây, cách giải quyết thứ 2 của tui chưa phải là tốt nhất, chỉ là dùng LinearLayout thì các bạn dễ đọc và hình dung thôi, các bạn có thể thử dùng RelativeLayout.

Vậy qua bài viết này giúp các bạn hiểu rõ hơn về cách Android UI framework vẽ các view và layout lên màn hình như thế nào và từ đó giúp chúng ta thiết kế layout hiệu quả để có một ứng dụng tương tác mượt mà hơn.


26 tháng 8, 2011

Chọn công việc (chọn công ty)


Nếu bạn đi phỏng vấn vào một công ty cho một vị trí bắt đầu được gọi là quan trọng (từ team leader trở lên) thì ngoài các vấn đề thông thường cần nắm như lương bổng, tình hình dự án, môi trường làm việc, thì mình góp vào một số vấn đề cần chú ý sau:

1. Bạn sẽ làm việc với ai?
Bạn cần xác định rõ nếu vào công ty bạn sẽ làm việc cũng nhóm với ai, khả năng của họ ra sao. Sếp trực tiếp của bạn là ai, có đủ khả năng thuyết đối với bạn hay không. Sếp lớn của công ty như thế nào, có đủ tầm để lãnh đạo công ty vươn xa hay hay không, có một hoài bão thực sự lớn và một định hướng đủ tốt hay không? Ban lãnh đạo của công ty có khả năng đánh giá chính xác năng lực của các nhân viên trong công ty hay không.

2. Bộ sậu chính của công ty như thế nào?
Nếu không phải là A-team thì cũng cần đủ mạnh về năng lực nội tại để cạnh tranh với các đối thủ khác khi công ty lớn dần, hẳn bạn sẽ không muốn làm việc với một bộ sậu mà không đủ năng lực cạnh tranh với các đối thủ lớn. Đặc biệt với các công ty start up, những người đi đầu rất quan trọng, nếu sau này công ty phát triển lên, những người đó không đủ tầm để đi cùng sự phát triển của công ty thì sẽ rất khó khăn trong việc giải quyết vấn đề nhân sự giữa người cũ với người mới. Bạn không thể thành công nếu chỉ làm một mình, và nếu bạn không xem xét nghiêm túc vấn đề này, thì cho dù bạn làm tốt tới đâu kết quả chung của toàn công ty cũng vẫn không được cải thiện nếu các bộ phận khác làm không đủ tốt, và ngược lại.

3. Con đường phát triển sự nghiệp của bạn sẽ ra sao sau 2 năm, 5 năm nếu bạn làm việc tốt?
Bạn có thể tham gia một công ty nhưng toàn bộ những vị trí chính yếu của công ty đã có người nắm hoặc bạn được giao làm teamleader nhưng bên trên bạn có 1 programmer lead và 2 CTO cùng một số vị trí quản lý trực tiếp thì liệu sau 5 năm phấn đấu bạn sẽ vươn tới được đâu? Công ty có đầu tư cho bạn phát triển kỹ năng hay không, cam kết đó rõ ràng tới mức nào hay chỉ dừng ở việc nói có và sẽ suy xét?

4. Thu nhập tương lai của bạn ra sao?
Nhiều công ty đưa ra cái bẫy thu nhập mà ứng viên sẽ bị mắc kẹt trong đó, đó là mức lương cao nhưng thưởng gần như không có, các lợi ích khác ngoài tiền như cổ phần, bảo hiểm (loại bảo hiểm thực sự tốt ấy), thiết bị làm việc đều không có hoặc rất hiếm hoi. Bạn nên cân nhắc và làm rõ vấn đề này. Tôi đã từng từ chối một công ty offer mức lương cao gấp đôi mức lương hiện tại chỉ bởi vì một năm chỉ có 14 tháng lương, không có gì khác, khi tôi hỏi nếu tôi phát triển bộ phận này thành công, mở rộng qui mô, đạt nhiều thành quả thì tôi có được hương cổ phần của công ty hay không, và dĩ nhiên là không, vậy nên tôi từ chối cơ hội đó. Dĩ nhiên, vấn đề tiền ở tương lai luôn có rủi ro vì không ai chắc chắn được, nhưng cái này thì tùy từng trường hợp và tùy từng đánh giá của mỗi cá nhân.

5. Và cuối cùng, đừng quên bạn có 2 tháng thử việc để xác định xem quyết định của mình có đúng hay không
Đôi khi việc bỏ qua 2 tháng thử việc thể hiện sự tin tưởng lần nhau giữa bạn và công ty, nhưng đôi khi, 2 tháng thử việc là thực sự cần thiết nếu bạn thấy trong những điều trên có điều nào đó mà bạn chưa chắc chắn. Đôi khi mọi thứ có vẻ đều ổn khi nhìn từ bên ngoài, nhưng ở trong chăn mới biết chăn có rận, bạn tham gia công ty thì muốn mọi người cùng đoàn kết để hướng tới kết quả chung, nếu công ty có mâu thuận nội bộ thì coi như xong: nhân viên làm việc không phối hợp tốt, chỉ chăm chăm đấu đá, chia bè kết phái để thanh trừng lần nhau… điều đó quả thật rất đáng sợ, và rất hay xảy ra ở các ông ty start up, nơi mà tốc độ phát triển rất nhanh, mâu thuẫn về lợi ích thường xuất hiện khi công ty to ra mà sếp không đủ tinh ý để giải quyết các vấn đề này ngay từ đầu. Vậy nên, 2 tháng thử việc thực sự là quan trọng nếu bạn thấy chư đủ chắc chắn.

Chúc phỏng bạn tìm được một công việc như ý!

02 tháng 8, 2011

Barcamp Saigon 24 tháng 7, một ngày ý nghĩa, học được nhiều điều

Hôm nay đi Barcamp Sài Gòn, vòng vòng nghe nhiều topic và xem xét tình hình nên cũng ngộ ra được nhiều điều, ghi chép lại và cũng để chia sẻ luôn.

  1. Các công ty công nghệ muốn lớn mạnh thì phải có một đội ngũ nòng cốt cực mạnh và được đầu tư đúng mức. Cái này quá rõ ràng. Khi nghe các topic của VNG và SkunkWorks thì thấy rõ đội ngũ của họ rất mạnh và mạnh toàn diện: server, client (mobile), web, system admin, product team… Như SkunkWorks, nghe về gamification thấy rõ được cái cách họ đưa ra các tiêu chí phát triển sản phẩm, cách đánh giá và học hỏi từ các sản phẩm thành công. Với VNG, đó là khả năng thiết kế hệ thống server với scalability để đáp ứng một lượng cực lớn user mà vẫn hoạt động tốt… nói chung nghe xong là nể. Dĩ nhiên, những người này cũng nhận được những mức đãi ngộ đủ tốt để họ yên lòng chuyên tâm vào công việc và điều kiện làm việc thì được hỗ trợ đầy đủ rồi.
  2. Các sự kiện trong giới công nghệ nói chung là cơ hội tốt để quảng bá hình ảnh của các công ty, đơn cử như barcamp đợt này, lại là SkunkWorks và VNG đã rất thành công, vì đã có những topic hay vũng như thể hiển được năng lực của người đứng nói, qua đó cho thấy hình ảnh công ty có một nền móng tốt, môi trường chuyên nghiệp và có thể vươn xa, cũng như cho thấy vào đó sẽ có nhiều cơ hội phát triển bản thân. Ngược lại, thật tiếc cho JoomlArt và East Agile, bản thân 2 công ty này không hề kém, nhưng những topic họ nói cho thấy sự thiếu chuẩn bị, người nghe không nhận được gì nhiều từ topic cũng như không thể hiện được năng lực của người thuyết trình nói riêng và đội ngũ công ty nói chung. Nói vậy chứ mình biết East Agile là cty có môi trường làm việc và mức đãi ngộ rất tốt ở SG này.
  3. Cơ hội tuyển dụng là ở đây. Khác với các hội chợ việc làm nơi các công ty có các sạp và chỉ quảng cáo về mình qua các tờ rơi, các sản phẩm, và ứng viên nhận thông tin, đăng ký và chờ phỏng vấn. Ở Barcamp, các công ty cho thấy được sức mạnh, tiềm năng của họ qua các topic, người tham gia có cơ hội nhìn nhận tốt hơn. Ngược lại những head hunter cũng được thấy những người có năng lực qua các topic mà họ thuyết trình hoặc qua cách họ đặt câu hỏi, trao đổi với nhau về các vấn đề công nghệ… hoặc có được những reference khá chính xác về một người qua cách trao đổi với họ hoặc những người trong cuộc. Cá nhân mình thấy những người tham gia barcamp thường là những người trẻ năng động, ham học hỏi hoặc là những người đã ở một trình độ nhất định nào đó trong giới công nghệ, nên chất lượng tuyển dụng ở đây sẽ tốt hơn.
  4. Cơ hội việc làm. Rõ ràng, các công ty tới đây quảng bá hình ảnh của mình cũng chỉ trong mục đích tìm người (người làm hoặc đối tác). Và điều hay là khá nhiều cty start up tham gia, cơ hội làm việc tại một cty start up là cơ hội lớn, tốt hơn rất nhiều so với việc tham gia một cty đã đủ to và có các vị trí chủ chốt. Nếu để ý sẽ thấy SkunkWorks cài người vào các topic hay để ngay cuối topic họ lại xin thông tin liên lạc của diễn giả, dĩ nhiên mục đích chính vlà gì thì ai cũng rõ. Ngoài ra, nếu ai ở lại cuối để nghe topic về cách làm một CV ấn tượng sẽ thấy ngay đó là một cơ hội lớn để tham gia một cty start up trụ sở tại Singapore, cho cả sinh viên thực tập luôn nhé.
  5. Dĩ nhiên, đây cũng là cơ hội tăng kiến thức. Hôm nay có 3 topic mà mình nghe thấy giúp cho mình rất nhiều: ZingMe webchat architect (nghe nhiều kinh nghiệm hay về server scalability), Gamification (thiết kế game cần chú ý các tiêu chí nào, các case study chuẩn), How to write a cv/resumes that gets you hired in a startup (kinh nghiệm làm cv đơn giản, hiệu quả và hướng tới đối tượng tuyển dụng). Ngoài ra còn có một số topic hay nhưng mình không có thời gian nghe nên không liệt kê ra ở đây.

Nhìn chung lại thì hôm nay là một ngày ý nghĩa, mình đúc rút được nhiều điều dù trước đây đã thấy nhưng chưa hề hệ thống lại. Cũng có nhiều suy nghĩ mới. Nhưng ngay trước mắt, mình nên cập nhật CV cho tốt hơn, chứ như hiện tại thì chưa đủ tốt, nghe xong thì phải làm ngay chứ, vậy mới có tinh thần học hỏi http://www.linkedin.com/in/tranvutatbinh


Danh sách các topic tại đây

13 tháng 1, 2011

Ví dụ làm loading ở cuối ListView và tự động load thêm dữ liệu



Nếu các bạn dùng ứng dụng Gmail, Android Market thì sẽ thấy trong các listview ở dưới cùng đều có hiện một cái loading animation sẽ load thêm dữ liệu khi mình scroll xuống tới cái loading đó. Hôm nay mình sẽ demo cách để làm việc này, khá đơn giản thôi.
Các bạn có sẵn code của list14 trong ApiDemos rồi, copy cái adapter đó ra dùng cho tiện nhé. Trong cái adapter đó, các bạn thêm giúp một hàm appendItems() để có thể nối thêm dữ liệu vào adapter hiện hành:
public void appendItems(ArrayList items) {
mItems.addAll(items);
notifyDataSetChanged();
}


Với ListView của mình, các bạn cần thêm vào một FooterView chính là cái hiển thị loading đó, lưu ý cơ bản là chỉ có thể thêm footer/header vào trong ListView trước khi setAdapter, xòn xóa thì lúc nào cũng được

LayoutInflater inflater = LayoutInflater.from(this);
mLoadMoreFooter = inflater.inflate(R.layout.loading_footer_item, null);
getListView().addFooterView(mLoadMoreFooter);

Rồi, giờ cần bắt sự kiện khi nào mình scroll list tới cuối và cái loading được hiện lên thì mình sẽ load thêm dữ liệu mới và add vào list, trong sự kiện onScroll() của ListView mình sẽ xử lý như sau:

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// minus 1 here because the total list item count include the
// loading footer
if (view.getLastVisiblePosition() >= totalItemCount - 1)
checkLoadMore();
}

Rồi, cái hàm checkLoadMore đơn giản chỉ là kiểm tra xem lúc đó có đang load dữ liệu chưa, dĩ nhiên nếu đang load dữ liệu rồi thì mình ko load thêm làm gì nữa, còn nếu chưa thì mình bắt đầu load thêm dữ liệu, vậy thôi
private void checkLoadMore() {
if (mLoading)
return;

/*
* if the loading footer is still there, then we will load more items
*/
if (getListView().getFooterViewsCount() != 0) {
loadMoreItems();
}
}

private void loadMoreItems() {
mLoading = true;

new Thread() {
public void run() {
try {
// in this example, we just simply wait for 2 seconds
Thread.sleep(2000);
Message msg = mMainhandler.obtainMessage(
MSG_APPEND_MORE_ITEMS, generateList());
mMainhandler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}

Hàm loadMoreItems() thì vì đây là ví dụ nên mình chỉ cho làm mỗi việc là đợi 2 giây rồi tạo dữ liệu giả ra, coi như là đã lấy được dữ liệu từ server về rồi chẳng hạn. Hàm generateList() tạo ra một list với 15 String.
Vì mình load dữ liệu ở một thread riêng biệt, mà khi muốn cập nhật trên giao diện (chụ thể ở đây là ListView) thì mình cần phải làm trong main thread, nên mình sẽ gửi một message tới handler của main thread để xử lý.
switch (msg.what) {
case MSG_APPEND_MORE_ITEMS:
ArrayList newData = (ArrayList) msg.obj;
if (mAdapter == null) {
mAdapter = new SimpleAdapter(LoadingListDemoActivity.this,
newData);
setListAdapter(mAdapter);
} else {
mAdapter.appendItems(newData);
}

// if the list we get from server is smaller than 15 (in this
// example)
// then that means there's nothing more to get, remove the
// loading item now
if (newData.size() <>
getListView().removeFooterView(mLoadMoreFooter);
}
mLoading = false;
break;
}

Đoạn code ở trên là: nếu như dữ liệu lấy về không đủ số lượng đã yêu cầu thì hiểu như là đã khôngcòn gì để load thêm nữa, vậy thì sẽ xóa cái loading ra khỏi list, vậy là xong :)

Đây là toàn bộ code của Activity:

package tvtbinh.bino.demo.loadingfooter;

import java.util.ArrayList;

import android.app.ListActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;

public class LoadingListDemoActivity extends ListActivity {
private SimpleAdapter mAdapter;
private View mLoadMoreFooter;

private boolean mLoading;

private static final int MSG_APPEND_MORE_ITEMS = 0;

private static final int NUMBER_OF_ITEMS_REQUESTED = 15;

/**
* Use this field to count how many trunks that we have loaded if it's more
* than 4 then we will not load anymore
*/
private int mTrunksCount = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// inflate the loading item and add to the ListView
// we all know that the footers/headers must be addded before setting
// the adapter for ListView
LayoutInflater inflater = LayoutInflater.from(this);
mLoadMoreFooter = inflater.inflate(R.layout.loading_footer_item, null);
getListView().addFooterView(mLoadMoreFooter);

getListView().setOnScrollListener(mScrollListener);

mLoading = false;

mAdapter = new SimpleAdapter(LoadingListDemoActivity.this,
new ArrayList());
setListAdapter(mAdapter);
}

/**
* A mocking method for getting data from some source asynchronously
*/
private void loadMoreItems() {
mLoading = true;

new Thread() {
public void run() {
try {
// in this example, we just simply wait for 2 seconds
Thread.sleep(2000);
Message msg = mMainhandler.obtainMessage(
MSG_APPEND_MORE_ITEMS, generateList());
mMainhandler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
}

/**
* Generate an arrayList with 15 strings
*/
private ArrayList generateList() {
ArrayList results = new ArrayList();

mTrunksCount++;
if (mTrunksCount <= 4) {
for (int i = 0; i <>
results.add(String.valueOf(System.currentTimeMillis()));
}
}
return results;
}

private Handler mMainhandler = new Handler() {
public void handleMessage(Message msg) {

switch (msg.what) {
case MSG_APPEND_MORE_ITEMS:
ArrayList newData = (ArrayList) msg.obj;
if (mAdapter == null) {
mAdapter = new SimpleAdapter(LoadingListDemoActivity.this,
newData);
setListAdapter(mAdapter);
} else {
mAdapter.appendItems(newData);
}

// if the list we get from server is smaller than 15 (in this
// example)
// then that means there's nothing more to get, remove the
// loading item now
if (newData.size() <>
getListView().removeFooterView(mLoadMoreFooter);
}
mLoading = false;
break;
}
};
};

/**
* Check if should load more item at this time
*/
private void checkLoadMore() {
if (mLoading)
return;

/*
* if the loading footer is still there, then we will load more items
*/
if (getListView().getFooterViewsCount() != 0) {
loadMoreItems();
}
}

private OnScrollListener mScrollListener = new OnScrollListener() {

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// We have nothing to do with this right now
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// minus 1 here because the total list item count include the
// loading footer
if (view.getLastVisiblePosition() >= totalItemCount - 1)
checkLoadMore();
}
};

private static class SimpleAdapter extends BaseAdapter {
private ArrayList mItems;
private LayoutInflater mInflater;
private Bitmap mIcon1;
private Bitmap mIcon2;

public SimpleAdapter(Context context, ArrayList items) {
mItems = new ArrayList(items);
mInflater = LayoutInflater.from(context);

// Icons bound to the rows.
mIcon1 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon48x48_2);
}

public void appendItems(ArrayList items) {
mItems.addAll(items);
notifyDataSetChanged();
}

@Override
public int getCount() {
if (mItems == null) {
return 0;
}
return mItems.size();
}

@Override
public Object getItem(int position) {
return mItems.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;

if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text,
null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

holder.text.setText(mItems.get(position));
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);

return convertView;
}

static class ViewHolder {
TextView text;
ImageView icon;
}

}
}

14 tháng 2, 2009

Chương trình chặn tin nhắn

Mình viết chương trình này chỉ mang tính chất thử nghiệm, giao diện bằng tiếng Việt, không chau chuốt nhiều và đang trong giai đoạn mời dùng thử nên có gì các bạn cho ý kiến nhé.
Chương trình này cho phép nhập vào một số điện thoại cần chặn tin nhắn hoặc lấy trực tiếp từ danh bạ. Tuy nhiên, vì cấu trúc bảo mật của Android nên các bạn phải tắt chế độ tự động báo tin nhắn đến của ứng dụng Messaging (chương trình của mình sẽ lo chuyện này), cách tắt như sau:
-Vào ứng dụng messaging, chọn menu, setting:

-Sau đó bỏ chọn cái thông báo khi có tin nhắn mới:

Vậy là xong!

Mình rất lưu ý là đây chỉ là bản dùng thử, chức năng chính tất nhiên là chạy ổn, đã test thử. Tuy nhiên, rất có thể còn thiếu sót, mong nhận được góp ý!

Link down tại đây:
http://rapidshare.com/files/198036278/Block_SMS.apk

29 tháng 1, 2009

Ứng dụng nhắn tin bằng tiếng Việt

Mấy hôm nay Tết về quê chơi cũng rảnh rỗi nên tranh thủ chút thời gian viết một ứng dụng nhỏ hy vọng giúp ích cho cộng đồng Android Việt Nam.
Ứng dụng này hỗ trợ gửi tin nhắn bằng tiếng Việt, thực ra chủ yếu là gõ tiếng Việt thôi, gửi tin nhắn là làm cho có chức năng, chứ chỉ gõ tiếng Việt không thì cũng kì kì. Mà cái bộ gõ này do tui tự viết, lại chỉ tranh thủ thời gian rảnh rỗi nên nó cũng bưởi lắm, nhưng đại khái là dùng được (còn bưởi như thế nào thì mọi người dùng sẽ biết vậy).
Lưu ý quan trọng là mấy cái từ có viết hoa dễ bị die lắm:

Đại loại nhìn giao diện như trên, soạn tin tối đa là 160 kí tự (tuy nhiên lưu ý nếu nhắn tiếng Việt mà 160 là thành 2 tin).
Qua màn hình chọn số gửi tin nó nhìn như sau:

Có thể lấy số từ danh bạ hoặc nhập số trực tiếp!
Sau đó bấm gửi tin là xong!

Cái này làm chỉ ở mức có thể dùng được, thực sự là không được tốt cho lắm. Nếu có anh em nào dùng mà muốn sửa chỗ nào thì cho tui ý kiến nhé (có dịa chỉ email trong phần about của ứng dụng)

Ai da, không biết attach file làm sao nữa! Dùng rapidshare vậy :(
http://rapidshare.com/files/190983817/Vietnamese_SMS.apk