自定义组件—ListView_加头去头,加脚去脚、添加或删除ListView中的数据、


该组件的功能有三个:

(1)       任意加头去头,加脚去脚。

我们知道ListView在setAdapter之后再调用addHeader方法会抛出异常,而加脚有时管用,有时不管用。Android开发文档中也明确指出ListView在setAdapter之后不应该再进行setHeader和setFooter方法。这明显不能满足我们的实际需求。

我的解决方案是:在setAdapter之前给ListView先加上一个空头布局和空脚布局,布局高度设为wrap_content,这样当头或脚布局中没有任何组件时,头和脚是看不到的。当需要显示头和脚时,直接向头和脚布局中添加要显示的组件即可。

(2)       可任意添加或删除ListView中的数据。

有过实际开发经验的同学可能会有所体会,我们每次改变数据后都要调一次adapter.notifyDataSetChanged(),而且当ListView添加或删除数据的操作未完成时,再来一次添加或删除数据操作的话会报异常。比如用ListView显示网络获取的数据时,不断的刷新数据就会出现这种情况。这时候“同步“获取数据的线程是无法避免这个问题的,因为当线程获取网络数据特别快时还是会出现Listview添加数据操作的同时发生。

我的解决办法是:在该组件中自己实现添加和删除数据的方法并用一个变量来标记当前ListView是否在进行添加或删除数据操作,是的话则不允许在进行数据操作。

(3)       实现了向下滑动出现一个显示正在加载的头布局,手离开屏幕时头布局自动隐藏的功能。如YiBo微博首页的下拉刷新效果。效果图如下:

      

 

拉动前图片

按下拉动后图片

代码:

package diy.ts.wader.widget;

 

import java.util.Collection;

import java.util.List;

 

import android.content.Context;

import android.graphics.Color;

import android.util.AttributeSet;

import android.view.Gravity;

import android.view.MotionEvent;

import android.view.View;

import android.widget.BaseAdapter;

import android.widget.LinearLayout;

import android.widget.ListView;

import android.widget.ProgressBar;

import android.widget.TextView;

 

/******************************************************************

 * 文件名称 : MyListView.java

 * 作者 : wader

 * 创建时间 : 2011-7-20上午10:40:05

 * 文件描述 :

 * 实现了下拉显示组件头、任意添加头脚、添加删除数据功能的ListView组件

 ******************************************************************/

public class MyListView extends ListView {

    private BaseAdapter adapter;

    private List<Object> dataList;

    private Context context;

    private LinearLayout header;

    private LinearLayout footer;

    /*

     * 在touch时间后的move动作时显示的头部动画

     */

    private LinearLayout headerContent;

    ProgressBar headerProgressBar;

    TextView headertextView;

 

    private OnHeadShowOrHideAcion onHeadShowOrHideAcion = null;

    /*

     * 在init时是否加头布局,在init方法前设置

     */

    private boolean addHeader = false;

    /*

     * 在init时是否加脚布局,在init方法前设置

     */

    private boolean addFooter = false;

    /*

     * 在touch时间后的move动作时是否显示头部动画

     */

    private boolean showAnimationHead = false;

    /*

     * 在显示头部动画时是否执行HeadShowOrHideAcion的doActionOnShowHead方法

     */

    private boolean doActionOnShowHead = true;

    /*

     * 在显示头部动画时是否执行HeadShowOrHideAcion的doActionOnHideHead方法

     */

    private boolean doActionOnHideHead = true;

    /*

     * 头布局中是否已有内容(headerContent!=null)并已显示出来

     */

    private boolean haveHead = false;

    /*

     * 是否可以对list进行添加或删除数据操作,保证list数据同步

     */

    private boolean ableOperateData = true;

    private float initY = 0;

 

    public MyListView(Context context) {

       super(context);

    }

 

    public MyListView(Context context, AttributeSet attrs) {

       this(context, attrs, 0);

    }

 

    public MyListView(Context context, AttributeSet attrs, int defStyle) {

       super(context, attrs, defStyle);

    }

 

    /*

     * 设置在init时是否加头布局,在init方法前设置

     */

    public void setAddHeader(boolean addHeader) {

       this.addHeader = addHeader;

    }

 

    /*

     * 设置在init时是否加脚布局,在init方法前设置

     */

    public void setAddFooter(boolean addFooter) {

       this.addFooter = addFooter;

    }

 

    /*

     * 设置在touch时间后的move动作时是否显示头部动画

     */

    public void setShowAnimationHead(boolean param) {

       this.showAnimationHead = param;

    }

 

    /*

     * 设置在显示头部动画时是否执行HeadShowOrHideAcion的doActionOnShowHead方法

     */

    public void setDoActionOnShowHead(boolean param) {

       this.doActionOnShowHead = param;

    }

 

    /*

     * 设置在隐藏头部动画时是否执行HeadShowOrHideAcion的doActionOnHideHead方法

     */

    public void setDoActionOnHideHead(boolean param) {

       this.doActionOnHideHead = param;

    }

 

    /*

     * 对listview进行初始化,在使用该组件前必须先执行这个方法

     */

    public void init(Context context) {

       this.context = context;

       this.addHeaderAndFooterLayout();

    }

 

    /*

     * 加头布局和脚布局

     */

    private void addHeaderAndFooterLayout() {

       if (addHeader) {

           header = new LinearLayout(context);

           addHeaderView(header);

           header.setVisibility(View.GONE);

       }

       if (addFooter) {

           footer = new LinearLayout(context);

           addFooterView(footer);

           footer.setVisibility(View.GONE);

       }

    }

 

    /*

     * 显示指定的头内容

     */

    public void showHeader(View view) {

       if (view.getLayoutParams() == null) {

           view.setLayoutParams(new LinearLayout.LayoutParams(

                  LinearLayout.LayoutParams.WRAP_CONTENT,

                  LinearLayout.LayoutParams.WRAP_CONTENT));

       }

       header.setVisibility(View.VISIBLE);

       if (header.getChildCount() <= 0)

           header.addView(view);

       this.postInvalidate();

    }

 

    /*

     * 显示指定的头动画

     */

    public void showAnimationHeader() {

       if (!haveHead && addHeader) {

           if (headerContent == null) {

              headerContent = new LinearLayout(context);

              headerContent.setLayoutParams(new LinearLayout.LayoutParams(

                     LinearLayout.LayoutParams.FILL_PARENT,

                     LinearLayout.LayoutParams.FILL_PARENT));

              headerContent.setBackgroundColor(Color.LTGRAY);

              headerContent.setGravity(Gravity.CENTER);

              headerContent.setOrientation(LinearLayout.HORIZONTAL);

 

           } else

              headerContent.removeAllViews();

           if (headertextView == null) {

              headertextView = new TextView(context);

              headertextView.setTextColor(Color.BLACK);

              headertextView.setTextSize(20);

              headertextView.setText("松开即可更新");

           }

           headerProgressBar = new ProgressBar(context);

           headerProgressBar.setPadding(0, 3, 20, 3);

           headerContent.addView(headerProgressBar);

           headerContent.addView(headertextView);

 

           this.showHeader(headerContent);

           haveHead = true;

           if (onHeadShowOrHideAcion != null && doActionOnShowHead) {

              doActionOnShowHead = false;

              onHeadShowOrHideAcion.doActionOnShowHead(this);

           }

       }

    }

 

    /*

     * 删除头内容

     */

    public void hideHeader() {

       if (addHeader) {

           header.removeAllViews();

           haveHead = false;

           header.setVisibility(View.GONE);

           this.postInvalidate();

           if (onHeadShowOrHideAcion != null && doActionOnHideHead)

              onHeadShowOrHideAcion.doActionOnHideHead(this);

       }

    }

 

    /*

     * 显示指定的脚内容

     */

    public void showFooter(View view) {

       if (addFooter) {

           if (view.getLayoutParams() == null) {

              view.setLayoutParams(new LinearLayout.LayoutParams(

                     LinearLayout.LayoutParams.WRAP_CONTENT,

                     LinearLayout.LayoutParams.WRAP_CONTENT));

           }

           footer.setVisibility(View.VISIBLE);

           if (footer.getChildCount() <= 0)

              footer.addView(view);

           this.postInvalidate();

           this.postInvalidate();

       }

    }

 

    /*

     * 删除脚内容

     */

    public void hideFooter(View view) {

       if (addFooter) {

           footer.removeAllViews();

           footer.setVisibility(View.GONE);

           this.postInvalidate();

       }

    }

 

    /*

     * 为listview设置adapter和与该adapter相关的list,应该在init方法之后执行

     */

    @SuppressWarnings("unchecked")

    public void setData(BaseAdapter adapter, List<? extends Object> dataList) {

       this.adapter = adapter;

       this.dataList = (List<Object>) dataList;

       setAdapter(adapter);

    }

 

    /*

     * 在list最后添加新数据

     */

    public void addData(Collection<? extends Object> c) {

       if (ableOperateData) {

           ableOperateData = false;

           if (dataList == null || adapter == null)

              throw new NullPointerException(

                     "You do not call the setData() method");

           dataList.addAll(c);

           adapter.notifyDataSetChanged();

       }

       ableOperateData = true;

    }

 

    /*

     * 在list的指定位置添加新数据

     */

    public void addData(int index, Collection<? extends Object> c) {

       if (ableOperateData) {

           ableOperateData = false;

           if (dataList == null || adapter == null)

              throw new NullPointerException(

                     "You don not call the setData() method or the Adapter or List param in setData() is null");

           dataList.addAll(index, c);

           adapter.notifyDataSetChanged();

       }

       ableOperateData = true;

    }

 

    /*

     * 删除list中指定位置的数据

     */

    public void deleteData(int index) {

       if (ableOperateData) {

           ableOperateData = false;

           dataList.remove(index);

           adapter.notifyDataSetChanged();

       }

       ableOperateData = true;

    }

 

    /*

     * 删除list中的全部数据

     */

    public void deleteAll() {

       if (ableOperateData) {

           ableOperateData = false;

           dataList.clear();

           adapter.notifyDataSetChanged();

       }

       ableOperateData = true;

    }

 

    /*

     * 触屏事件,主要是为了显示头动画

     */

    public void touchEventAction(MotionEvent ev) {

       switch (ev.getAction()) {

       case MotionEvent.ACTION_MOVE:

           if (this.getFirstVisiblePosition() == 0 && (ev.getY() - initY > 30)

                  && !haveHead) {// 当向下滑动30个单位时显示头部动画

              showAnimationHeader();

 

           }

           break;

       case MotionEvent.ACTION_UP:

           hideHeader();

           break;

       case MotionEvent.ACTION_DOWN:

           initY = ev.getY();

           break;

       }

    }

 

    @Override

    public boolean dispatchTouchEvent(MotionEvent ev) {

       if (showAnimationHead)

           this.touchEventAction(ev);

       super.dispatchTouchEvent(ev);

       return true;

    }

 

    /*

     * 设置在显示头部动画时要执行HeadShowOrHideAcion

     */

    public void setHeadShowOrHideAcion(

           OnHeadShowOrHideAcion paramOnHeadShowOrHideAcion) {

       this.onHeadShowOrHideAcion = paramOnHeadShowOrHideAcion;

    }

 

    /*

     * 自定义内部接口

     */

    public abstract interface OnHeadShowOrHideAcion {

       public void doActionOnShowHead(ListView paramListView);

 

       public void doActionOnHideHead(ListView paramListView);

    }

}

 

  注:

    (1)OnHeadShowOrHideAcion接口是为了实现当下拉刷新时要进行的操作,如启动线程获取网络数据。

         (2)ListView加头布局后item下标是从1开始而不是从0开始

         (3)该组件用起来可能比较麻烦,大家可以根据自身需要对它进行瘦身和改造。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自定义数据ListView通常是在Android开发用来显示一组动态数据的控件,它结合了列表和视图的优点,可以展示丰富的用户界面元素。以下是自定义数据ListView的基本步骤: 1. **继承AdapterView基类**:你需要创建一个继承自`ArrayAdapter`, `BaseAdapter`, 或者 `CursorAdapter`的子类,用于绑定数据ListView。 ```java public class CustomAdapter extends ArrayAdapter<String> { // 添加构造函数,传递数据源和布局资源 public CustomAdapter(Context context, int resource, List<String> objects) { super(context, resource, objects); } // 重写getView()方法,这里是关键,用于为每个列表项创建视图 @NonNull @Override public View getView(int position, View convertView, ViewGroup parent) { // 创建或获取ViewHolder(如果convertView已存在) ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_layout, parent, false); viewHolder.textView = convertView.findViewById(R.id.text_view); // 将ViewHolder保存在convertView上 convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } // 设置视图的文本 viewHolder.textView.setText(getItem(position)); return convertView; } // 内部类ViewHolder用于存储需要在视图复用的对象 static class ViewHolder { TextView textView; } } ``` 2. **创建布局文件**(item_layout.xml):定义每个列表项的视图结构。 3. **设置数据源**:在你的Activity或Fragment初始化ListView,并设置你的自定义Adapter: ```java ListView listView = findViewById(R.id.listView); CustomAdapter adapter = new CustomAdapter(this, R.layout.item_layout, dataList); listView.setAdapter(adapter); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值