Android UI ListView講解

前言

在Android系統中,針對大量數據的展示,可以使用ListView以列表的形式的呈現。雖然現在ListView在很多地方都被RecyclerView取代了,但是在一些合適的場景中依舊有用武之地。本文將詳細講解ListView的使用和常用技巧。

基本使用

ListView的使用還是很簡單的,重點在於數據由Adapter(適配器)提供的,ListView並不直接訪問數據源。因此,可以將ListView的使用分爲3步:

  1. 獲得數據源(如數組,List等)
  2. 通過數據源建立適配器(如ArrayAdapter等)
  3. 爲ListView設置適配器

使用系統提供的佈局

針對一些簡單的場景(如只需要展示字符串),使用系統提供的ArrayAdapter即可。ArrayAdapter使用數組或List作爲數據源,常用的兩個構造方法如下:

//resource:列表項的佈局文件
//objects:數據源
public ArrayAdapter(Context context,@LayoutRes int resource,T[] objects);
public ArrayAdapter(Context context,@LayoutRes int resource,List<T> objects)

使用ListView的示例代碼如下:

//初始化普通佈局的ListView
String[] dataArray=new String[]{"coding","ending","CodingEnding","Github","coder","Android"};//1.建立數據源
ArrayAdapter<String> normalAdapter=new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1,dataArray);//2.建立適配器
normalListView.setAdapter(normalAdapter);//3.設置適配器

ArrayAdapter中使用的android.R.layout.simple_list_item_1是系統提供的佈局文件,其實就是一個TextView。

效果截圖:

監聽點擊事件

//監聽單擊事件
normalListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Log.i(TAG,"當前位置:"+position);
        String msg= (String) parent.getAdapter().getItem(position);//獲取選中對象
        Toast.makeText(ListViewActivity.this,msg,Toast.LENGTH_SHORT).show();
    }
});

//監聽長按事件
normalListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(ListViewActivity.this,"發生長按事件",Toast.LENGTH_SHORT).show();
        return true;
    }
});

可以看到,在監聽器中通過parent.getAdapter().getItem(position)獲取選中對象。注意,這個方法的返回值是Object對象,因此需要進行強制轉換。

相關屬性

android:divider:設置ListView各項之間的分割線 [color或drawable資源]
android:dividerHeight:分割線的高度
android:headerDividersEnabled:是否繪製每個HeaderView後的分割線 [默認爲true]
android:footerDividersEnabled:是否繪製每個FooterView前的分割線 [默認爲true]
android:listSelector:設置列表項被選中時的效果 [color或drawable資源]
android:fastScrollEnabled:是否在快速滑動的是否顯示右側的滑動塊
android:scrollbars:設置滑動條的展示方式 [horizontal|vertical|none]
android:stackFromBottom:是否在初始狀態時顯示ListView的最底部。 [默認爲false]

stackFromBottom這個屬性需要簡單解釋一下:如果設置爲true,那麼打開ListView首先看到的就是最底部的內容,看起來就像是ListView已經滾動到了最後一行;如果設置爲false,就和默認狀態一樣,首先看到第一行的內容。

自定義列表佈局

如果需要展示的內容比較複雜(比如圖片加文字),我們就應該自定義適配器,使用自己的佈局去展示列表項。

基本步驟

首先,建立一個實體類Book:

public class Book {
    private String name;
    private int imageRes;//圖片資源

    public Book(String name, int imageRes) {
        this.name = name;
        this.imageRes = imageRes;
    }
    @Override
    public String toString() {
        return name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getImageRes() {
        return imageRes;
    }
    public void setImageRes(int imageRes) {
        this.imageRes = imageRes;
    }
}

接着,自定義一個佈局文件(左側圖片,右側文字),本例中命名爲listview_custom_item.xml,代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="4dp"
    android:layout_marginBottom="4dp"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/book_image"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_marginLeft="8dp" />
    <TextView
        android:id="@+id/book_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textAllCaps="false"
        android:textSize="16sp" />
</LinearLayout>

然後,通過繼承BaseAdapter實現我們自己的適配器,本例中命名爲StyleListViewAdapter

public class StyleListViewAdapter extends BaseAdapter{
    private Context context;
    private List<Book> dataList;

    public StyleListViewAdapter(Context context, List<Book> dataList) {
        this.context = context;
        this.dataList = dataList;
    }

