Android ListView收縮與展開的封裝實現

creasylai19發表於2015-03-06

常有這種需求,即ListView中資料較多(不涉及分頁),如果都展開,資料量較多,體驗不好,所以需要提供使用者檢視更多、收縮資料的互動

截圖如下:

如圖所示,點選更多,則展開所有資料。點選收起,則自動收縮。

程式碼如下(主要通過繼承Adapetr,控制展示的資料量getCount()方法實現,當資料量大於預設值(2)時,自動只展示2條資料,當點選更多時,則展示全部資料):

(在使用這種方法前曾想自定義ListView實現,但遇到較多問題,如:

1.由於我們通過adapter設定資料,不直接呼叫listview的方法,所以需要了解Adapter中notifyDataSetChanged方法的回撥函式,通過分析原始碼瞭解到notifyDataSetChanged方法會回撥ListView的requestLayout方法。所以通過在requestLayout中新增動態設定listview高度的方法,但設定高度會回撥requestLayout,從而造成死迴圈,可通過設計標誌位解決

2.動態設定listview高度並沒有實現把多餘的listitem隱藏,導致本該顯示footview的高度顯示了部分listitem。隱藏多餘的listitem後,目前還是未顯示出footview,通過網上了解到的一些解決辦法,還是沒有解決

3.此外還有其他問題,所以後來索性轉為繼承BaseAdapter實現)

package com.example.android_train.adapter;

import java.util.ArrayList;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;

import com.example.android_train.R;

public abstract class BaseFootviewAdapter<E> extends BaseAdapter {

    public static final int DEFAULT_SHOW_COUNT = 2;

    protected Context mContext;
    protected ListView mListView;
    protected LayoutInflater inflater;
    protected LinearLayout headView;
    protected Button btn_loadmore;
    protected ArrayList<E> mShowObjects = new ArrayList<E>();
    protected ArrayList<E> mAllObjects = null;
    protected boolean shrink = true;

    @SuppressWarnings("unused")
    private BaseFootviewAdapter() {
    }

    @SuppressLint("InflateParams")
    public BaseFootviewAdapter( Context mContext, ListView mListView) {
        this.mContext = mContext;
        this.mListView = mListView;
        inflater = LayoutInflater.from(mContext);
        headView = (LinearLayout) inflater.inflate(R.layout.lv_footer_button, null);
        btn_loadmore = (Button) headView.findViewById(R.id.btn_loadmore);
        btn_loadmore.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                changeShow();
            }
        });
        mListView.addFooterView(headView, null, false);
    }

    public void setAdapterData( ArrayList<E> mAllObjects ) {
        this.mAllObjects = mAllObjects;
        mShowObjects.clear();
        if( mAllObjects != null ) {
            if( mAllObjects.size() <= DEFAULT_SHOW_COUNT ) {
                headView.setVisibility(View.GONE);
                mShowObjects.addAll(mAllObjects);
            } else {
                headView.setVisibility(View.VISIBLE);
                for (int i = 0; i < DEFAULT_SHOW_COUNT; i++) {
                    mShowObjects.add(mAllObjects.get(i));
                }
            }
        }
        notifyDataSetChanged();
        setListViewHeightBasedOnChildren(mListView);
    }

    @Override
    public int getCount() {
        int showCount = 0;
        if( mShowObjects != null ) {
            showCount = mShowObjects.size();
        }
        return showCount;
    }

    @Override
    public E getItem(int position) {
        E object = null;
        if( mShowObjects != null ) {
            object = mShowObjects.get(position);
        }
        return object;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    private void changeShow() {
        if( headView.getVisibility() == View.GONE ) {
            headView.setVisibility(View.VISIBLE);
        }
        mShowObjects.clear();
        if( shrink ) {
            shrink = false;
            mShowObjects.addAll(mAllObjects);
            btn_loadmore.setText("收起");
        } else {
            shrink = true;
            for (int i = 0; i < DEFAULT_SHOW_COUNT; i++) {
                mShowObjects.add(mAllObjects.get(i));
            }
            btn_loadmore.setText("更多");
        }
        notifyDataSetChanged();
        setListViewHeightBasedOnChildren(mListView);
    }

    /**
     * 當ListView外層有ScrollView時,需要動態設定ListView高度
     * @param listView
     */
    protected void setListViewHeightBasedOnChildren(ListView listView) { 
        if(listView == null) return;
        ListAdapter listAdapter = listView.getAdapter(); 
        if (listAdapter == null) { 
            return; 
        } 
        int totalHeight = 0; 
        for (int i = 0; i < listAdapter.getCount(); i++) { 
            View listItem = listAdapter.getView(i, null, listView); 
            listItem.measure(0, 0); 
            totalHeight += listItem.getMeasuredHeight(); 
        }
        ViewGroup.LayoutParams params = listView.getLayoutParams(); 
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); 
        listView.setLayoutParams(params); 
    }

}

