listview的使用
這篇講在koltin中如何使用listview,跟java程式碼比起來又會有哪些不同呢 先用java程式碼建立一個bookAdapter.繼承自BaseAdapter,實現那固定的幾個方法,貼一下程式碼:
public class BookAdapter2 extends BaseAdapter {
private List<Book> books = new ArrayList<>();
@Override
public int getCount() {
return books.size();
}
@Override
public Object getItem(int position) {
return books.get(position);
}
@Override
public long getItemId(int id) {
return id;
}
@Override
public View getView(int position, View convertView, ViewGroup container) {
ViewHolder viewHolder;
Book book = (Book) getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(container.getContext()).inflate(R.layout.view_book_list_item, container, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.author.setText(book.getAuthor());
viewHolder.name.setText(book.getName());
return convertView;
}
static class ViewHolder {
TextView author;
TextView name;
ViewHolder(View itemView) {
author = itemView.findViewById(R.id.author);
name = itemView.findViewById(R.id.name);
}
}
}
複製程式碼
adapter裡面最關鍵的程式碼就是getView了。那我們看看轉換成kotlin之後改怎麼寫,筆者這裡沒有使用一鍵轉換,有點不太靠譜,還不如自己手敲,看一下得到的最原始的程式碼
override fun getView(position: Int, convertView: View?, container: ViewGroup): View {
val data = getItem(position) as Book
val view: View
val viewHolder: ViewHolder
if (convertView == null) {
view = LayoutInflater.from(container.context).inflate(R.layout.view_book_list_item, container, false)
viewHolder = ViewHolder(view)
view.tag = viewHolder
} else {
view = convertView
viewHolder = view.tag as ViewHolder
}
viewHolder.name.text = data.name
viewHolder.author.text = data.author
return view
}
複製程式碼
getView方法在轉換為java方法的時候,override fun getView(position: Int, convertView: View?, container: ViewGroup): View{}
後兩個引數都是可空的,而且引數都是常量了,不能夠當做變數用,這個地方得注意一下。
在這裡,由於converView可能為null,而且是常量,不能修改值,那麼原來的返回converView就不行了,得重新定義一個view,在convertView為空的時候載入佈局,非空的時候把converView賦值給view。以上的程式碼寫法,是初學使用kotlin的寫法,但是程式碼看起來也還是清晰的。。
那麼我們是否有辦法修改優化呢,變得更加函式式風格一點呢。
override fun getView(position: Int, convertView: View?, container: ViewGroup): View {
val data = getItem(position) as Book
val view: View = convertView ?: LayoutInflater.from(container.context).inflate(R.layout.view_book_list_item, container, false).apply {
tag = ViewHolder(this)
}
(view.tag as ViewHolder).apply {
name.text = data.name
author.text = data.author
}
return view
}
複製程式碼
直接看這麼一段程式碼,這個就是我們做了一定程度的程式碼優化,是的看起來更加偏函數語言程式設計,思路是直接對view賦值,根據converView是否為空做區分,不為空直接複製,為空的話,先載入佈局同時執行apply{}程式碼塊,設定viewHolder。 然後就是講viewHolder取出來了,對控制元件進行賦值,思路其實跟最開始的koltin的思路是一樣的,只不過程式碼風格上面變化了。 那麼是否可以再進一步演進呢。
override fun getView(position: Int, convertView: View?, container: ViewGroup): View {
val data = getItem(position) as Book
return (convertView ?: LayoutInflater.from(container.context).inflate(R.layout.view_book_list_item, container, false).apply {
tag = ViewHolder(this)
}).apply {
(tag as ViewHolder).apply {
name.text = data.name
author.text = data.author
//this@apply.container.isSelected = selectedBooks.contains(data)
}
}
}
複製程式碼
這樣看起來就只定義了一個data常量,然後就直接return了convertView。顯得挺簡潔了。 當然了這樣的程式碼看起來可能不是很直觀,因為if條件不是很明顯,沒有java程式碼寫出來的邏輯清晰分明,條件判斷區分的很顯眼。需要團隊成員熟悉這種寫法才好。初看這段程式碼幾乎是一臉懵逼的,都不知道為啥這樣apply{}.apply{}的巢狀程式碼,而且直接引用的是類屬性,各種this之類的,不好好的分析程式碼,肯定會很困惑的,如果專案裡面類似這樣的程式碼多了,新成員剛接觸肯定很不適應的。在這裡就得扯遠一點了,如何讓程式碼變得清晰可讀並且簡潔?
個人有一些看法,清晰可讀簡潔,我覺得得從邏輯的角度上來說
邏輯上得清晰,無論是if條件判斷,多重if/else判斷,還是迴圈巢狀,遞迴,或者是其他的設計模式等邏輯上一定得清晰,讀程式碼的人能夠知道你是怎麼區分的,依據條件是什麼,是否分類的完善,邏輯跳轉是否合理正常等。
程式碼可讀性強,寫出來的程式碼,讓團隊成員看著不困惑,就如同知名的開源專案那樣,大神寫出來的java程式碼,基本上做過java開發的同學都是能夠看懂的,java語言決定了它的程式碼寫出來的就是這樣的套路,只不過在大神的手裡,寫出來可讀性,邏輯性都很高。讓人覺得程式碼寫的很漂亮,很清爽。程式碼的流轉執行過程比較符合正常人的思維習慣等,這樣的程式碼容易給開發者一種親切感
簡潔,簡潔還是得是邏輯上簡潔,更多的時候不要過度沉迷於程式碼層面了,比如說,某某一個驗證碼控制元件,不同業務都有自己的特殊場景,這種情況下不好複用的話,就按業務拆離,不要為了節省程式碼,而將這兩三個業務的程式碼都放在一起,而且還堆纏在一起,這樣不太好,還不如為每一個業務做一個獨立的封裝,出現部分重複的程式碼是可以接受的,一定要記住,我們需要的是邏輯上的簡潔,在舉個例子形容,比如說圖片載入元件picasso,或者是Glide等,我在專案中使用了其中一個,那麼我需要考慮到以後存在換元件的問題而封裝一個抽象層嗎(當然了這個例子可能舉的不好,但是這裡我還是想說下這個),我覺得是沒有必要的,這種元件api已經足夠簡單了,使用也是極其方便,而且還很方面開發者擴充套件一些特殊的需求,比如說載入通知欄的icon等場景,使用元件原生api已經足夠方便,足夠簡潔了,我覺得在做封裝是沒有必要的,等你真正覺得需要換的時候,半天到一天就換過來了,畢竟我也是做過這種工作,不覺得有多麻煩。更何況做的封裝,不見的能夠滿足所有的場景,難道等不滿足的時候在重新寫一個新的方法嗎,或者就是直接呼叫原生元件的api呢? 反正我覺得沒必要。這就是我的技術理念。