• 【原创】StickHeaderListView的简单实现,解决footerView问题


    1、前言:

    前几天用了GitHub上se.emilsjolander.stickylistheaders这个组件,然后发现这个组件的listview不能添加footerView,加了footer后,滑倒footer的时候head会消失,与我项目中的需求不符。

    于是就自己写了一个StickHeaderListView,实现比较简单,做法和stickylistheaders基本相同只是没有封装那么多功能,可以添加footer但不能添加header,有需要的同学可以拿去改良后用。

    另外由于是临时写的一个组件,代码没做什么优化,如果有想法可以提点意见,谢谢!

    2、示例效果:

    3、组件源码:

      1 /**
      2  * 带头部固定的列表
      3  * Created by shengdong.huang on 2016/6/24.
      4  */
      5 public class StickHeaderListView extends FrameLayout {
      6 
      7     /** 页面引用 */
      8     protected Context context;
      9     /** 列表视图 */
     10     protected ListView listView;
     11     /** 适配器 */
     12     protected StickHeaderAdapter adapter;
     13     /** 头部布局 */
     14     protected FrameLayout headLayout;
     15     /** 滚动监听器 */
     16     protected OnScrollListener listener;
     17     /** 提供Adapter响应的观察者 */
     18     protected DataSetObserver mDataSetObserver = new DataSetObserver() {
     19         @Override
     20         public void onChanged() {
     21             if (listView != null && headLayout != null && adapter != null) {
     22                 refreshHead(listView.getFirstVisiblePosition(), true);
     23             }
     24         }
     25 
     26         @Override
     27         public void onInvalidated() {
     28         }
     29     };
     30 
     31     /**
     32      * 滚动监听器
     33      */
     34     protected AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() {
     35 
     36         @Override
     37         public void onScrollStateChanged(AbsListView view, int scrollState) {
     38             if (listener != null) {
     39                 listener.onScrollStateChanged(view, scrollState);
     40             }
     41         }
     42 
     43         @Override
     44         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
     45             if (listener != null) {
     46                 listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
     47             }
     48             // 刷新
     49             refreshHead(firstVisibleItem, false);
     50         }
     51     };
     52 
     53     protected void refreshHead(int firstVisibleItem, boolean forceRefresh) {
     54         // 防空
     55         if (headLayout == null || adapter == null || adapter.getHeadPos() == null
     56                 || listView.getChildAt(0) == null) {
     57             return;
     58         }
     59 
     60         // 获取头部位置记录
     61         ArrayList<Integer> headPos = adapter.getHeadPos();
     62         // 是否有找到head
     63         boolean find = false;
     64 
     65         // 获取head中的位置
     66         int prevHeadPos = -1;
     67         if (headLayout.getChildCount() > 0 && headLayout.getChildAt(0) != null &&
     68                 headLayout.getChildAt(0).getTag() != null) {
     69             prevHeadPos = (int) headLayout.getChildAt(0).getTag();
     70         }
     71 
     72         // 反向遍历头部位置记录
     73         for (int i = (headPos.size() - 1); i>=0; i--) {
     74             // 如果当前位置大于等于某个头部记录,表示应该使用该头部
     75             if (firstVisibleItem >= headPos.get(i)) {
     76                 // 获取headLayout中视图的pos标签
     77 
     78                 // 构造或者从headLayout中获取视图
     79                 View v;
     80                 if (prevHeadPos == -1 || prevHeadPos != headPos.get(i) || forceRefresh) {
     81                     // 无Pos标签或POS标签不配对
     82                     headLayout.removeAllViews();
     83                     v = listView.getAdapter().getView(headPos.get(i), null, null);
     84                     v.setTag(headPos.get(i));
     85                     LayoutParams params = new FrameLayout.LayoutParams(-1, -2);
     86                     v.setLayoutParams(params);
     87                     headLayout.addView(v);
     88                 } else if (i+1 < headPos.size() && firstVisibleItem == headPos.get(i+1) - 1) {
     89                     // 当前第一个item的top值
     90                     int top = listView.getChildAt(0).getTop();
     91                     // Pos标签配对但,有下一个head,且下一个head的pos为下一个item时
     92                     v = headLayout.getChildAt(0);
     93                     // 设置head的Top
     94                     LayoutParams params = (LayoutParams) v.getLayoutParams();
     95                     params.setMargins(0, top, 0, -top);
     96                     v.setLayoutParams(params);
     97                 } else {
     98                     // 修正head top没有回到0的问题
     99                     v = headLayout.getChildAt(0);
    100                     LayoutParams params = (LayoutParams) v.getLayoutParams();
    101                     if (params.topMargin != 0) {
    102                         params.setMargins(0, 0, 0, 0);
    103                         v.setLayoutParams(params);
    104                     }
    105                 }
    106                 find = true;
    107                 break;
    108             }
    109         }
    110         // 未找到head的情况,清空Head
    111         if (!find && headLayout != null) {
    112             headLayout.removeAllViews();
    113         }
    114     }
    115 
    116     public StickHeaderListView(Context context) {
    117         super(context);
    118         this.context = context;
    119     }
    120 
    121     public StickHeaderListView(Context context, AttributeSet attrs) {
    122         super(context, attrs);
    123         this.context = context;
    124     }
    125 
    126     public void setBackgroundColor(int color) {
    127         if (listView != null) {
    128             listView.setBackgroundColor(color);
    129         }
    130     }
    131 
    132     public void addFooterView(View v) {
    133         if (listView != null) {
    134             listView.addFooterView(v);
    135         }
    136     }
    137 
    138     public boolean removeFooterView(View v) {
    139         if (listView != null) {
    140             return listView.removeFooterView(v);
    141         }
    142         return false;
    143     }
    144 
    145     public int getFooterViewsCount() {
    146         if (listView != null) {
    147             return listView.getFooterViewsCount();
    148         }
    149         return 0;
    150     }
    151 
    152     public void setDividerHeight(int height) {
    153         if (listView != null) {
    154             listView.setDividerHeight(height);
    155         }
    156     }
    157 
    158     public void setCacheColorHint(int color) {
    159         if (listView != null) {
    160             listView.setCacheColorHint(color);
    161         }
    162     }
    163 
    164     public void setSelector(int resID) {
    165         if (listView != null) {
    166             listView.setSelector(resID);
    167         }
    168     }
    169 
    170     public void setAdapter(StickHeaderAdapter adapter) {
    171         if (adapter instanceof BaseAdapter && listView != null) {
    172             this.adapter = adapter;
    173             if (adapter != null && mDataSetObserver != null) {
    174                 try {
    175                     ((BaseAdapter) adapter).unregisterDataSetObserver(mDataSetObserver);
    176                 } catch (Exception e) {}
    177             }
    178             listView.setAdapter((ListAdapter) this.adapter);
    179             ((BaseAdapter) adapter).registerDataSetObserver(mDataSetObserver);
    180         }
    181     }
    182 
    183     public void setOnScrollListener(OnScrollListener listener) {
    184         this.listener = listener;
    185     }
    186 
    187     @Override
    188     protected void onFinishInflate() {
    189         super.onFinishInflate();
    190         // 添加列表,属性直接使用父视图
    191         listView = new ListView(context);
    192         listView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    193                 ViewGroup.LayoutParams.MATCH_PARENT));
    194         addView(listView);
    195         // 添加head
    196         headLayout = new FrameLayout(context);
    197         headLayout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    198                 ViewGroup.LayoutParams.WRAP_CONTENT));
    199         addView(headLayout);
    200         // 添加滚动监听
    201         listView.setOnScrollListener(scrollListener);
    202     }
    203 
    204     public interface OnScrollListener extends AbsListView.OnScrollListener {}
    205 
    206     public interface StickHeaderAdapter {
    207         public ArrayList<Integer> getHeadPos();
    208     }
    209 }

    4、示例代码:

     1 /**
     2  * 主页面
     3  * Created by shengdong.huang on 2016/6/20.
     4  */
     5 public class MainActivity extends FragmentActivity {
     6 
     7     private StickHeaderListView listView;
     8     private TestAdapter adapter;
     9 
    10     @Override
    11     protected void onCreate(@Nullable Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         setContentView(R.layout.activity_main);
    14         listView = (StickHeaderListView) findViewById(R.id.list);
    15 
    16         View footer = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
    17         footer.setBackgroundColor(Color.GREEN);
    18         listView.addFooterView(footer);
    19 
    20         View footer2 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
    21         footer2.setBackgroundColor(Color.BLUE);
    22         listView.addFooterView(footer2);
    23 
    24         View footer3 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
    25         footer3.setBackgroundColor(Color.RED);
    26         listView.addFooterView(footer3);
    27 
    28         listView = (StickHeaderListView) findViewById(R.id.list);
    29 
    30         adapter = new TestAdapter();
    31         listView.setAdapter(adapter);
    32     }
    33 
    34     public class TestAdapter extends BaseAdapter implements StickHeaderListView.StickHeaderAdapter {
    35 
    36         private ArrayList<Integer> headpos = new ArrayList<>();
    37 
    38         public TestAdapter() {
    39             headpos.add(0);
    40             headpos.add(5);
    41             headpos.add(15);
    42         }
    43         @Override
    44         public int getCount() {
    45             return 30;
    46         }
    47         @Override
    48         public Object getItem(int position) {
    49             return position;
    50         }
    51         @Override
    52         public long getItemId(int position) {
    53             return position;
    54         }
    55         @Override
    56         public View getView(final int position, View convertView, ViewGroup parent) {
    57             ViewHolder holder;
    58             if (convertView == null) {
    59                 convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_list, null);
    60                 holder = new ViewHolder();
    61                 holder.text = (TextView) convertView.findViewById(R.id.text);
    62                 convertView.setTag(holder);
    63             } else {
    64                 holder = (ViewHolder) convertView.getTag();
    65             }
    66             holder.text.setText("AAAAAAAAAAAAAAAAAAAAA"+position);
    67             holder.text.setOnClickListener(new View.OnClickListener() {
    68                 @Override
    69                 public void onClick(View v) {
    70                     Toast.makeText(MainActivity.this, "pos:"+position, Toast.LENGTH_SHORT).show();
    71                 }
    72             });
    73             convertView.setBackgroundColor(0x110011 * position % 0xffffff + 0xff000000);
    74             return convertView;
    75         }
    76 
    77         @Override
    78         public ArrayList<Integer> getHeadPos() {
    79             return headpos;
    80         }
    81     }
    82 
    83     private class ViewHolder {
    84         TextView text;
    85     }
    86 }

    5、使用方法:

    1、布局中加入StickHeaderListView

    2、Adapter实现StickHeaderAdapter接口

    3、getHeadPos()中返回headitem的位置,标示listview item中各个head的起点(这点与stickylistheaders不同)

    4、如果有需要用到一些listview的方法,请自行在StickHeaderListView中添加,但注意,不能addHeaderView,会导致滑动异常

    -END-

  • 相关阅读:
    大型网站应用之海量数据和高并发解决方案总结
    反转二叉树
    Spring bean循环依赖
    死磕Spring AOP (学习持续更新中。。)
    Context 初始化流程
    ConcurrentHashMap
    2-MySQL高级-事务-基本概念(1)
    1-MySQL高级-视图
    11-python基础—格式化的两种方式
    22-MySQL-Ubuntu-备份与恢复
  • 原文地址:https://www.cnblogs.com/halfmanhuang/p/5624342.html
Copyright © 2020-2023  润新知