資原始檔lv_footer_button.xml(此處需注意,不要在fl_loadmore這個外部LinearLayout設定高度,而應在Button中設定,否則listview展示的高度與設定的不符(暫不瞭解原因))

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fl_loadmore"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#ffffff"
    android:orientation="vertical" >

    <LinearLayout android:layout_width="match_parent"
        android:layout_height="1px"
        android:orientation="vertical"
        android:background="#000000"/>

    <Button
        android:id="@+id/btn_loadmore"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:gravity="center"
        android:background="#00000000"
        android:text="更多"
        android:textSize="14sp" />

</LinearLayout>

外部呼叫例子

物件Bean(StationImage):

package com.example.android_train.bean;

public class StationImage {

    public String img_name = "";
    public String img_url_s = "";
    public String img_url_l = "";

    public String getImg_name() {
        return img_name;
    }

    public void setImg_name(String img_name) {
        this.img_name = img_name;
    }

    public String getImg_url_s() {
        return img_url_s;
    }

    public void setImg_url_s(String img_url_s) {
        this.img_url_s = img_url_s;
    }

    public String getImg_url_l() {
        return img_url_l;
    }

    public void setImg_url_l(String img_url_l) {
        this.img_url_l = img_url_l;
    }

}

FootviewAdapter繼承BaseFootviewAdapter,並實現getView()方法

package com.example.android_train.adapter;

import android.content.Context;
import android.graphics.Paint;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.example.android_train.R;
import com.example.android_train.bean.StationImage;

public class FootviewAdapter extends BaseFootviewAdapter<StationImage>{

    private ListItemView listItemView = null;

    public final class ListItemView {
        public LinearLayout gas_station_groupon_ll;
        public TextView gpn_name;
        public TextView gpn_price;
        public TextView gpn_old_price;
    }

    public FootviewAdapter( Context mContext, ListView mListView) {
        super(mContext, mListView);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final StationImage object = (StationImage)getItem(position);
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.lv_gas_station_detail_groupon, parent, false);
            listItemView = new ListItemView();
            creatView(convertView, listItemView);
            convertView.setTag(listItemView);
        } else {
            listItemView = (ListItemView) convertView.getTag();
        }
        listItemView.gpn_name.setText(object.getImg_name());
        listItemView.gpn_price.setText("¥" + object.getImg_url_l());
        listItemView.gpn_old_price.setText("¥" + object.getImg_url_s());
        listItemView.gpn_old_price.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG); //中劃線
        return convertView;
    }

    private void creatView(View rowView, ListItemView listItemView) {
        listItemView.gas_station_groupon_ll = (LinearLayout) rowView.findViewById(R.id.gas_station_groupon_ll);
        listItemView.gpn_name = (TextView) rowView.findViewById(R.id.gpn_name);
        listItemView.gpn_price = (TextView) rowView.findViewById(R.id.gpn_price);
        listItemView.gpn_old_price = (TextView) rowView.findViewById(R.id.gpn_old_price);
    }

}

資原始檔(lv_gas_station_detail_groupon)如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gas_station_groupon_ll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp" >

        <TextView
            android:id="@+id/gpn_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:text="ahah"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/gpn_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_marginRight="16dp"
            android:singleLine="true"
            android:text="abc" />

        <TextView
            android:id="@+id/gpn_old_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_toRightOf="@id/gpn_price"
            android:singleLine="true"
            android:text="qew"
            android:textSize="16sp" />
    </RelativeLayout>

</LinearLayout>

Activity中的呼叫(例項化FootviewAdapter子類,然後設定相應資料即可):

ArrayList<StationImage> mStationImages = new ArrayList<StationImage>();
ListView listview = null;
for (int i = 0; i < 5; i++) {
    StationImage mStationImage = new StationImage();
    mStationImage.setImg_name("第" + i + "個專案的名字");
    mStationImage.setImg_url_l("第" + i + "個專案的長URL");
    mStationImage.setImg_url_s("第" + i + "個專案的短URL");
    mStationImages.add(mStationImage);
}
listview = (ListView) findViewById(R.id.listview);
FootviewAdapter mFootviewAdapter = new FootviewAdapter(this, listview);
listview.setAdapter(mFootviewAdapter);
mFootviewAdapter.setAdapterData(mStationImages);

相關文章