編輯:關於Android編程
前面一篇我們介紹了常用的幾種適配器的簡單實現和ListView的簡單使用,這一篇中,我們介紹一下ListView的優化和一些其它的問題。
在ListView中,我們最常用的就是自定義Adapter,在我們自定義Adapter中,需要實現兩個比較重要的方法getCount()和getView(),前者是負責計算ListView的總Item數,後者是生成Item,有多少個Item就會調用getView()方法多少次。getView()方法每次調用的時候都會重新inflate一個View出來返回去,但是對於ListView,只需要保留能夠顯示的最大的View的數目即可,而新的View可以復用消失的View。ListView給我們提供了可復用的View對象,在getView()方法裡面,有一個參數View,這個就是可以復用的View對象。當參數View為null的時候,我們需要inflate一個View,當它不為null的時候,我們可以直接將他返回。例如:
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
//view為空的時候,inflate一個新的view
if (view == null) {
view = LayoutInflater.from(context).inflate(R.layout.item_base_adapter, null);
viewHolder = new ViewHolder();
viewHolder.tv_base_adapter = (TextView) view.findViewById(R.id.tv_base_adapter);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.tv_base_adapter.setText(datas[i]);
//不為空,復用view
return view;
}
上面介紹的是對View復用的優化,這樣我們可以不必每一item都inflate一個新的view,可以通過復用,減小內存開銷。下面我們介紹一個每一個AndroidAPP中都必不可少的操作,獲取控件句柄,簡單的說就是拿到id。在ListView中,我們inflate一個View,裡面也有需要獲取到組件id的,我們可以用ViewHolder來實現優化:
具體的思路就是,我們在ViewHolder中存放我們需要的控件,在View為null的時候,需要inflate一個新的view,同時我們還new一個ViewHolder類的對象,並將findviewById的結果賦值給ViewHolder中對應的成員變量,我們可以調用View中setTag()方法,將ViewHolder和View綁定起來。當view不為null的時候,通過getTag()方法取出ViewHolder對象,這樣就可以獲得ViewHolder中的成員變量,也不再需要調用findViewById方法了。例如:
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
view = LayoutInflater.from(context).inflate(R.layout.item_base_adapter, null);
//view為空,new一個ViewHolder對象
viewHolder = new ViewHolder();
//獲取到ViewHolder對象中成員變量的id
viewHolder.tv_base_adapter = (TextView) view.findViewById(R.id.tv_base_adapter);
//調用setTag方法,將ViewHolder綁定到中
view.setTag(viewHolder);
} else {
//view不為空,調用getTag方法,取出保存的ViewHolder對象
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.tv_base_adapter.setText(datas[i]);
return view;
}
static class ViewHolder {
TextView tv_base_adapter;
}
上面介紹了兩種ListView的優化方法,第二種優化效率根據google官方文檔的解析,可以優化5%左右的效率。下面介紹一下第三種優化方法。
在我們實際開發中,ListView顯示的數據都是在網絡中加載,假如網絡比較好,能一次將所有的數據加載出來,這樣用戶體驗還好,如果網絡不好,那麼加載數據需要時間比較久,用戶體驗就不好。另外,我們知道,虛擬機為每一個進程分配的內存是有限的,如果一下加載太多的數據就會出現內存溢出的情況。為了解決這兩個問題,我們可以用分批加載的方法,但是分批加載還是不能完全解決問題。假如我有10萬條數據需要加載,分批加載任然可能會出現OOM問題,這時,我們需要將數據分頁加載,先分頁加載,然後在分批加載,這樣用戶體驗會好一些。
在一些情況中,我們需要在ListView的Item中添加Button、EditText、CheckBox等控件,這就涉及到了焦點獲取的問題。我們在ListView的Item中添加了Button按鈕,點擊發現,我們不能觸發onItemClick和onItemLongClick方法,這就是ListView的焦點被攔截了。解決辦法也很簡單:
給攔截ListView焦點的控件設置android:focusable=”false”屬性或者代碼調用setFocusable(false) 防范即可。還有一種方法就是在Item布局的根節點設置android:descendantFocusability=”blocksDescendants” 屬性,這個屬性有三個可選值
beforeDescendants:viewgroup會優先其子類控件而獲取到焦點 afterDescendants:viewgroup只有當其子類控件不需要獲取焦點時才獲取焦點 blocksDescendants:viewgroup會覆蓋子類控件而直接獲得焦點這是ListView焦點問題的解決方法。
首先實現一個簡單的ListView
布局代碼:
Item布局代碼:
這裡的Item比較簡單,就直接是一張圖片和一個文字
Activity代碼:
package com.example.listviewdemo.activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import com.example.listviewdemo.MYData;
import com.example.listviewdemo.R;
import com.example.listviewdemo.adapter.DataAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Devin on 2016/7/11.
*/
public class DataUpActivity extends AppCompatActivity {
private Button btn_add_one;
private Button btn_add_on_position;
private Button btn_delete_one;
private Button btn_delete_on_position;
private Button btn_delete_all;
private ListView lv_data;
private DataAdapter adapter;
private List datas;
private int flag = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data);
btn_add_one = (Button) findViewById(R.id.btn_add_one);
btn_add_on_position = (Button) findViewById(R.id.btn_add_on_position);
btn_delete_one = (Button) findViewById(R.id.btn_delete_one);
btn_delete_on_position = (Button) findViewById(R.id.btn_delete_on_position);
btn_delete_all = (Button) findViewById(R.id.btn_delete_all);
lv_data = (ListView) findViewById(R.id.lv_data);
datas = new ArrayList<>();
adapter = new DataAdapter(this, datas);
lv_data.setAdapter(adapter);
}
}
自定義適配器代碼:
package com.example.listviewdemo.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.listviewdemo.MYData;
import com.example.listviewdemo.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Devin on 2016/7/11.
*/
public class DataAdapter extends BaseAdapter {
private List datas;
private Context mContext;
public DataAdapter(Context mContext, List datas) {
this.mContext = mContext;
this.datas = datas;
}
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int i) {
return i;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.item_data, null);
viewHolder = new ViewHolder();
viewHolder.tv_data_text = (TextView) view.findViewById(R.id.tv_data_text);
viewHolder.iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.iv_icon.setImageResource(datas.get(i).getIconId());
viewHolder.tv_data_text.setText(datas.get(i).getContent());
return view;
}
private static class ViewHolder {
TextView tv_data_text;
ImageView iv_icon;
}
}
還有就是一個普通的bean
package com.example.listviewdemo;
/**
* Created by Devin on 2016/7/11.
*/
public class MYData {
private int iconId;
private String content;
public MYData() {
}
public MYData(int iconId, String content) {
this.iconId = iconId;
this.content = content;
}
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Data{" +
"iconId=" + iconId +
", content='" + content + '\'' +
'}';
}
}
這樣,我們運行的效果是:
沒有顯示有Item,因為我們在適配器中添加的是一個空的list,接下來我們實現按鈕的事件和具體更新ListView
在適配器中添加這個方法:
/**
* 添加數據
*
* @param myData
*/
public void addData(MYData myData) {
if (datas == null) {
datas = new ArrayList<>();
}
datas.add(myData);
notifyDataSetChanged();
}
在activity中實現點擊事件:
btn_add_one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MYData data = new MYData(R.drawable.icon, "這是添加的數據~~~~~~X" + flag);
adapter.addData(data);
flag++;
}
});
就可以實現在ListView中添加一條數據,具體的效果圖會在後面統一附上
在適配器中添加如下方法:
/**
* 在指定位置添加數據
*
* @param position
* @param myData
*/
public void addData(int position, MYData myData) {
if (datas == null) {
datas = new ArrayList<>();
}
datas.add(position, myData);
notifyDataSetChanged();
}
實現按鈕的點擊事件:
btn_add_on_position.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MYData data = new MYData(R.drawable.icon, "這是添加的數據~~~~~~X" + flag);
adapter.addData(3, data);
}
});
就可以完成在指定位置添加一條數據,這裡只是簡單的實現,如果list中沒有那麼多數據,會出現數組下標越界的錯誤
在適配器中添加如下方法:
/**
* 根據對象刪除數據
*
* @param myData
*/
public void removeData(MYData myData) {
if (datas != null) {
datas.remove(myData);
}
notifyDataSetChanged();
}
實現按鈕的點擊事件:
btn_delete_one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MYData data = datas.get(2);
adapter.removeData(data);
}
});
這裡只是簡單的刪除下標為3的數據,如果listview的長度小於3,會出現數組下標越界的錯誤
在適配器中添加如下方法:
/**
* 根據位置刪除數據
*
* @param position
*/
public void removeData(int position) {
if (datas != null && position <= datas.size()) {
datas.remove(position);
}
notifyDataSetChanged();
}
實現按鈕的點擊事件:
btn_delete_on_position.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
adapter.removeData(3);
}
});
刪除指定位置的數據,需要判定是否存在不然會出現錯誤
在適配器中添加如下方法:
/**
* 清除所有的數據
*/
public void removeAll() {
if (datas != null) {
datas.clear();
}
notifyDataSetChanged();
}
實現按鈕的點擊事件:
btn_delete_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
adapter.removeAll();
}
});
最後附上效果圖:
這裡實現ListView的數據更新問題,但是任然存在一些小問題,比如,沒有數據的時候顯示一片空白,用戶體驗不是很好。可以重寫,根據服務器返回的數據更新界面。這裡就不做實現了。下一節我們介紹一下ListView多布局的實現,類似QQ等的聊天界面。
本文的目的是要實現左右滑動的指引效果。那麼什麼是指引效果呢?現在的應用為了有更好的用戶體驗,一般會在應用開始顯示一些指引幫助頁面,使用戶能更好的理解應用的功能,甚至是一些
1.這篇博文不算什麼知識點。使用的都是的系統中已經提供給我們的方式方法。這是最近用到了,感覺很實用,特此貢獻出來。 首先需要定義,listview中需要展示的
盡管網絡上已經有很多關於這個話題的優秀文章了,但還是寫了這篇文章,主要還是為了加強自己的記憶吧,自己過一遍總比看別人的分析要深刻得多,那就走起吧。簡單示例 先看一
Android系統裡面有個東西很好用,也很常用,那就是Toast,但是長期使用也會發現,Toast有他的不足之處:形式單一,只有文字,風格不變等等,那麼要如