1. 程式人生 > >Android中ListView使用getItemViewType為item設定不同的佈局

Android中ListView使用getItemViewType為item設定不同的佈局

實際開發過程中,有一處需要顯示一個交易記錄的列表,這個列表很容易讓人聯想到用listview來實現,但是這個列表又有稍許不同的地方,那就是它裡面的item並不是一樣的佈局,其中某些部分顯示的是消費的記錄,而有些地方顯示的是充值的記錄,也就對應了不同的item佈局。而且,這兩處地方都是從服務端獲取資料的,這兩個item的資料對應的類內容也各不相同,該怎麼處理呢?下面來一步步實現這個效果,先看效果圖:


實現的原理就是listview的adapter中的一個關鍵的方法就是getItemViewType(getItemViewType),這個方法有一個引數是position,有了這個position我們就可以對list集合中的不同位置的資料進行不同的處理,進而標識不同的type,將list中的資料進行分類處理。
在這個專案中,資料來源是從服務端獲取的json資料,資料的格式如下:

{
    "status_code": "0",
    "result": [
        {
            "mr_content": {
                "point": "10",
                "member_money": "100",
                "pay_money": "300",
                "cash": "200",
                "bonus": "消費滿200元立減50元餐券1張",
                "activities": "三鍋雞1元任吃",
                "coupon": "滿100送50",
                "branch_name": "四海一家"
            },
            "mr_id": "25",
            "mr_createtime": "1333333333",
            "mr_type": "0",
            "user_id": "108",
            "merchant_id": "1",
            "branch_id": "1",
            "branch_name": "ffff"
        },
        {
            "mr_content": {
                "member_money": "300",
                "branch_name": "四海一家"
            },
            "mr_id": "30",
            "mr_createtime": "1333333333",
            "mr_type": "1",
            "user_id": "108",
            "merchant_id": "1",
            "branch_id": "1",
            "branch_name": "fff"
        }
    ],
    "status_desc": "ok"
}

可以看到其中mr_content這個欄位,是一個自定義物件,但是兩個mr_content的內容不同,這裡是分別為mr_content的內容定義兩個不同的類還是如何處理呢?
一開始,我是分別為兩個mr_content定義不同的類,後來發現這樣行不通,因為這樣做的話定義外層類的時候mr_content就無法指定資料型別了。所以,最後採用某人的方法將mr_content定義為一個類,將兩個不同的mr_content的欄位都定義進去,解析的時候不會出現問題,沒有資料會顯示null
下面是我定義的mr_content欄位的資料型別ComsumAndChargeRecordBean
public class ComsumAndChargeRecordBean {  
    private String branch_name;  
    private String pay_money;  
    private String coupon;//使用特權  
    private String activities;  
    private String member_money;  
    private String cash;  
    private String point;  
    private String bonus;  
//  private String prestore_money;//預存款  
      
    public String getBranch_name() {  
        return branch_name;  
    }  
    public void setBranch_name(String branch_name) {  
        this.branch_name = branch_name;  
    }  
    public String getPay_money() {  
        return pay_money;  
    }  
    public void setPay_money(String pay_money) {  
        this.pay_money = pay_money;  
    }  
    public String getCoupon() {  
        return coupon;  
    }  
    public void setCoupon(String coupon) {  
        this.coupon = coupon;  
    }  
    public String getActivities() {  
        return activities;  
    }  
    public void setActivities(String activities) {  
        this.activities = activities;  
    }  
    public String getMember_money() {  
        return member_money;  
    }  
    public void setMember_money(String member_money) {  
        this.member_money = member_money;  
    }  
    public String getCash() {  
        return cash;  
    }  
    public void setCash(String cash) {  
        this.cash = cash;  
    }  
    public String getPoint() {  
        return point;  
    }  
    public void setPoint(String point) {  
        this.point = point;  
    }  
    public String getBonus() {  
        return bonus;  
    }  
    public void setBonus(String bonus) {  
        this.bonus = bonus;  
    }  
}  

資料準備好了,下面是傳入listview中進行顯示:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
  
    <include  
        android:id="@+id/traderecord_layout"  
        layout="@layout/topview_activity" />  
  
    <ListView  
        android:id="@+id/lv_my_traderecord"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent" >  
    </ListView>  
  
</LinearLayout>  

兩個不同item的佈局檔案就省略了,相信大家都會,這個沒什麼難度
下面是主介面程式碼:
protected void onCreate(Bundle savedInstanceState) {  
        // TODO Auto-generated method stub  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_trade_record);  
          
        mListView = (ListView) findViewById(R.id.lv_my_traderecord);  
        E_TempTradeRecordAdapter adapter = new E_TempTradeRecordAdapter(  
                E_TradeRecordActivity.this, myModel.tradeRecordList);  
        mListView.setAdapter(adapter);  
        adapter.notifyDataSetChanged();  
    }  

下面是adapter介面卡的一部分程式碼:
欄位和建構函式:
private static final String TAG = "E_TradeRecordAdapter";  
    private static final int TYPE_COUNT = 2;//item型別的總數  
    private static final int TYPE_COMSUM = 0;//消費型別  
    private static final int TYPE_CHARGE = 1;//充值型別  
    private ArrayList<TradeRecordBean> dataList = new ArrayList<TradeRecordBean>();//資料集合  
    private Context mContext;  
    private int currentType;//當前item型別  
  
    public E_TempTradeRecordAdapter(Context mContext,  
            ArrayList<TradeRecordBean> dataList) {  
        super();  
        this.dataList = dataList;  
        this.mContext = mContext;  
    }  

幾個重要方法:
@Override  
    public int getCount() {  
        // TODO Auto-generated method stub  
        return dataList.size();  
    }  
  
    @Override  
    public Object getItem(int position) {  
        // TODO Auto-generated method stub  
        return dataList.get(position);  
    }  
  
    @Override  
    public long getItemId(int position) {  
        // TODO Auto-generated method stub  
        return position;  
    }  

獲取子item的型別 獲取型別的數量  這裡是根據欄位Mr_type來確定的,json資料裡面是根據這個欄位來確定消費記錄的型別的。總之,在為item設定不同的佈局的時候肯定有一個標記用來區分不同的item,你可以用這個作為判斷的標記,來設定不同的type。
@Override  
    public int getItemViewType(int position) {  
        // TODO Auto-generated method stub  
        if ("0".equals(dataList.get(position).getMr_type())) {  
            return TYPE_COMSUM;// 消費型別  
        } else if ("1".equals(dataList.get(position).getMr_type())) {  
            return TYPE_CHARGE;// 充值型別  
        } else {  
            return 100;  
        }  
    }  
  
    @Override  
    public int getViewTypeCount() {  
        return TYPE_COUNT;  
    }  
viewholder:快取這幾個textview控制元件
/** 
     * 消費記錄 
     * @author yl 
     * 
     */  
    class ComsumViewHolder {  
        TextView branchnameCom;  
        TextView comsumemoney;  
        TextView useprevillage;  
        TextView yuezhifu;  
        TextView cash;  
        TextView thisscore;  
        TextView extrareward;  
        TextView prestoremoney;  
    }  
      
    /** 
     * 充值記錄 
     * @author yl 
     * 
     */  
    class ChargeViewHolder {  
        TextView branchnameCha;  
        TextView prestoremoney;  
        TextView extrasmoney;  
        TextView totalmoney;  
    }  

