Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android UI——分組+懸停 listview

android UI——分組+懸停 listview

編輯:關於Android編程

能夠使頭部懸停的listview在項目中是經常用到的,例如qq的好友列表或者地區選擇。

先不多說,看效果圖:(懶得上gif圖了)
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

這裡借鑒了別人的核心代碼,我做了一些分析。
主要是使用PinnedSectionListView來替換listview。
這裡的PinnedSectionListView是別人的。我們主要看如何使用這個PinnedSectionListView以及如何適配數據進去。

 

先看項目結構圖:
這裡寫圖片描述

其中:PinnedSectionListView是原封不動借鑒的別人的
其他的則是適配listview和數據的類<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="1activity">1,activity

activity裡面很簡單了

public class IndexActivity extends AppCompatActivity {
    private PinnedSectionListView pinned_section_list;
    private IndexAdapter indexAdapter;
    private List data;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //實例化擁有懸停頭的控件
        pinned_section_list = (PinnedSectionListView) findViewById(R.id.pinned_section_list);
        //模擬數據
        data = new TestData().initData();
        //初始化適配器
        indexAdapter = new IndexAdapter(this, data);
        //添加適配器
        pinned_section_list.setAdapter(indexAdapter);
    }
}

2,xml布局




    

3,城市實體類

public class CityBean {
    private int cityId;//城市id
    private String cityName;//城市名
    private int superiorId;//上級城市id
    private List subordinateList;//下級城市集合
    //忽略get/set方法
    }

4,測試數據,模擬城市數據

public class TestData {
    private List data;

    public List initData() {
        data = new ArrayList<>();
        for (int j = 1; j < 11; j++) {
            List beijingList = new ArrayList<>();
            for (int i = 1; i < 11; i++) {
                CityBean cityBean = new CityBean();
                cityBean.setCityId(i + 10);
                cityBean.setCityName("北京" + i + "區");
                beijingList.add(cityBean);
            }
            CityBean cityBean = new CityBean();
            cityBean.setCityId(1);
            cityBean.setCityName("北京" + j);
            cityBean.setSubordinateList(beijingList);
            data.add(cityBean);

            List shanghaiList = new ArrayList<>();
            for (int i = 1; i < 11; i++) {
                CityBean cityBean1 = new CityBean();
                cityBean1.setCityId(i + 20);
                cityBean1.setCityName("上海" + i + "區");
                shanghaiList.add(cityBean1);
            }
            CityBean cityBean1 = new CityBean();
            cityBean1.setCityId(2);
            cityBean1.setCityName("上海" + j);
            cityBean1.setSubordinateList(shanghaiList);
            data.add(cityBean1);

            List shenzhenList = new ArrayList<>();
            for (int i = 1; i < 11; i++) {
                CityBean cityBean2 = new CityBean();
                cityBean2.setCityId(i + 20);
                cityBean2.setCityName("深圳" + i + "區");
                shenzhenList.add(cityBean2);
            }
            CityBean cityBean2 = new CityBean();
            cityBean2.setCityId(3);
            cityBean2.setCityName("深圳" + j);
            cityBean2.setSubordinateList(shenzhenList);
            data.add(cityBean2);
        }

        return data;
    }
}

5,Item類,真正在listview中顯示的對象

public class Item {
    public static final int ITEM = 0;//判斷是否是普通item
    public static final int SECTION = 1;//判斷是否是需要置頂懸停的item

    public final int type;//外部傳入的類型,ITEM或者SECTION
    public final CityBean cityBean;//外部傳入的數據,這裡我們將它寫成城市實體類,可以任意更換

    public int sectionPosition;//頭標記,一般用父數據的id標記
    public int listPosition;//集合標記,一般用自身的id標記

    public Item(int type, CityBean cityBean) {
        this.type = type;
        this.cityBean = cityBean;
    }

    //獲得其中保存的數據
    public CityBean getCityBean() {
        return cityBean;
    }
}

Item類用來保存外部傳進來的數據和設置該數據的類型是屬於懸停頭還是普通item。

6,最重要的,adapter

懸停listview的適配器必須實現PinnedSectionListView中的接口:PinnedSectionListAdapter和android原生接口:SectionIndexer

public class IndexAdapter extends BaseAdapter implements PinnedSectionListView.PinnedSectionListAdapter, SectionIndexer {
    //為了區分頭部的背景顏色,也可換成其他方式。例如:布局樣式
    private static final int[] COLORS = new int[]{
            R.color.green_light, R.color.orange_light,
            R.color.blue_light, R.color.red_light};

    private List data;//外部傳進來的原始數據
    private List items;//這個才是真正顯示的list
    private Context context;
    private Item[] sections;//頭標記數組

    public IndexAdapter(Context context, List data) {
        this.data = data;
        this.context = context;
        initSection();
    }

    //初始化顯示數據
    private void initSection() {
        items = new ArrayList<>();
        sections = new Item[data.size()];
        //數據准備
        for (int i = 0; i < data.size(); i++) {
            //添加頭信息,將頭標記和數據傳入
            Item section = new Item(Item.SECTION, data.get(i));
            section.sectionPosition = data.get(i).getCityId();//將父類城市id作為頭id傳入,因為父類id沒有上級城市了,所以傳自身id
            section.listPosition = data.get(i).getCityId();//傳入自身id,將當前城市id作為普通id傳入
            //頭標記組中城市id的標記相對應放入該城市的Item實例
            sections[section.sectionPosition] = section;
            items.add(section);
            //當前城市的下級城市
            for (int j = 0; j < data.get(i).getSubordinateList().size(); j++) {
                //下級城市為普通item,所以傳入Item.ITEM
                Item item = new Item(Item.ITEM, data.get(i).getSubordinateList().get(j));
                item.sectionPosition = data.get(i).getCityId();//將父類城市id作為頭id傳入,證明該普通id下的城市屬於哪個父類城市
                item.listPosition = data.get(i).getSubordinateList().get(j).getCityId();//傳入自身id,將當前城市id作為普通id傳入
                items.add(item);
            }
        }
    }

    /*************************
     * 以下是PinnedSectionListView的重寫方法
     *************************************************/
    //當前view是否屬於固定的item
    @Override
    public boolean isItemViewTypePinned(int viewType) {
        return viewType == Item.SECTION;
    }

    /*************************
     * 以下是adapter的重寫方法
     *************************************************/

    //傳入視圖類型的個數,表示有幾種視圖類型
    //PinnedSectionListView中如果發現adapter的getViewTypeCount<2會拋出異常
    @Override
    public int getViewTypeCount() {
        return 2;
    }

    //返回每一個視圖的類型
    @Override
    public int getItemViewType(int position) {
        return getItem(position).type;
    }

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

    @Override
    public Item getItem(int position) {
        return items.get(position);
    }

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

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        View v;
        ViewHolder vh;
        Item item = items.get(position);// 從集合中獲取當前行的數據
        if (view == null) {
            // 說明當前這一行不是重用的
            // 加載行布局文件,產生具體的一行
            v = View.inflate(context, R.layout.item, null);
            // 創建存儲一行控件的對象
            vh = new ViewHolder();
            // 將該行的控件全部存儲到vh中
            vh.tvName = (TextView) v.findViewById(R.id.text_text);
            v.setTag(vh);// 將vh存儲到行的Tag中
        } else {
            v = view;
            // 取出隱藏在行中的Tag--取出隱藏在這一行中的vh控件緩存對象
            vh = (ViewHolder) view.getTag();
        }
        // 從ViewHolder緩存的控件中改變控件的值
        // 這裡主要是避免多次強制轉化目標對象而造成的資源浪費
        vh.tvName.setText(item.getCityBean().getCityName());
        if (item.type == Item.SECTION) {
            v.setBackgroundColor(parent.getResources().getColor(COLORS[item.sectionPosition % COLORS.length]));
        }
        return v;
    }

    // 存儲一行中的控件(緩存作用)---避免多次強轉每行的控件
    class ViewHolder {
        TextView tvName;
    }

    /************************
     * 以下是SectionIndexer接口的重寫方法
     ******************************************/

    //返回一個對象數組代表列表的部分,用於來顯示頭
    //這裡返回的Item[]是裝的頭的部分
    @Override
    public Item[] getSections() {
        return sections;
    }

    //給定的索引部分數組內的部分對象,返回在適配器部分的起始位置。
    @Override
    public int getPositionForSection(int sectionIndex) {
        //以免拋出異常
        if (sectionIndex >= sections.length) {
            sectionIndex = sections.length - 1;
        }
        //返回當前頭集合的頭id
        return sections[sectionIndex].listPosition;
    }

    //給定一個適配器內的位置,返回相應的索引部分數組內的部分對象。
    @Override
    public int getSectionForPosition(int position) {
        if (position >= getCount()) {
            position = getCount() - 1;
        }
        //返回當前item中保存的頭id
        return getItem(position).sectionPosition;
    }

}

整個項目的代碼就是這些,有興趣的可以看看PinnedSectionListView中是如何實現的。我看了一部分,因為時間關系沒有繼續研究了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved