這個我也問了同事以及開發群裡的朋友,居然都沒得到最優的實現方式的回答,看來這種復雜列表的需求還是比較少的,我自己也走了一些彎路,把我幾個實現的方式整理下,希望對於還不了解的朋友有所幫助。
實現方式1:(每次getView時重新inflate itemView,convertView沒有復用,性能低,運行沒問題)
private class MyAdapter extends BaseAdapter{
private List<Object> datas = Collections.EMPTY_LIST;
public void setDatas(List<Object> datas) {
if(datas == null){
datas = Collections.EMPTY_LIST;
}
this.datas = datas;
notifyDataSetChanged();
}
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Object data = getItem(position);
if(data instanceof Folder){
FolderViewHolder holder = null;
if(convertView != null && convertView.getTag() instanceof FolderViewHolder){
//View與數據類型一致
holder = (FolderViewHolder) convertView.getTag();
}else{
convertView = mInflater.inflate(R.layout.listitem1, null);
holder = new FolderViewHolder(convertView);
convertView.setTag(holder);
}
holder.setData((Folder)data);
}else{
FileViewHolder holder = null;
if(convertView != null && convertView.getTag() instanceof FileViewHolder){
//View與數據類型一致
holder = (FileViewHolder) convertView.getTag();
}else{
convertView = mInflater.inflate(R.layout.listitem2, null);
holder = new FileViewHolder(convertView);
convertView.setTag(holder);
}
holder.setData((File)data);
}
return convertView;
}
}
private class FolderViewHolder{
public TextView tvName;
public FolderViewHolder(View itemView){
tvName = (TextView) itemView.findViewById(R.id.tvName);
}
public void setData(Folder data) {
tvName.setText(data.name);
}
}
private class FileViewHolder{
public TextView tvName;
public FileViewHolder(View itemView){
tvName = (TextView) itemView.findViewById(R.id.tvName);
}
public void setData(File data) {
tvName.setText(data.name);
}
}
實現方式2:(因為方式1不斷inflate view,影響性能,於是考慮是否能盡可能重用已經inflate的view,於是添加了一個緩存,不過實際測試快速滑動或切換數據會顯示異常,應該是AbsListView#RecycleBin緩存的原因,具體原因我後面理清了再添加,看別人的代碼最痛苦了。。。)
private class MyAdapter extends BaseAdapter{
private List<View> folderViewCaches = new ArrayList<View>(5);
private List<View> fileViewCaches = new ArrayList<View>(5);
private List<Object> datas = Collections.EMPTY_LIST;
public void setDatas(List<Object> datas) {
if(datas == null){
datas = Collections.EMPTY_LIST;
}
this.datas = datas;
notifyDataSetChanged();
}
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Object data = getItem(position);
if(data instanceof Folder){
//文件夾,應該返回R.layout.listitem1對應的View
FolderViewHolder holder = null;
if(convertView != null && convertView.getTag() instanceof FolderViewHolder){
//View與數據類型一致
holder = (FolderViewHolder) convertView.getTag();
}else{
if(convertView != null){
//緩存到文件列表
fileViewCaches.add(convertView);
convertView = null;
}
//從緩存裡面取已從ListView移除的緩存(注釋掉此部分代碼顯示正常)
if(!folderViewCaches.isEmpty()){
for(View cache : folderViewCaches){
if(cache.getParent() == null){
//緩存的View已從listView裡面移除
convertView = cache;
holder = (FolderViewHolder) convertView.getTag();
folderViewCaches.remove(cache);
break;
}
}
}
//還是沒有,重新inflate
if(convertView == null){
convertView = mInflater.inflate(R.layout.listitem1, null);
holder = new FolderViewHolder(convertView);
convertView.setTag(holder);
}
}
holder.setData((Folder) data);
}else{
//文件,應該返回R.layout.listitem2對應的View
FileViewHolder holder = null;
if(convertView != null && convertView.getTag() instanceof FileViewHolder){
//View與數據類型一致
holder = (FileViewHolder) convertView.getTag();
}else{
if(convertView != null){
//緩存到文件夾列表
folderViewCaches.add(convertView);
convertView = null;
}
//從緩存裡面取已從ListView移除的緩存(注釋掉此部分代碼顯示正常)
if(!fileViewCaches.isEmpty()){
for(View cache : fileViewCaches){
if(cache.getParent() == null){
//緩存的View已從listView裡面移除
convertView = cache;
holder = (FileViewHolder) convertView.getTag();
fileViewCaches.remove(cache);
break;
}
}
}
//還是沒有,重新inflate
if(convertView == null){
convertView = mInflater.inflate(R.layout.listitem2, null);
holder = new FileViewHolder(convertView);
convertView.setTag(holder);
}
}
holder.setData((File) data);
}
return convertView;
}
}
實現方式3:(最佳實現,運行正常)
後面仔細閱讀ListView相關源碼,才發現Adapter本身就支持不同的布局了,而且AbsListView#RecycleBin也支持不同類型的布局的緩存策略,RecycleBin.mViewTypeCount標示有多少種View類型。
我們需要做的就是重寫Adapter的下面3個方法:
1.getViewTypeCount:
/**
* 有多少種不同布局的View
*/
@Override
public int getViewTypeCount() {
return 2;
}
2.getItemViewType
/**
* 相應position對應的View類型
*/
@Override
public int getItemViewType(int position) {
if(getItem(position) instanceof Folder){
return TYPE_FOLDER;
}else{
return TYPE_FILE;
}
}
3.getView,通過判斷對應position的類型,返回相應類型的view:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Object data = getItem(position);
if(data instanceof Folder){
//TYPE_FOLDER,文件夾,應該返回R.layout.listitem1對應的View
FolderViewHolder holder = null;
if(convertView != null){
holder = (FolderViewHolder) convertView.getTag();
}else{
convertView = mInflater.inflate(R.layout.listitem1, null);
holder = new FolderViewHolder(convertView);
convertView.setTag(holder);
}
holder.setData((Folder) data);
}else{
//TYPE_FILE,文件,應該返回R.layout.listitem2對應的View
FileViewHolder holder = null;
if(convertView != null){
holder = (FileViewHolder) convertView.getTag();
}else{
convertView = mInflater.inflate(R.layout.listitem2, null);
holder = new FileViewHolder(convertView);
convertView.setTag(holder);
}
holder.setData((File) data);
}
return convertView;
}