最後是getview方法:其中有一個關鍵的方法:
currentType = getItemViewType(position);  
這個方法獲取到當前position的型別,也就是在前面的getItemViewType方法設定的型別。
其中對convertView進行了複用和holder的使用,算是對listview的優化吧。
當currentType == TYPE_COMSUM,消費型別時,載入comsumView = LayoutInflater.from(mContext).inflate(  R.layout.traderecord_item_comsume, null);消費型別的佈局檔案。反之,載入充值型別的佈局檔案。這樣就可以達到為不同的item設定不同的佈局檔案了。
public View getView(int position, View convertView, ViewGroup parent) {  
    // TODO Auto-generated method stub  
    View comsumView = null;  
    View chargeView = null;  
  
    ComsumAndChargeRecordBean record = (ComsumAndChargeRecordBean) dataList  
            .get(position).getMr_content();  
  
    currentType = getItemViewType(position);  
    if (currentType == TYPE_COMSUM) {  
        ComsumViewHolder comsumHolder = null;  
        if (convertView == null) {  
            comsumHolder = new ComsumViewHolder();  
            comsumView = LayoutInflater.from(mContext).inflate(  
                    R.layout.traderecord_item_comsume, null);  
            comsumHolder.branchnameCom = (TextView) comsumView  
                    .findViewById(R.id.tv_branch_name);  
            comsumHolder.comsumemoney = (TextView) comsumView  
                    .findViewById(R.id.tv_comsumemoney);  
            comsumHolder.useprevillage = (TextView) comsumView  
                    .findViewById(R.id.tv_useprevillage);  
            comsumHolder.yuezhifu = (TextView) comsumView  
                    .findViewById(R.id.tv_yuezhifu);  
            comsumHolder.cash = (TextView) comsumView  
                    .findViewById(R.id.tv_cash);  
            comsumHolder.thisscore = (TextView) comsumView  
                    .findViewById(R.id.tv_thisscore);  
            comsumHolder.extrareward = (TextView) comsumView  
                    .findViewById(R.id.tv_extrareward);  
            comsumView.setTag(comsumHolder);  
            convertView = comsumView;  
        } else {  
            comsumHolder = (ComsumViewHolder) convertView.getTag();  
        }  
        comsumHolder.branchnameCom.setText(DateFormatUtil.formatDate(Long  
                .valueOf(dataList.get(position).getMr_createtime()))  
                + "  "  
                + record.getBranch_name());// 消費時間和分店  
        comsumHolder.comsumemoney.setText(record.getPay_money());// 消費金額  
        comsumHolder.useprevillage.setText(record.getCoupon());// 使用特權  
        comsumHolder.yuezhifu.setText(record.getMember_money());// 餘額支付  
        comsumHolder.cash.setText(record.getCash());// 現金支付  
        comsumHolder.thisscore.setText(record.getPoint());// 本次積分  
        comsumHolder.extrareward.setText(record.getBonus());// 額外獎勵  
    } else if (currentType == TYPE_CHARGE) {  
        ChargeViewHolder chargeHoler = null;  
        if (convertView == null) {  
            chargeHoler = new ChargeViewHolder();  
            chargeView = LayoutInflater.from(mContext).inflate(  
                    R.layout.traderecord_item_chongzhi, null);  
            chargeHoler.branchnameCha = (TextView) chargeView  
                    .findViewById(R.id.tv_branchname_charge);  
            chargeHoler.prestoremoney = (TextView) chargeView  
                    .findViewById(R.id.tv_prestoremoney);  
            chargeHoler.extrasmoney = (TextView) chargeView  
                    .findViewById(R.id.tv_extrasmoney);  
            chargeHoler.totalmoney = (TextView) chargeView  
                    .findViewById(R.id.tv_totalmoney);  
            chargeView.setTag(chargeHoler);  
            convertView = chargeView;  
        } else {  
            chargeHoler = (ChargeViewHolder) convertView.getTag();  
        }  
  
        chargeHoler.branchnameCha.setText(DateFormatUtil.formatDate(Long  
                .valueOf(dataList.get(position).getMr_createtime()))  
                + " "  
                + record.getBranch_name());// 消費時間和分店  
        // chargeHoler.prestoremoney.setText(record.getPrestore_money() +  
        // "元");// 存款  
        chargeHoler.extrasmoney.setText(record.getMember_money() + "元");// 餘額  
        chargeHoler.totalmoney.setText(record.getMember_money() + "元");// 合計  
    }  
    return convertView;  
}  

上面就是整個效果的實現過程


總結
其實為listview的item設定不同的佈局檔案,達到上面的效果,步驟如下;
1、為不同的item寫不同的佈局檔案,設定統一的javabean類
2、繼承BaseAdapter類,實現getItemViewType(int position)和getViewTypeCount() 方法,根據這兩個方法,為item設定不同的標記,也就是不同的type
3、在getView方法中,利用getItemViewType(position)方法獲取當前的type型別,然後根據不同的type型別,載入不同的item佈局檔案。
4、其他的一些listview的優化同一般的listview沒有很大區別。