    @Override
    public int getCount() {
        return dataList.size();
    }
    @Override
    public Object getItem(int position) {
        return dataList.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Book book=dataList.get(position);
        View view= LayoutInflater.from(context).inflate(R.layout.listview_custom_item,parent,false);
        ImageView bookImageView=view.findViewById(R.id.book_image);
        TextView bookNameView=view.findViewById(R.id.book_name);
        bookImageView.setImageResource(book.getImageRes());
        bookNameView.setText(book.getName());
        return view;
    }
}

可以看到,需要重寫getCount、getItem、getItemId、getView這四個方法。此外,還要提供一個構造方法用於外界傳入Context和數據源(本例中爲List<Book>)。

使用ViewHolder提升運行效率

在實際使用中,通常會使用ViewHolder提升ListView的運行效率,這一方式將充分利用ListView中View的複用機制。

首先,在Adapter中建立一個靜態內部類ViewHolder:

static class ViewHolder{
    ImageView bookImageView;
    TextView bookNameView;
}

然後,修改Adapter中的getView方法,複用已有的View:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Book book=dataList.get(position);
    ViewHolder viewHolder;
    if(convertView==null){
        convertView= LayoutInflater.from(context).inflate(
                R.layout.listview_custom_item,parent,false);
        viewHolder=new ViewHolder();
        viewHolder.bookImageView=convertView.findViewById(R.id.book_image);
        viewHolder.bookNameView=convertView.findViewById(R.id.book_name);
        convertView.setTag(viewHolder);//存儲ViewHolder
    }else{//複用已有的View
        viewHolder= (ViewHolder) convertView.getTag();
    }
    viewHolder.bookImageView.setImageResource(book.getImageRes());
    viewHolder.bookNameView.setText(book.getName());
    return convertView;
}

最後,在代碼中爲ListView設置自定義的適配器即可,代碼如下:

//初始化自定義佈局的ListView
List<Book> dataList=new ArrayList<>();
dataList.add(new Book("《小王子》",R.mipmap.ic_launcher));
dataList.add(new Book("《資本論》",R.mipmap.ic_launcher));
dataList.add(new Book("《三體》",R.mipmap.ic_launcher));
StyleListViewAdapter styleAdapter=new StyleListViewAdapter(this,dataList);
customListView.setAdapter(styleAdapter);

效果截圖:

實現多佈局列表

在實際使用中,列表項可能不止一種佈局形式,典型的如通訊錄列表就有聯繫人、標題(如A、B、C等)這兩種形式的列表項。通過對Adapter的修改,可以通過ListView實現多佈局列表。在這裏,將介紹如何實現一個簡單的多佈局列表,最終的效果如下:

準備佈局文件

在本例中,主要有兩種列表項,即標題項和內容項。因此,準備兩個對應的佈局文件,分別命名爲listview_multi_title.xmllistview_multi_item.xml,代碼如下:

listview_multi_title.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/item_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="6dp" />
</LinearLayout>

listview_multi_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="4dp"
    android:layout_marginBottom="4dp"
    android:gravity="center_vertical">
    <ImageView
        android:id="@+id/item_image"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_marginLeft="8dp" />

    <TextView
        android:id="@+id/item_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textAllCaps="false"
        android:textSize="16sp"
        android:textColor="#000000"/>
</LinearLayout>

準備實體類

對於不同的佈局而言,應該使用不同的實體類。在本例中,有兩種列表項,因此需要兩個實體類。首先可以建立一個基類,本例中命名爲BaseMultiBean,代碼如下:

public abstract class BaseMultiBean {
    public static final int TYPE_TITLE=0;//標題項
    public static final int TYPE_ITEM=1;//內容項
    protected int type;//類型

    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
}

可以看到,基類中主要是封裝了實體的類型屬性,這一屬性將用於確定要使用的列表項佈局。然後,再建立兩個繼承自基類的實體類,分別對應標題項和內容項,本例中命名爲TitleBeanItemBean,代碼如下:

TitleBean

public class TitleBean extends BaseMultiBean{
    private String title;

    public TitleBean(String title) {
        this.title = title;
        this.type=TYPE_TITLE;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

ItemBean

public class ItemBean extends BaseMultiBean{
    private int imageRes;//圖片資源
    private String content;//內容

    public ItemBean(int imageRes, String content) {
        this.imageRes = imageRes;
        this.content = content;
        this.type=TYPE_ITEM;
    }
    public int getImageRes() {
        return imageRes;
    }
    public void setImageRes(int imageRes) {
        this.imageRes = imageRes;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}

創建適配器

有了佈局和實體類,就可以開始着手創建適配器了,本例中命名爲MultiListViewAdapter。和前面提到的適配器相比,還需要實現getViewTypeCountgetItemViewType這兩個方法。此外,getView也需要修改,以及還要提供兩種不同的ViewHolder分別對應兩種列表項。示例代碼如下:

public class MultiListViewAdapter extends BaseAdapter{
    ......
    @Override
    public int getViewTypeCount() {//返回類型種類數
        return 2;
    }
    @Override
    public int getItemViewType(int position) {//返回當前項的類型
        BaseMultiBean bean=dataList.get(position);
        return bean.getType();
    }

    static class TitleViewHolder{//針對標題項的複用
        TextView titleView;
    }
    static class ItemViewHolder{//針對內容項的複用
        ImageView itemImageView;
        TextView itemContentView;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TitleViewHolder titleViewHolder;
        ItemViewHolder itemViewHolder;
        switch(getItemViewType(position)){//根據Item的類型不同,執行相應的操作
            case BaseMultiBean.TYPE_TITLE:
                TitleBean titleBean= (TitleBean) dataList.get(position);
                if(convertView==null){
                    convertView=inflater.inflate(R.layout.listview_multi_title,parent,false);
                    titleViewHolder=new TitleViewHolder();
                    titleViewHolder.titleView=convertView.findViewById(R.id.item_title);
                    convertView.setTag(titleViewHolder);
                }else{
                    titleViewHolder= (TitleViewHolder) convertView.getTag();
                }
                titleViewHolder.titleView.setText(titleBean.getTitle());
                break;
            case BaseMultiBean.TYPE_ITEM:
                ItemBean itemBean= (ItemBean) dataList.get(position);
                if(convertView==null){
                    convertView=inflater.inflate(R.layout.listview_multi_item,parent,false);
                    itemViewHolder=new ItemViewHolder();
                    itemViewHolder.itemImageView=convertView.findViewById(R.id.item_image);
                    itemViewHolder.itemContentView=convertView.findViewById(R.id.item_content);
                    convertView.setTag(itemBean);
                }else{
                    itemViewHolder= (ItemViewHolder) convertView.getTag();
                }
                itemViewHolder.itemImageView.setImageResource(itemBean.getImageRes());
                itemViewHolder.itemContentView.setText(itemBean.getContent());
                break;
            default:break;
        }
        return convertView;
    }
}

爲ListView設置適配器

有了前面三步的準備工作,現在就可以着手爲ListView設置適配器了,示例代碼如下:

//初始化多狀態佈局的ListView(未設置點擊監聽)
List<BaseMultiBean> multiDataList=new ArrayList<>();
multiDataList.add(new TitleBean("第一個區域"));
multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《小王子》"));
multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《獅子王》"));
multiDataList.add(new TitleBean("第二個區域"));
multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《資本論》"));
multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《三體》"));
multiDataList.add(new ItemBean(R.mipmap.ic_launcher,"《孤獨的進化者》"));
MultiListViewAdapter multiAdapter=new MultiListViewAdapter(this,multiDataList);
multiListView.setAdapter(multiAdapter);

常用技巧

設置空數據佈局

public void setEmptyView(View emptyView);//在ListView的數據爲空時顯示emptyView

首先,在ListView所在的XML文件中定義一個EmptyView的佈局,示例代碼如下:

<FrameLayout
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:textSize="20sp"
    android:text="暫無數據"/>
</FrameLayout>

提示: EmptyView的widthheight屬性可以和ListView保持一致,這樣在空數據時剛好可以讓EmptyView佔據ListView的空間。

然後,在代碼中爲ListView設置EmptyView,示例代碼如下:

View view=findViewById(R.id.empty_view);
normalListView.setEmptyView(view);

效果截圖:

隱藏滾動條

只需將android:scrollbars屬性設置爲none就可以隱藏ListView的滾動條。

去掉默認的選中顏色

只需將android:listSelector屬性設置爲#00000000就可以去掉默認的選中顏色(其實是設置爲透明色)。

設置入場動畫

android:layoutAnimation:爲ListView設置佈局動畫。 [使用layoutAnimation資源]

爲ListView設置layoutAnimation屬性後,ListView的所有可見項都會執行指定的動畫,有多少可見項就會執行多少次動畫。主要的使用步驟如下:

首先,在res文件夾下的anim文件夾中建立一個set動畫資源,本例中命名爲listview_anim.xml,示例代碼如下:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="1000"/>
    <translate android:fromXDelta="1000" android:toXDelta="0" android:duration="1000"/>
</set>

這個動畫的作用是讓View從右側飛入,且有一個由淺入深的漸變效果,每個動畫持續1000ms。

然後,在anim文件夾下建立一個layoutAnimation資源,本例中命名爲listview_layout_animation.xml,示例代碼如下:

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.3"
    android:animationOrder="random"
    android:animation="@anim/listview_anim">
</layoutAnimation>

delay指定下一次動畫相對上一次動畫的延遲倍數,可以是0到1之間的值;animation指定需要使用的動畫資源。animationOrder指定子View的動畫執行順序,可選值與含義如下:

  1. normal:ListView的列表項順序執行動畫(從第一個可見列表項開始執行到最後一個可見列表項)
  2. random:ListView的列表項隨機執行動畫
  3. reverse:ListView的列表項逆序執行動畫(從最後一個可見列表項開始執行到第一個可見列表項)

最後,爲ListView指定對應的layoutAnimation資源即可:

<ListView
    android:id="@+id/list_view_normal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutAnimation="@anim/listview_layout_animation"/>

提示:如果需要手動執行佈局動畫,可以調用ListView的startLayoutAnimation方法。

效果截圖:

增加列表頭和列表尾

相關方法:

//data:爲HeaderView綁定的數據(可通過ListAdapter#getItem方法獲得)
//isSelectable:指定HeaderView是否可選中(是否觸發OnItemClickListener和OnItemLongClickListener)
public void addHeaderView(View v, Object data, boolean isSelectable);//添加指定列表頭(可調用多次,從上往下逐次添加)
public void addHeaderView(View v);//添加指定列表頭(可調用多次,從上往下逐次添加)
public int getHeaderViewsCount();//獲得列表頭的個數
public boolean removeHeaderView(View v);//移除列表頭

//data:爲FooterView綁定的數據(可通過ListAdapter#getItem方法獲得)
//isSelectable:指定FooterView是否可選中((是否觸發OnItemClickListener和OnItemLongClickListener))
public void addFooterView(View v, Object data, boolean isSelectable);//添加指定列表頭(可調用多次,從上往下逐次添加)
public void addFooterView(View v);//添加指定列表頭(可調用多次,從上往下逐次添加)
public int getFooterViewsCount();//獲得列表尾的個數
public boolean removeFooterView(View v);//移除列表尾

說明:方法中的addHeaderView(View v)其實是通過調用addHeaderView(view, null, true)的方式實現的。addFooterView(View v)方法與之同理。

示例代碼:

//爲ListView添加列表頭/尾
String[] dataArray=new String[]{"coding","ending","CodingEnding","Github","coder","Android"};
ArrayAdapter<String> headerFooterAdapter=new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1,dataArray);
LayoutInflater inflater=LayoutInflater.from(this);
View headerView=inflater.inflate(R.layout.listview_header,headerFooterListView,false);//實例化頭佈局
View footerView=inflater.inflate(R.layout.listview_footer,headerFooterListView,false);//實例化尾佈局
headerFooterListView.addHeaderView(headerView,"HeaderView",true);//設置列表頭可選中
headerFooterListView.addFooterView(footerView,"FooterView",false);//設置列表尾不可選中
headerFooterListView.setAdapter(headerFooterAdapter);

效果截圖:

提示: addFooterView和addHeaderView應該在ListView使用setAdapter設置適配器前調用,否則可能出現異常。

注意:如果爲ListView設置了HeaderView或者FooterView,在OnItemClickListener的onItemClick方法中,position可能不是我們希望取得的值(因爲算上了HeaderView和FooterView的個數)。此時,如果想要獲得選中項,應該通過AdapterView#getAdapter獲取,示例代碼如下:

customListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Book book= (Book) parent.getAdapter().getItem(position);
    }
});

