android UI——分组+悬停 listview

能够使头部悬停的listview在项目中是经常用到的,例如qq的好友列表或者地区选择。

先不多说,看效果图:(懒得上gif图了)

这里借鉴了别人的核心代码,我做了一些分析。

主要是使用PinnedSectionListView来替换listview。

这里的PinnedSectionListView是别人的。我们主要看如何使用这个PinnedSectionListView以及如何适配数据进去。

博文的最后我会上传demo,里面有PinnedSectionListView和完整项目。可以根据这篇博客来看看是如何适配数据进去的。

先看项目结构图:

其中:PinnedSectionListView是原封不动借鉴的别人的

其他的则是适配listview和数据的类

1,activity

activity里面很简单了

public class IndexActivity extends AppCompatActivity {
    private PinnedSectionListView pinned_section_list;
    private IndexAdapter indexAdapter;
    private List<CityBean> 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布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.android.list.PinnedSectionListView
        android:id="@+id/pinned_section_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@null"
        android:footerDividersEnabled="false"
        android:headerDividersEnabled="false" />
</RelativeLayout>

3,城市实体类

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

4,测试数据,模拟城市数据

public class TestData {
    private List<CityBean> data;

    public List<CityBean> initData() {
        data = new ArrayList<>();
        for (int j = 1; j < 11; j++) {
            List<CityBean> 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<CityBean> 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<CityBean> 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<CityBean> data;//外部传进来的原始数据
    private List<Item> items;//这个才是真正显示的list
    private Context context;
    private Item[] sections;//头标记数组

    public IndexAdapter(Context context, List<CityBean> 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中是如何实现的。我看了一部分,因为时间关系没有继续研究了。

demo下载

时间: 2024-10-10 01:51:04

android UI——分组+悬停 listview的相关文章

Android UI组件----自定义ListView实现动态刷新

[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3910541.html 联系方式:[email protected] [正文] 一.具体步骤: (1)在activiy_main.xml中加一个ListView控件:再添加一个item的模板activity_main_item.xml,加一个底部加载的视图activity_main_load.xml

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载) POSTED ON 2014年6月27日 BY 天边的星星 本文内容: 1.横向ListView的所有实现思路; 2.其中一个最通用的思路HorizontalListView,并基于横向ListView开发一个简单的相册: 3.实现的横向ListView在点击.浏览时item背景会变色,并解决了listview里setSelected造成item的选择状态混乱的问题.

Android UI学习之ListView(使用ArrayAdapter和SimpleAdapter)

既然要使用ArrayAdapter,那我们先看看系统中所以的Adapter关系: 在实际中使用频率比较高的有:SimpleAdapter, ArrayAdapter, BaseAdapter BaseAdapter: 是一个抽象类,实现它要实现比较多的方法,但是灵活的高 ArrayAdapter:支持了泛型操作,比较简单,一般只能显示同类型的数据 SimpleAdapter:有比较好的灵活的,可以定义自己的UI 关于BaseAdapter在上一节已经讲过,这里不在举例说明 我们先使用ArrayA

Android UI学习之ListView

ListView是手机系统中使用非常广泛的一种组件,它以垂直列表的形式显示所以列表项. 今天我们学习如何将系统的短信显示到listView上. 关于如何获取系统的短信请看: Android 四大组件学习之ContentProvider三 先看一下我们的布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.andr

android UI效果相关-ListView(一)

方法一 通过反射: 代码:         Class clsClass = listView.getClass().getSuperclass();         if(clsClass == null){             Log.d("tag", "null");         }else {             Log.d("tag", "not null");             if(clsCla

Android UI学习 - ListView (android.R.layout.simple_list_item_1是个什么东西)

Android UI学习 - ListView 2010-06-20 18:21:35 标签:Android UI 移动开发 ListView ListActivity 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://android.blog.51cto.com/268543/336162 ListActivity ListActivity是一个专门显示ListView的Activity类,它内置了ListView对象,只要我

Android UI设计之&lt;十&gt;自定义ListView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果

转载请注明出处:http://blog.csdn.net/llew2011/article/details/51559694 好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客是在2014年写的:Android UI设计之<七>自定义Dialog,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效

android UI进阶之实现listview中checkbox的多选与记录

今天继续和大家分享涉及到listview的内容.在很多时候,我们会用到listview和checkbox配合来提供给用户一些选择操作.比如在一个清单页面,我们需要记录用户勾选了哪些条目.这个的实现并不太难,但是有很多朋友来问我如何实现,他们有遇到各种各样的问题,这里就一并写出来和大家一起分享. ListView的操作就一定会涉及到item和Adapter,我们还是先来实现这部分内容. 首先,写个item的xml布局,里面放置一个TextView和一个CheckBox.要注意的时候,这里我设置了C

android UI进阶之实现listview的分页加载

 分享了下拉刷新,这是一个用户体验非常好的操作方式.新浪微薄就是使用这种方式的典型. 还有个问题,当用户从网络上读取微薄的时候,如果一下子全部加载用户未读的微薄这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了.通过分页分次加载数据,用户看多少就去加载多少. 通常这也分为两种方式,一种是设置一个按钮,用户点击即加载.另一种是当用户滑动到底部时自动加载.今天我就和大家分享一下这个功能的实现. 首先,