本博客原地址:http://www.jianshu.com/p/cf4a77727d68
本博客为,下拉刷新和上拉加载的listview的实现。
本项目github地址:
https://github.com/CameloeAnthony/DropDownListViewDemo
(1)效果展示:
首先看下大致的效果:
(2) 具体使用:
接着看一下怎么使用的:
2.1 在layout中定义
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:listViewAttr="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.nsu.app.dropdownlistviewdemo.DropDownListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"
listViewAttr:isAutoLoadOnBottom="true"
listViewAttr:isDropDownStyle="true"
listViewAttr:isOnBottomStyle="true" />
</RelativeLayout>
DropDownListView自定义了三个boolean属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="drop_down_list_attr">
<attr name="isDropDownStyle" format="boolean" />
<attr name="isOnBottomStyle" format="boolean" />
<attr name="isAutoLoadOnBottom" format="boolean" />
</declare-styleable>
</resources>
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<String> listItems = null;
private DropDownListView listView = null;
private ArrayAdapter<String> 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<String>();
listItems.addAll(Arrays.asList(mStrings));
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listItems);
listView.setAdapter(adapter);
}
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
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中定义下面属性:
<resources>
<string name="app_name">DropDownListViewDemo</string>
<string name="action_settings">Settings</string>
<string name="image_content">Image</string>
<string name="drop_down_list_header_default_text">Click to refresh。。。</string>
<string name="drop_down_list_header_pull_text">Drop Down to refresh。。。</string>
<string name="drop_down_list_header_release_text">Release to refresh。。。</string>
<string name="drop_down_list_header_loading_text">Loading。。。</string>
<string name="drop_down_list_footer_default_text">More</string>
<string name="drop_down_list_footer_loading_text">Loading。。。</string>
<string name="drop_down_list_footer_no_more_text">No More</string>
<string name="drop_down_tip">Pull down to refresh or scroll to bottom to load more</string>
<string name="update_at">updated at:</string>
</resources>
3.3定义相关字体颜色,colors.xml中定义下面属性:
<!-- drop down list header font color -->
<color name="drop_down_list_header_font_color">#000000</color>
<!-- drop down list header second font color -->
<color name="drop_down_list_header_second_font_color">#000000</color>
<!-- drop down list footer font color -->
<color name="drop_down_list_footer_font_color">#000000</color>
3.4 定义相关样式(会覆盖前面的string和color定义),styles.xml中定义下面属性:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppTheme" parent="@android:style/Theme.Holo.Light">
</style>
<style name="drop_down_list_header_progress_bar_style">
<item name="android:minHeight">@dimen/drop_down_list_header_progress_bar_height</item>
<item name="android:maxHeight">@dimen/drop_down_list_header_progress_bar_height</item>
</style>
<style name="drop_down_list_footer_progress_bar_style">
<item name="android:minHeight">@dimen/drop_down_list_footer_progress_bar_height</item>
<item name="android:maxHeight">@dimen/drop_down_list_footer_progress_bar_height</item>
</style>
<style name="drop_down_list_header_font_style">
<item name="android:textColor">@color/drop_down_list_header_font_color</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
</style>
<style name="drop_down_list_header_second_font_style">
<item name="android:textColor">@color/drop_down_list_header_second_font_color</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
</style>
<style name="drop_down_list_footer_font_style">
<item name="android:textColor">@color/drop_down_list_footer_font_color</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
</style>
</resources>
3.4 定义相关dimen值,dimens.xml中定义下面属性:
<dimen name="drop_down_list_header_padding_top">12dp</dimen>
<dimen name="drop_down_list_header_padding_bottom">15dp</dimen>
<dimen name="drop_down_list_header_progress_bar_height">36dp</dimen>
<dimen name="drop_down_list_footer_progress_bar_height">36dp</dimen>
(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
* <ul>
* <li>if current header status is HEADER_STATUS_RELEASE_TO_LOAD, call onDropDown.</li>
* <li>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.</li>
* </ul>
*/
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
* <ul>
* if header layout is visiable,
* <li>if height of header is higher than a fixed value, then set header status to
* HEADER_STATUS_RELEASE_TO_LOAD.</li>
* <li>else set header status to HEADER_STATUS_DROP_DOWN_TO_LOAD.</li>
* </ul>
* <ul>
* if header layout is not visiable,
* <li>set header status to HEADER_STATUS_CLICK_TO_LOAD.</li>
* </ul>
*/
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 <a href="http://www.trinea.cn" target="_blank">Trinea</a> 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
网友评论