parent.getAdapter()獲取的是ListView適配器的包裝類,它的getItem方法會排除HeaderView和FooterView的影響,返回正確的對象。

動態增加列表項

先向數據源(如ArrayList)中添加數據,然後調用ListAdapter#notifyDataSetChanged方法通知列表刷新。示例代碼如下:

List<Book> dataList dataList=new ArrayList<>();
.....
dataList.add(new Book("《新的書籍》",R.mipmap.ic_launcher));//爲數據源新增數據
styleAdapter.notifyDataSetChanged();//通知ListView數據已更新

具體代碼請參考demo。

設置列表的滾動模式

android:transcriptMode:設置列表的滾動模式

ListView的滾動模式由transcriptMode屬性決定,它有三種可選值,含義如下:

  1. disabled:列表有新的數據增加時,ListView並不發生滾動。[默認值]
  2. normal:如果最後一個列表項在可視範圍內,當有新的數據增加時,列表會自動滾動到底部。
  3. alwaysScroll:只要有新的數據增加時,列表就會自動滾動到底部。

在開發中根據實際需求選擇相應的滾動模式即可。

跳轉到指定位置

//跳轉到指定位置(讓這個Item成爲列表當前的第一個可見項)
public void setSelection(int position);

//讓HeaderView成爲列表當前的第一個可見項(如果HeaderView不存在則顯示position爲0的項)
public void setSelectionAfterHeaderView();

