Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android學習筆記二十四之ListView列表視圖二

Android學習筆記二十四之ListView列表視圖二

編輯:關於Android編程

前面一篇我們介紹了常用的幾種適配器的簡單實現和ListView的簡單使用,這一篇中,我們介紹一下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;
}

ListView優化方法二

上面介紹的是對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優化方法三

上面介紹了兩種ListView的優化方法,第二種優化效率根據google官方文檔的解析,可以優化5%左右的效率。下面介紹一下第三種優化方法。

在我們實際開發中,ListView顯示的數據都是在網絡中加載,假如網絡比較好,能一次將所有的數據加載出來,這樣用戶體驗還好,如果網絡不好,那麼加載數據需要時間比較久,用戶體驗就不好。另外,我們知道,虛擬機為每一個進程分配的內存是有限的,如果一下加載太多的數據就會出現內存溢出的情況。為了解決這兩個問題,我們可以用分批加載的方法,但是分批加載還是不能完全解決問題。假如我有10萬條數據需要加載,分批加載任然可能會出現OOM問題,這時,我們需要將數據分頁加載,先分頁加載,然後在分批加載,這樣用戶體驗會好一些。

ListView出現的一些問題和解決

ListView焦點問題

在一些情況中,我們需要在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數據更新問題

首先實現一個簡單的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. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved