自定义控件-下拉刷新和上拉加载的listView

本博客原地址:http://www.jianshu.com/p/cf4a77727d68
本博客为,下拉刷新和上拉加载的listview的实现。
本项目github地址:
https://github.com/CameloeAnthony/DropDownListViewDemo

(1)效果展示:

首先看下大致的效果:


自定义控件-下拉刷新和上拉加载的listView_第1张图片
下拉刷新和上拉加载的listview

(2) 具体使用:

接着看一下怎么使用的:
2.1 在layout中定义




    

DropDownListView自定义了三个boolean属性



    
        
        
        
    

isDropDownStyle表示是否允许下拉样式,java代码中可自定义下拉listener,表示需要完成的任务
isOnBottomStyle表示是否允许底部样式,java代码中可自定义滚动到底部的listener,表示需要完成的任务
isAutoLoadOnBottom表示是否允许滚动到底部时自动执行对应listener,仅在isOnBottomStyle为true时有效
PS:如果isDropDownStyle或isOnBottomStyle为false,并不会加载对应的布局,所以性能同ListView一样。
2.2 在java类中调用:
示例代码如下:

package com.nsu.app.dropdownlistviewdemo;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;

/**
 * Create By Anthony on 2016/2/15
 * demo of dropDownListView
 */
public class DropDownListViewDemo extends Activity {
    private LinkedList listItems           = null;
    private DropDownListView listView            = null;
    private ArrayAdapter adapter;

    private String[]             mStrings            = {"11111", "2222", "3333",
            "4444", "5555", "6666", "77777", "8888", "99999",
            "aaaaa", "bbbb", "cccc", "dddd", "eeee",};
    public static final int      MORE_DATA_MAX_COUNT = 3;
    public int                   moreDataCount       = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drop_down_list_view_demo);

        listView = (DropDownListView)findViewById(R.id.list_view);
        // set drop down listener
        listView.setOnDropDownListener(new DropDownListView.OnDropDownListener() {

            @Override
            public void onDropDown() {
                new GetDataTask(true).execute();
            }
        });

        // set on bottom listener
        listView.setOnBottomListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                new GetDataTask(false).execute();
            }
        });

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
//                ToastUtils.show(context, R.string.drop_down_tip);
                Toast.makeText(DropDownListViewDemo.this,R.string.drop_down_tip,Toast.LENGTH_LONG).show();
            }
        });
        // listView.setShowFooterWhenNoMore(true);

        listItems = new LinkedList();
        listItems.addAll(Arrays.asList(mStrings));
        adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, listItems);
        listView.setAdapter(adapter);
    }

    private class GetDataTask extends AsyncTask {

        private boolean isDropDown;

        public GetDataTask(boolean isDropDown) {
            this.isDropDown = isDropDown;
        }

        @Override
        protected String[] doInBackground(Void... params) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                
            }
            return mStrings;
        }

        @Override
        protected void onPostExecute(String[] result) {

            if (isDropDown) {
                listItems.addFirst("Added after drop down");
                adapter.notifyDataSetChanged();

                // should call onDropDownComplete function of DropDownListView at end of drop down complete.
                SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss");
                listView.onDropDownComplete(getString(R.string.update_at) + dateFormat.format(new Date()));
            } else {
                moreDataCount++;
                listItems.add("Added after on bottom");
                adapter.notifyDataSetChanged();

                if (moreDataCount >= MORE_DATA_MAX_COUNT) {
                    listView.setHasMore(false);
                }

                // should call onBottomComplete function of DropDownListView at end of on bottom complete.
                listView.onBottomComplete();
            }

            super.onPostExecute(result);
        }
    }

}

可以看到我们通过setOnDropDownListener设置下拉的事件,不过需要在事件结束时手动调用onDropDownComplete恢复状态(注意需要在adapter.notifyDataSetChanged();后面调用)。
通过setOnBottomListener设置滚动到底部的事件,不过需要在事件结束时手动调用onBottomComplete恢复状态。

(3) 相关属性的定义:

样式的定义(header 和footer)
我们是在listview的上面加上了header和下面加上了footer,那么来看看我们的自定义属性:
3.1自定义下拉的图片,在项目drawable资源下添加文件名为drop_down_list_arrow.png的图片即可
3.2定义相关文字,strings.xml中定义下面属性:


    DropDownListViewDemo
    Settings
    Image

    Click to refresh。。。
    Drop Down to refresh。。。
    Release to refresh。。。
    Loading。。。
    More
    Loading。。。
    No More

    Pull down to refresh or scroll to bottom to load more
    updated at:

3.3定义相关字体颜色,colors.xml中定义下面属性:


#000000

#000000

#000000

3.4 定义相关样式(会覆盖前面的string和color定义),styles.xml中定义下面属性:


    
    

    

    

    

    

3.4 定义相关dimen值,dimens.xml中定义下面属性:

12dp
15dp
36dp
36dp

(4)核心代码:

也是最重要的我们的自定义控件:

package com.nsu.app.dropdownlistviewdemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * ListView which can drop down to refresh and
 * load more when scroll to bottom
 */
public class DropDownListView extends ListView implements OnScrollListener {

    private boolean            isDropDownStyle         = true;
    private boolean            isOnBottomStyle         = true;
    private boolean            isAutoLoadOnBottom      = false;

    private String             headerDefaultText;
    private String             headerPullText;
    private String             headerReleaseText;
    private String             headerLoadingText;
    private String             footerDefaultText;
    private String             footerLoadingText;
    private String             footerNoMoreText;

    private Context            context;

    /** header layout view **/
    private RelativeLayout     headerLayout;
    private ImageView          headerImage;
    private ProgressBar        headerProgressBar;
    private TextView           headerText;
    private TextView           headerSecondText;

    /** footer layout view **/
    private RelativeLayout     footerLayout;
    private ProgressBar        footerProgressBar;
    private Button             footerButton;

    private OnDropDownListener onDropDownListener;
    private OnScrollListener   onScrollListener;

    /** rate about drop down distance and header padding top when drop down **/
    private float              headerPaddingTopRate    = 1.5f;
    /** min distance which header can release to loading **/
    private int                headerReleaseMinDistance;

    /** whether bottom listener has more **/
    private boolean            hasMore                 = true;
    /** whether show footer loading progress bar when loading **/
    private boolean            isShowFooterProgressBar = true;
    /** whether show footer when no more data **/
    private boolean            isShowFooterWhenNoMore  = false;

    private int                currentScrollState;
    private int                currentHeaderStatus;

    /** whether reached top, when has reached top, don't show header layout **/
    private boolean            hasReachedTop           = false;

    /** image flip animation **/
    private RotateAnimation    flipAnimation;
    /** image reverse flip animation **/
    private RotateAnimation    reverseFlipAnimation;

    /** header layout original height **/
    private int                headerOriginalHeight;
    /** header layout original padding top **/
    private int                headerOriginalTopPadding;
    /** y of point which user touch down **/
    private float              actionDownPointY;

    /** whether is on bottom loading **/
    private boolean            isOnBottomLoading       = false;

    public DropDownListView(Context context) {
        super(context);
        init(context);
    }

    public DropDownListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        getAttrs(context, attrs);
        init(context);
    }

    public DropDownListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        getAttrs(context, attrs);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        initDropDownStyle();
        initOnBottomStyle();

        // should set, to run onScroll method and so on
        super.setOnScrollListener(this);
    }

    /**
     * init drop down style, only init once
     */
    private void initDropDownStyle() {
        if (headerLayout != null) {
            if (isDropDownStyle) {
                addHeaderView(headerLayout);
            } else {
                removeHeaderView(headerLayout);
            }
            return;
        }
        if (!isDropDownStyle) {
            return;
        }

        headerReleaseMinDistance = context.getResources().getDimensionPixelSize(
                R.dimen.drop_down_list_header_release_min_distance);
        flipAnimation = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        flipAnimation.setInterpolator(new LinearInterpolator());
        flipAnimation.setDuration(250);
        flipAnimation.setFillAfter(true);
        reverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        reverseFlipAnimation.setInterpolator(new LinearInterpolator());
        reverseFlipAnimation.setDuration(250);
        reverseFlipAnimation.setFillAfter(true);

        headerDefaultText = context.getString(R.string.drop_down_list_header_default_text);
        headerPullText = context.getString(R.string.drop_down_list_header_pull_text);
        headerReleaseText = context.getString(R.string.drop_down_list_header_release_text);
        headerLoadingText = context.getString(R.string.drop_down_list_header_loading_text);

        LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        headerLayout = (RelativeLayout)inflater.inflate(R.layout.drop_down_list_header, this, false);
        headerText = (TextView)headerLayout.findViewById(R.id.drop_down_list_header_default_text);
        headerImage = (ImageView)headerLayout.findViewById(R.id.drop_down_list_header_image);
        headerProgressBar = (ProgressBar)headerLayout.findViewById(R.id.drop_down_list_header_progress_bar);
        headerSecondText = (TextView)headerLayout.findViewById(R.id.drop_down_list_header_second_text);
        headerLayout.setClickable(true);
        headerLayout.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                onDropDown();
            }
        });
        headerText.setText(headerDefaultText);
        addHeaderView(headerLayout);

        measureHeaderLayout(headerLayout);
        headerOriginalHeight = headerLayout.getMeasuredHeight();
        headerOriginalTopPadding = headerLayout.getPaddingTop();
        currentHeaderStatus = HEADER_STATUS_CLICK_TO_LOAD;
    }

    /**
     * init on bottom style, only init once
     */
    private void initOnBottomStyle() {
        if (footerLayout != null) {
            if (isOnBottomStyle) {
                addFooterView(footerLayout);
            } else {
                removeFooterView(footerLayout);
            }
            return;
        }
        if (!isOnBottomStyle) {
            return;
        }

        footerDefaultText = context.getString(R.string.drop_down_list_footer_default_text);
        footerLoadingText = context.getString(R.string.drop_down_list_footer_loading_text);
        footerNoMoreText = context.getString(R.string.drop_down_list_footer_no_more_text);

        LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        footerLayout = (RelativeLayout)inflater.inflate(R.layout.drop_down_list_footer, this, false);
        footerButton = (Button)footerLayout.findViewById(R.id.drop_down_list_footer_button);
        footerButton.setDrawingCacheBackgroundColor(0);
        footerButton.setEnabled(true);

        footerProgressBar = (ProgressBar)footerLayout.findViewById(R.id.drop_down_list_footer_progress_bar);
        addFooterView(footerLayout);
    }

    /**
     * @return isDropDownStyle
     */
    public boolean isDropDownStyle() {
        return isDropDownStyle;
    }

    /**
     * @param isDropDownStyle
     */
    public void setDropDownStyle(boolean isDropDownStyle) {
        if (this.isDropDownStyle != isDropDownStyle) {
            this.isDropDownStyle = isDropDownStyle;
            initDropDownStyle();
        }
    }

    /**
     * @return isOnBottomStyle
     */
    public boolean isOnBottomStyle() {
        return isOnBottomStyle;
    }

    /**
     * @param isOnBottomStyle
     */
    public void setOnBottomStyle(boolean isOnBottomStyle) {
        if (this.isOnBottomStyle != isOnBottomStyle) {
            this.isOnBottomStyle = isOnBottomStyle;
            initOnBottomStyle();
        }
    }

    /**
     * @return isAutoLoadOnBottom
     */
    public boolean isAutoLoadOnBottom() {
        return isAutoLoadOnBottom;
    }

    /**
     * set whether auto load when on bottom
     * 
     * @param isAutoLoadOnBottom
     */
    public void setAutoLoadOnBottom(boolean isAutoLoadOnBottom) {
        this.isAutoLoadOnBottom = isAutoLoadOnBottom;
    }

    /**
     * get whether show footer loading progress bar when loading
     * 
     * @return the isShowFooterProgressBar
     */
    public boolean isShowFooterProgressBar() {
        return isShowFooterProgressBar;
    }

    /**
     * set whether show footer loading progress bar when loading
     * 
     * @param isShowFooterProgressBar
     */
    public void setShowFooterProgressBar(boolean isShowFooterProgressBar) {
        this.isShowFooterProgressBar = isShowFooterProgressBar;
    }

    /**
     * get isShowFooterWhenNoMore
     * 
     * @return the isShowFooterWhenNoMore
     */
    public boolean isShowFooterWhenNoMore() {
        return isShowFooterWhenNoMore;
    }

    /**
     * set isShowFooterWhenNoMore
     * 
     * @param isShowFooterWhenNoMore the isShowFooterWhenNoMore to set
     */
    public void setShowFooterWhenNoMore(boolean isShowFooterWhenNoMore) {
        this.isShowFooterWhenNoMore = isShowFooterWhenNoMore;
    }

    /**
     * get footer button
     * 
     * @return
     */
    public Button getFooterButton() {
        return footerButton;
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);
        if (isDropDownStyle) {
            setSecondPositionVisible();
        }
    }

    @Override
    public void setOnScrollListener(OnScrollListener listener) {
        onScrollListener = listener;
    }

    /**
     * @param onDropDownListener
     */
    public void setOnDropDownListener(OnDropDownListener onDropDownListener) {
        this.onDropDownListener = onDropDownListener;
    }

    /**
     * @param onBottomListener
     */
    public void setOnBottomListener(OnClickListener onBottomListener) {
        if (!isDropDownStyle) {
            throw new RuntimeException(
                    "isDropDownStyle is false, cannot call setOnBottomListener, you can call setDropDownStyle(true) at fitst.");
        }
        footerButton.setOnClickListener(onBottomListener);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isDropDownStyle) {
            return super.onTouchEvent(event);
        }

        hasReachedTop = false;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                actionDownPointY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                adjustHeaderPadding(event);
                break;
            case MotionEvent.ACTION_UP:
                if (!isVerticalScrollBarEnabled()) {
                    setVerticalScrollBarEnabled(true);
                }
                /**
                 * set status when finger leave screen if first item visible and header status is not
                 * HEADER_STATUS_LOADING
                 * 
    *
  • if current header status is HEADER_STATUS_RELEASE_TO_LOAD, call onDropDown.
  • *
  • if current header status is HEADER_STATUS_DROP_DOWN_TO_LOAD, then set header status to * HEADER_STATUS_CLICK_TO_LOAD and hide header layout.
  • *
*/ if (getFirstVisiblePosition() == 0 && currentHeaderStatus != HEADER_STATUS_LOADING) { switch (currentHeaderStatus) { case HEADER_STATUS_RELEASE_TO_LOAD: onDropDown(); break; case HEADER_STATUS_DROP_DOWN_TO_LOAD: setHeaderStatusClickToLoad(); setSecondPositionVisible(); break; case HEADER_STATUS_CLICK_TO_LOAD: default: break; } } break; } return super.onTouchEvent(event); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (isDropDownStyle) { if (currentScrollState == SCROLL_STATE_TOUCH_SCROLL && currentHeaderStatus != HEADER_STATUS_LOADING) { /** * when state of ListView is SCROLL_STATE_TOUCH_SCROLL(ListView is scrolling and finger is on screen) * and header status is not HEADER_STATUS_LOADING *
    * if header layout is visiable, *
  • if height of header is higher than a fixed value, then set header status to * HEADER_STATUS_RELEASE_TO_LOAD.
  • *
  • else set header status to HEADER_STATUS_DROP_DOWN_TO_LOAD.
  • *
*
    * if header layout is not visiable, *
  • set header status to HEADER_STATUS_CLICK_TO_LOAD.
  • *
*/ if (firstVisibleItem == 0) { headerImage.setVisibility(View.VISIBLE); int pointBottom = headerOriginalHeight + headerReleaseMinDistance; if (headerLayout.getBottom() >= pointBottom) { setHeaderStatusReleaseToLoad(); } else if (headerLayout.getBottom() < pointBottom) { setHeaderStatusDropDownToLoad(); } } else { setHeaderStatusClickToLoad(); } } else if (currentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0 && currentHeaderStatus != HEADER_STATUS_LOADING) { /** * when state of ListView is SCROLL_STATE_FLING(ListView is scrolling but finger has leave screen) and * first item(header layout) is visible and header status is not HEADER_STATUS_LOADING, then hide first * item, set second item visible and set hasReachedTop true. */ setSecondPositionVisible(); hasReachedTop = true; } else if (currentScrollState == SCROLL_STATE_FLING && hasReachedTop) { /** * when state of ListView is SCROLL_STATE_FLING(ListView is scrolling but finger has leave screen) and * hasReachedTop is true(it's because flick back), then hide first item, set second item visible. */ setSecondPositionVisible(); } } // if isOnBottomStyle and isAutoLoadOnBottom and hasMore, then call onBottom function auto if (isOnBottomStyle && isAutoLoadOnBottom && hasMore) { if (firstVisibleItem > 0 && totalItemCount > 0 && (firstVisibleItem + visibleItemCount == totalItemCount)) { onBottom(); } } if (onScrollListener != null) { onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (isDropDownStyle) { currentScrollState = scrollState; if (currentScrollState == SCROLL_STATE_IDLE) { hasReachedTop = false; } } if (onScrollListener != null) { onScrollListener.onScrollStateChanged(view, scrollState); } } /** * drop down begin, adjust view status */ private void onDropDownBegin() { if (isDropDownStyle) { setHeaderStatusLoading(); } } /** * on drop down loading, you can call it by manual, but you should manual call onBottomComplete at the same time. */ public void onDropDown() { if (currentHeaderStatus != HEADER_STATUS_LOADING && isDropDownStyle && onDropDownListener != null) { onDropDownBegin(); onDropDownListener.onDropDown(); } } /** * drop down complete, restore view status * * @param secondText display below header text, if null, not display */ public void onDropDownComplete(CharSequence secondText) { if (isDropDownStyle) { setHeaderSecondText(secondText); onDropDownComplete(); } } /** * set header second text * * @param secondText secondText display below header text, if null, not display */ public void setHeaderSecondText(CharSequence secondText) { if (isDropDownStyle) { if (secondText == null) { headerSecondText.setVisibility(View.GONE); } else { headerSecondText.setVisibility(View.VISIBLE); headerSecondText.setText(secondText); } } } /** * drop down complete, restore view status */ public void onDropDownComplete() { if (isDropDownStyle) { setHeaderStatusClickToLoad(); if (headerLayout.getBottom() > 0) { invalidateViews(); setSecondPositionVisible(); } } } /** * on bottom begin, adjust view status */ private void onBottomBegin() { if (isOnBottomStyle) { if (isShowFooterProgressBar) { footerProgressBar.setVisibility(View.VISIBLE); } footerButton.setText(footerLoadingText); footerButton.setEnabled(false); } } /** * on bottom loading, you can call it by manual, but you should manual call onBottomComplete at the same time. */ public void onBottom() { if (isOnBottomStyle && !isOnBottomLoading) { isOnBottomLoading = true; onBottomBegin(); footerButton.performClick(); } } /** * on bottom load complete, restore view status */ public void onBottomComplete() { if (isOnBottomStyle) { if (isShowFooterProgressBar) { footerProgressBar.setVisibility(View.GONE); } if (!hasMore) { footerButton.setText(footerNoMoreText); footerButton.setEnabled(false); if (!isShowFooterWhenNoMore) { removeFooterView(footerLayout); } } else { footerButton.setText(footerDefaultText); footerButton.setEnabled(true); } isOnBottomLoading = false; } } /** * OnDropDownListener, called when header released * * @author Trinea 2012-5-31 */ public interface OnDropDownListener { /** * called when header released */ public void onDropDown(); } /** * set second position visible(index is 1), because first position is header layout */ public void setSecondPositionVisible() { if (getAdapter() != null && getAdapter().getCount() > 0 && getFirstVisiblePosition() == 0) { setSelection(1); } } /** * set whether has more. if hasMore is false, onBottm will not be called when listView scroll to bottom * * @param hasMore */ public void setHasMore(boolean hasMore) { this.hasMore = hasMore; } /** * get whether has more * * @return */ public boolean isHasMore() { return hasMore; } /** * get header layout view * * @return */ public RelativeLayout getHeaderLayout() { return headerLayout; } /** * get footer layout view * * @return */ public RelativeLayout getFooterLayout() { return footerLayout; } /** * get rate about drop down distance and header padding top when drop down * * @return headerPaddingTopRate */ public float getHeaderPaddingTopRate() { return headerPaddingTopRate; } /** * set rate about drop down distance and header padding top when drop down * * @param headerPaddingTopRate */ public void setHeaderPaddingTopRate(float headerPaddingTopRate) { this.headerPaddingTopRate = headerPaddingTopRate; } /** * get min distance which header can release to loading * * @return headerReleaseMinDistance */ public int getHeaderReleaseMinDistance() { return headerReleaseMinDistance; } /** * set min distance which header can release to loading * * @param headerReleaseMinDistance */ public void setHeaderReleaseMinDistance(int headerReleaseMinDistance) { this.headerReleaseMinDistance = headerReleaseMinDistance; } /*** * get header default text, default is R.string.drop_down_list_header_default_text * * @return */ public String getHeaderDefaultText() { return headerDefaultText; } /** * set header default text, default is R.string.drop_down_list_header_default_text * * @param headerDefaultText */ public void setHeaderDefaultText(String headerDefaultText) { this.headerDefaultText = headerDefaultText; if (headerText != null && currentHeaderStatus == HEADER_STATUS_CLICK_TO_LOAD) { headerText.setText(headerDefaultText); } } /** * get header pull text, default is R.string.drop_down_list_header_pull_text * * @return */ public String getHeaderPullText() { return headerPullText; } /** * set header pull text, default is R.string.drop_down_list_header_pull_text * * @param headerPullText */ public void setHeaderPullText(String headerPullText) { this.headerPullText = headerPullText; } /** * get header release text, default is R.string.drop_down_list_header_release_text * * @return */ public String getHeaderReleaseText() { return headerReleaseText; } /** * set header release text, default is R.string.drop_down_list_header_release_text * * @param headerReleaseText */ public void setHeaderReleaseText(String headerReleaseText) { this.headerReleaseText = headerReleaseText; } /** * get header loading text, default is R.string.drop_down_list_header_loading_text * * @return */ public String getHeaderLoadingText() { return headerLoadingText; } /** * set header loading text, default is R.string.drop_down_list_header_loading_text * * @param headerLoadingText */ public void setHeaderLoadingText(String headerLoadingText) { this.headerLoadingText = headerLoadingText; } /** * get footer default text, default is R.string.drop_down_list_footer_default_text * * @return */ public String getFooterDefaultText() { return footerDefaultText; } /** * set footer default text, default is R.string.drop_down_list_footer_default_text * * @param footerDefaultText */ public void setFooterDefaultText(String footerDefaultText) { this.footerDefaultText = footerDefaultText; if (footerButton != null && footerButton.isEnabled()) { footerButton.setText(footerDefaultText); } } /** * get footer loading text, default is R.string.drop_down_list_footer_loading_text * * @return */ public String getFooterLoadingText() { return footerLoadingText; } /** * set footer loading text, default is R.string.drop_down_list_footer_loading_text * * @param footerLoadingText */ public void setFooterLoadingText(String footerLoadingText) { this.footerLoadingText = footerLoadingText; } /** * get footer no more text, default is R.string.drop_down_list_footer_no_more_text * * @return */ public String getFooterNoMoreText() { return footerNoMoreText; } /** * set footer no more text, default is R.string.drop_down_list_footer_no_more_text * * @param footerNoMoreText */ public void setFooterNoMoreText(String footerNoMoreText) { this.footerNoMoreText = footerNoMoreText; } /** status which you can click to load, init satus **/ public static final int HEADER_STATUS_CLICK_TO_LOAD = 1; /** * status which you can drop down and then release to excute onDropDownListener, when height of header layout lower * than a value **/ public static final int HEADER_STATUS_DROP_DOWN_TO_LOAD = 2; /** status which you can release to excute onDropDownListener, when height of header layout higher than a value **/ public static final int HEADER_STATUS_RELEASE_TO_LOAD = 3; /** status which is loading **/ public static final int HEADER_STATUS_LOADING = 4; /** * set header status to {@link #HEADER_STATUS_CLICK_TO_LOAD} */ private void setHeaderStatusClickToLoad() { if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) { resetHeaderPadding(); headerImage.clearAnimation(); headerImage.setVisibility(View.GONE); headerProgressBar.setVisibility(View.GONE); headerText.setText(headerDefaultText); currentHeaderStatus = HEADER_STATUS_CLICK_TO_LOAD; } } /** * set header status to {@link #HEADER_STATUS_DROP_DOWN_TO_LOAD} */ private void setHeaderStatusDropDownToLoad() { if (currentHeaderStatus != HEADER_STATUS_DROP_DOWN_TO_LOAD) { headerImage.setVisibility(View.VISIBLE); if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) { headerImage.clearAnimation(); headerImage.startAnimation(reverseFlipAnimation); } headerProgressBar.setVisibility(View.GONE); headerText.setText(headerPullText); if (isVerticalFadingEdgeEnabled()) { setVerticalScrollBarEnabled(false); } currentHeaderStatus = HEADER_STATUS_DROP_DOWN_TO_LOAD; } } /** * set header status to {@link #HEADER_STATUS_RELEASE_TO_LOAD} */ private void setHeaderStatusReleaseToLoad() { if (currentHeaderStatus != HEADER_STATUS_RELEASE_TO_LOAD) { headerImage.setVisibility(View.VISIBLE); headerImage.clearAnimation(); headerImage.startAnimation(flipAnimation); headerProgressBar.setVisibility(View.GONE); headerText.setText(headerReleaseText); currentHeaderStatus = HEADER_STATUS_RELEASE_TO_LOAD; } } /** * set header status to {@link #HEADER_STATUS_LOADING} */ private void setHeaderStatusLoading() { if (currentHeaderStatus != HEADER_STATUS_LOADING) { resetHeaderPadding(); headerImage.setVisibility(View.GONE); headerImage.clearAnimation(); headerProgressBar.setVisibility(View.VISIBLE); headerText.setText(headerLoadingText); currentHeaderStatus = HEADER_STATUS_LOADING; setSelection(0); } } /** * adjust header padding according to motion event * * @param ev */ private void adjustHeaderPadding(MotionEvent ev) { // adjust header padding according to motion event history int pointerCount = ev.getHistorySize(); if (isVerticalFadingEdgeEnabled()) { setVerticalScrollBarEnabled(false); } for (int i = 0; i < pointerCount; i++) { if (currentHeaderStatus == HEADER_STATUS_DROP_DOWN_TO_LOAD || currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) { headerLayout .setPadding( headerLayout.getPaddingLeft(), (int)(((ev.getHistoricalY(i) - actionDownPointY) - headerOriginalHeight) / headerPaddingTopRate), headerLayout.getPaddingRight(), headerLayout.getPaddingBottom()); } } } /** * reset header padding */ private void resetHeaderPadding() { headerLayout.setPadding(headerLayout.getPaddingLeft(), headerOriginalTopPadding, headerLayout.getPaddingRight(), headerLayout.getPaddingBottom()); } /** * measure header layout * * @param child */ private void measureHeaderLayout(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } /** * get attrs * * @param context * @param attrs */ private void getAttrs(Context context, AttributeSet attrs) { TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.drop_down_list_attr); isDropDownStyle = ta.getBoolean(R.styleable.drop_down_list_attr_isDropDownStyle, false); isOnBottomStyle = ta.getBoolean(R.styleable.drop_down_list_attr_isOnBottomStyle, false); isAutoLoadOnBottom = ta.getBoolean(R.styleable.drop_down_list_attr_isAutoLoadOnBottom, false); ta.recycle(); } }

其中的一些重要方法介绍:
public void setOnDropDownListener(OnDropDownListener onDropDownListener)设置下拉的事件
public void onDropDownComplete()和public void onDropDownComplete(CharSequence secondText)恢复下拉状态,注意onDropDownComplete需要在adapter.notifyDataSetChanged();后面调用

public void setOnBottomListener(OnClickListener onBottomListener)设置滚动到底部的事件
public void onBottomComplete()恢复底部状态

public void setSecondPositionVisible()
在isDropDownStyle为true情况下,drop down的header layout为ListView position为0的item,所以可能需要调用(如adapter.notifyDataSetChanged())setSecondPositionVisible()设置position为1(即第二个)的item可见。setSelection(0)滚动到的header layout的item。onDropDownComplete()默认已经调用setSecondPositionVisible()

public void setDropDownStyle(boolean isDropDownStyle)同xml中的isDropDownStyle属性,表示是否为下拉样式,下拉释放后执行onDropDownListener
public void setOnBottomStyle(boolean isOnBottomStyle)同xml中isOnBottomStyle属性,表示滚动到底部执行onBottomListener样式
public void setAutoLoadOnBottom(boolean isAutoLoadOnBottom)同xml中isAutoLoadOnBottom属性,表示滚动到底部是否自动执行onBottomListener

public void setHeaderPaddingTopRate(float headerPaddingTopRate)设置header padding top距离与实际下拉距离的比例
public void setHeaderReleaseMinDistance(int headerReleaseMinDistance)设置header可释放执行onDropDownListener的最小距离
public void setShowFooterProgressBar(boolean isShowFooterProgressBar)设置底部是否显示progressbar

public void setHasMore(boolean hasMore)set whether has more
public void setHeaderDefaultText(String headerDefaultText)设置header默认文字, default is R.string.drop_down_list_header_default_text
public void setHeaderPullText(String headerPullText)设置header下拉提示文字, default is R.string.drop_down_list_header_pull_text
public void setHeaderReleaseText(String headerReleaseText)设置header可释放提示文字, default is R.string.drop_down_list_header_release_text
public void setHeaderLoadingText(String headerLoadingText)设置header加载中提示文字, default is R.string.drop_down_list_header_loading_text
public void setFooterDefaultText(String footerDefaultText)设置footer默认文字, default is R.string.drop_down_list_footer_default_text
public void setFooterLoadingText(String footerLoadingText)设置footer加载中提示文字, default is R.string.drop_down_list_footer_loading_text
public void setFooterNoMoreText(String footerNoMoreText)设置footer没有更多提示文字, default is R.string.drop_down_list_footer_no_more_text
public void setHeaderSecondText(CharSequence secondText)设置header第二部分文字, default is null
本项目github地址:
https://github.com/CameloeAnthony/DropDownListViewDemo

你可能感兴趣的:(自定义控件-下拉刷新和上拉加载的listView)