小技巧:如果這兩個方法在調用時無效,可以先調用ListView的clearFocus方法。

平滑滾動到指定位置

//平滑滾動到指定位置
public void smoothScrollToPosition(int position);

//平滑滾動n個列表項的距離
//offset:需要滾動的列表項個數(offset爲正數時ListView向上滾動,爲負數時向下滾動)
public void smoothScrollByOffset(int offset);

注意: smoothScrollToPosition並不保證將指定位置的列表項顯示爲列表當前的第一個可見項,只保證這個列表項在可視範圍內。

效果截圖:

監聽滾動狀態

只需要爲ListView設置OnScrollListener即可,示例代碼如下:

//監聽ListView的滑動狀態
normalListView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        //滑動狀態發生變化時觸發
        //scrollState的可能值:[SCROLL_STATE_IDLE|SCROLL_STATE_TOUCH_SCROLL|SCROLL_STATE_FLING]
    }
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //滑動完成時觸發
        //firstVisibleItem:第一個可見項的索引值
        //visibleItemCount:可見項的個數
        //totalItemCount:列表項的總數
    }
});

onScrollStateChanged中的scrollState可能有三種取值,含義如下:

  1. SCROLL_STATE_IDLE:靜止狀態
  2. SCROLL_STATE_TOUCH_SCROLL:滑動狀態(用戶此時觸碰着屏幕且在滑動)
  3. SCROLL_STATE_FLING:慣性滑動狀態(用戶此時未觸碰屏幕,ListView藉助上一次滑動的慣性滑動)

小技巧:可在OnScrollListener的onScroll方法中判斷ListView是否已經滑動到末尾,示例代碼如下:

@Override
public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) {
    if(totalItemCount>0&&firstVisibleItem+visibleItemCount==totalItemCount){
        //已經滾動到末尾
    }
}

遍歷列表當前所有的可見元素

for(int i=0;i<normalListView.getChildCount();i++){
    View view=normalListView.getChildAt(i);//可以強制轉換爲具體的View
}

常見問題

子控件搶奪焦點

問題描述:在自定義ListView列表項佈局的時候,如果列表項中包含Button、CheckBox等需要焦點的控件,就可能導致點擊列表項不起作用。

解決方案:

  1. 將列表項中Button、CheckBox等控件的android:focusable設置爲false。
  2. 將列表項根佈局的android:descendantFocusability屬性設置爲blocksDescendants。

descendantFocusability屬性的可選值和效果說明如下:

  1. beforeDescendants:ViewGroup會在所有子View之前獲得焦點
  2. afterDescendants:ViewGroup會在所有子View之後獲得焦點
  3. blocksDescendants:ViewGroup會組織子View獲得焦點

上面兩種解決方案任選一種即可。

異步加載時圖片顯示錯位

問題描述:如果列表項中的圖片需要異步加載,由於ListView的View複用機制,在圖片下載完畢時原來的ImageView可能已被複用,就可能導致圖片顯示錯位。

解決方案:首先調用setTag方法爲列表項中的ImageView設置標籤。在異步加載完畢後,通過ListView的findViewWithTag方法查找ImageView。如果ImageView已經被複用了,這個方法的返回值就是null。通過判斷這個方法的返回值是否爲null,決定是否爲ImageView設置圖片資源,就可以解決圖片顯示錯位的問題。示例代碼如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    .....
    imageView.setTag(imageUrl);//將圖片地址作爲ImageView的Tag
    .....
}
ImageView imageView=listView.findViewWithTag(imageUrl);
if(imageView!=null){
    imageView.setImageDrawable(drawable);
}

更多博客

《 Android UI GridView講解》:詳細講解GridView的使用方法和常用技巧。
《 Android UI 常用控件講解》:包括CheckBox、RadioButton、ToggleButton、Switch、ProgressBar、SeekBar、RatingBar、Spinner、ImageButton。

demo下載地址

https://github.com/CodingEnding/UISystemDemo [ 持續更新中 ]

參考資料

http://gundumw100.iteye.com/blog/1169065
http://blog.csdn.net/guolin_blog/article/details/45586553
http://blog.csdn.net/yangshangwei/article/details/50322919
http://blog.csdn.net/csdn_aiyang/article/details/70739945
http://blog.csdn.net/zhuwentao2150/article/details/52425334
http://blog.csdn.net/quwei3930921/article/details/51013012

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章