Android ListView 顯示多種數據類型


ListView往往可能會有不同的數據類型,單類型的數據可能運用會比較少些,這也是最近項目中的一個需求{在發送消息的時候,需要選擇聯系人,而聯系人列表由英文字母索引+聯系人組成},上一篇文章只是一個基調,這篇是更復雜的情況;
先看一下效果圖

最開始的時候,打算把兩種數據類型放入一個List<Object>中,參考上一篇隨筆的狀態保持的實現,在代碼寫完了開始測試的時候,發現問題眾多,上下滾動的時候左邊的CheckBox的選擇狀態沒有很好的保存,會出現混亂選擇的情況,於是參考網上的一些做法{尋找的參考方法並沒有描述像這樣稍稍復雜點的情況,都是TextView,沒有狀態的保持,沒有View的重用,所以寫了這篇隨筆}並延伸總結;
MutiTypeAdapter.java

public class MutiTypeAdapter extends BaseAdapter {
    private OnSelectedItemChanged listener;
    private List<ListItem> list;
    private LayoutInflater inflater;

    public MutiTypeAdapter(Context context, List<ListItem> list,
            OnSelectedItemChanged listener) {
        super();
        this.list = list;
        inflater = LayoutInflater.from(context);
        this.listener = listener;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 重點
        View view = list.get(position).getView(convertView, inflater);
        if (list.get(position).getClass() == BEntity.class) { // 如果是BEntity,也就是上面圖中左邊有CheckBox的項 final BEntity entity = (BEntity) list.get(position);
            final CheckBox cb = entity.cbox;
            cb.setChecked(entity.isChecked());
            cb.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    entity.setChecked(cb.isChecked()); // 更改List中Entity的選擇狀態 if (listener != null) {
                        listener.onClick(getSelectedItem(list)); // 接口的思想暴露給Activity選擇了多少項,當然也可以具體點通知Activity選擇了哪些項
                    }
                }
            });
        }
        return view;
    }

    public int getSelectedItem(List<ListItem> list) { // 獲取選擇了多少項 int i = 0;
        for (ListItem item : list) {
            if (item.isChecked()) {
                i++;
            }
        }
        return i;
    }

    public interface OnSelectedItemChanged {
        public void onClick(int count);
    }
}

上面是數據源適配器,最開始的時候我在getView方法中對Item進行數據類的判斷(AEntity/BEntity),再決定是選擇加載哪一個layout,結果發現在重用View的時候很混亂,所以改為上面的實現方法;
AEntity和BEntity都繼承自接口ListItem

public class AEntity implements ListItem {
    private String str;

    public AEntity(String str) {
        super();
        this.str = str;
    }

    @Override
    public View getView(View convertView, LayoutInflater inflater) {
        Holder holder = null;
        if (convertView == null
                || convertView.getTag().getClass() != Holder.class) {
            holder = new Holder();
            convertView = inflater.inflate(getLayoutId(), null);
            TextView tv = (TextView) convertView.findViewById(R.id.title_tv);
            holder.tv = tv;
            convertView.setTag(holder);
        } else {
            holder = (Holder) convertView.getTag();
        }
        holder.tv.setText(str);
        return convertView;
    }

    class Holder {
        TextView tv;
    }

    @Override
    public int getLayoutId() {
        return R.layout.title;
    }

    @Override
    public boolean isChecked() { // 此Entity相當於是標題項,沒有CheckBox,所以永遠返回false return false;
    }
}

 

public class BEntity implements ListItem {
    private boolean isChecked = false;
    private String str;

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public BEntity(String str) {
        super();
        this.str = str;
    }

    @Override
    public int getLayoutId() {
        return R.layout.child;
    }

    public CheckBox cbox;

    @Override
    public View getView(View convertView, LayoutInflater inflater) {
        Holder holder = null;
        if (convertView == null
                || convertView.getTag().getClass() != Holder.class) {
            holder = new Holder();
            convertView = inflater.inflate(getLayoutId(), null);
            TextView tv = (TextView) convertView.findViewById(R.id.item_tv);
            CheckBox cb = (CheckBox) convertView.findViewById(R.id.item_cb);
            holder.tv = tv;
            holder.cb = cb;
            convertView.setTag(holder);
        } else {
            holder = (Holder) convertView.getTag();
        }
        holder.tv.setText(str);
        final CheckBox cb = holder.cb;
        this.cbox = cb;return convertView;
    }

    class Holder {
        TextView tv;
        CheckBox cb;
    }
}

ListItem.java

public interface ListItem {
    public boolean isChecked(); // 當前項是否選中 public int getLayoutId();

    public View getView(View convertView, LayoutInflater inflater); // 返回Adapter中需要返回的View
}

在MainActivity中,模擬數據源並綁定到ListView列表;

public class MainActivity extends Activity {
    ListView lv;
    MutiTypeAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.lv);
        setAdapter();
    }

    private void setAdapter() {
        List<ListItem> list = new ArrayList<ListItem>();
        for (int i = 0; i < 50; i++) {
            if (i % 2 == 0) {
                list.add(new AEntity("item - " + i));
            } else {
                list.add(new BEntity("item - " + i));
            }
        }
        OnSelectedItemChanged listener = new OnSelectedItemChanged() {

            @Override
            public void onClick(int count) {
                Log.e("SelectedCount", count + "");
            }
        };
        adapter = new MutiTypeAdapter(getApplicationContext(), list, listener);
        lv.setAdapter(adapter);
    }
}

OK,通過幾步就實現了所想要的功能!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM