自定義View(ListView)下拉重新整理
下拉重新整理的操作流程:
1.使用者手指在ListView頁面按下並下拉
2.出現一個提示View在listView頂部
3.listView內容更新,頂部view顯示後隱藏
具體實現步驟
建立繼承Listview的RefreshListView,並新增頂部提示view
public class RefreshListView extends ListView {
View header;// 頂部提示View
public RefreshListView(Context context) {
super(context);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
// LayoutInflater作用是載入佈局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header_layout, null);
this.addHeaderView(header);
super.setOnScrollListener(this);
}
}
頂部提示View佈局檔案header_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dip"
android:paddingBottom="10dip"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/layout"
android:layout_centerInParent="true"
android:gravity="center"
>
<!-- 提示文字 -->
<TextView
android:id="@+id/tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可以重新整理"
/>
<!-- 距上次更新至今的時間 -->
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<!-- 箭頭 -->
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/layout"
android:src="@drawable/pull_to_refresh_arrow"
/>
<!-- 更新進度條 -->
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:layout_toLeftOf="@id/layout"
android:visibility="gone"
/>
</RelativeLayout>
</LinearLayout>
執行效果圖
預設載入的header是顯示出來的,應該隱藏,程式碼如下
private void initView(Context context){
//LayoutInflater作用是載入佈局
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.header_layout, null);
measureView(header);
headerHeight = header.getMeasuredHeight();
topPadding(-headerHeight);
this.addHeaderView(header);
super.setOnScrollListener(this);
}
/**
* 設定頂部佈局的上邊距
* @param topPadding
*/
private void topPadding(int topPadding){
//設定頂部提示的邊距
//除了頂部用引數值topPadding外,其他三個用header預設的值
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
//使header無效,將來呼叫onDraw()重繪View
header.invalidate();
}
/**
* 通知父佈局,佔用的寬和高
*/
private void measureView(View view){
//LayoutParams are used by views to tell their parents
//how they want to be laid out.
//LayoutParams被view用來告訴它們的父佈局它們應該被怎樣安排
ViewGroup.LayoutParams p = view.getLayoutParams();
if(p==null){
//兩個引數:width,height
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
//getChildMeasureSpec:獲取子View的widthMeasureSpec、heightMeasureSpec值
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int height;
int tempHeight = p.height;
if(tempHeight>0){
height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
}else{
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
效果圖
監聽使用者滑動螢幕操作
1.實現OnScrollListener介面
2.根據使用者按下,移動,擡起手勢來設定不同的響應事件
3.頂部提示View---header有四種狀態
1.正常狀態(NONE)
2.提示使用者下拉可以重新整理(PULL)
3.提示使用者釋放可以重新整理(RELEASE)
4.提示使用者正在重新整理狀態(REFREASH)
public class RefreshListView extends ListView implements OnScrollListener{
View header;//下拉重新整理時出現的頂部佈局View
int headerHeight;//header的高度
int firstVisibleItem;//當前介面第一個可見的item的位置
boolean isFlag;//標誌,是在當前顯示的listView是在listView最頂端時按下額
int startY;//使用者按下的Y值
int state;//當前狀態
final int NONE = 0;//正常狀態
final int PULL = 1;//提示下拉狀態
final int RELEASE = 2;//提示釋放狀態
final int REFRESH = 3;//提示正在重新整理狀態
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
this.scrollState = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.firstVisibleItem = firstVisibleItem;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if(firstVisibleItem == 0){
isFlag = true;//ListView最頂端按下,標誌值設為真
startY = (int)ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if(state == RELEASE){
state = REFRESH;
//載入資料
refreshViewByState();
iRefreshlistener.onRefresh();
}else if(state == PULL){
state = NONE;
isFlag = false;
refreshViewByState();
}
break;
}
return super.onTouchEvent(ev);
onMove()方法----根據使用者下拉距離的不同設定不同的響應方法
private void onMove(MotionEvent ev){
//如果不是最頂端按下,則直接返回
if(!isFlag){
return;
}
int currentY = (int)ev.getY();//當前的Y值
int space = currentY - startY;//使用者向下拉的距離
int topPadding = space - headerHeight;//頂部提示View距頂部的距離值
switch (state) {
//正常狀態
case NONE:
if(space>0){
state = PULL;//下拉的距離大於0,則將狀態改為PULL(提示下拉更新)
refreshViewByState();//根據狀態的不同更新View
}
break;
case PULL:
topPadding(topPadding);
if(space>headerHeight+30//下拉的距離大於header的高度加30且使用者滾動螢幕,手指仍在螢幕上
&&scrollState == SCROLL_STATE_TOUCH_SCROLL ){
state = RELEASE;//將狀態改為RELEASE(提示鬆開更新)
refreshViewByState();
}
break;
case RELEASE:
topPadding(topPadding);
if(space<headerHeight+30){//使用者下拉的距離不夠
state = PULL; //將狀態改為PULL
refreshViewByState();
}else if(space<=0){ //使用者下拉的距離為非正值
state = NONE; //將狀態改為NONE
isFlag = false; //標誌改為false
refreshViewByState();
}
break;
}
}
根據不同狀態,改變使用者介面的顯示
/**
* 根據當前狀態state,改變介面顯示
* state:
* NONE:無操作
* PULL:下拉可以重新整理
* RELEASE:鬆開可以重新整理
* REFREASH:正在重新整理
*/
private void refreshViewByState(){
//提示
TextView tips = (TextView)header.findViewById(R.id.tips);
//箭頭
ImageView arrow = (ImageView)header.findViewById(R.id.arrow);
//進度條
ProgressBar progress = (ProgressBar)header.findViewById(R.id.progress);
//箭頭的動畫效果1,由0度轉向180度,即箭頭由朝下轉為朝上
RotateAnimation animation1 = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation1.setDuration(500);
animation1.setFillAfter(true);
//箭頭的動畫效果2,由180度轉向0度,即箭頭由朝上轉為朝下
RotateAnimation animation2 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF,0.5f,
RotateAnimation.RELATIVE_TO_SELF,0.5f);
animation2.setDuration(500);
animation2.setFillAfter(true);
switch (state) {
case NONE: //正常狀態
arrow.clearAnimation(); //清除箭頭動畫效果
topPadding(-headerHeight); //設定header距離頂部的距離
break;
case PULL: //下拉狀態
arrow.setVisibility(View.VISIBLE); //箭頭設為可見
progress.setVisibility(View.GONE); //進度條設為不可見
tips.setText("下拉可以重新整理"); //提示文字設為"下拉可以重新整理"
arrow.clearAnimation(); //清除之前的動畫效果,並將其設定為動畫效果2
arrow.setAnimation(animation2);
break;
case RELEASE: //下拉狀態
arrow.setVisibility(View.VISIBLE); //箭頭設為可見
progress.setVisibility(View.GONE); //進度條設為不可見
tips.setText("鬆開可以重新整理"); //提示文字設為"鬆開可以重新整理"
arrow.clearAnimation(); //清除之前的動畫效果,並將其設定為動畫效果2
arrow.setAnimation(animation1);
break;
case REFRESH: //更新狀態
topPadding(50); //距離頂部的距離設定為50
arrow.setVisibility(View.GONE); //箭頭設為不可見
progress.setVisibility(View.VISIBLE); //進度條設為可見
tips.setText("正在重新整理..."); //提示文字設為""正在重新整理..."
arrow.clearAnimation(); //清除動畫效果
break;
}
}
更新資料 由於RefreshListView中不能直接更新資料,必須設定回撥介面來實現更新資料功能
IRefreshListener iRefreshlistener;//重新整理資料的介面
...
public void setInterface(IRefreshListener listener){
this.iRefreshlistener = listener;
}
/**
* 重新整理資料介面
* @author lenovo
*
*/
public interface IRefreshListener{
public void onRefresh();
}
在MainActivity中實現RefreshListView介面並實現onRefresh()方法
public class MainActivity extends Activity implements IRefreshListener{
......
@Override
public void onRefresh() {
// TODO Auto-generated method stub
//handler設定重新整理延時效果
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//獲取最新資料
getRefreshData();
//通知介面顯示
adapter.notifyDataSetChanged();
//通知listView重新整理資料完畢
listView.refreshComplete();
}
}, 2000);
}
重新整理完成的操作:
public void refreshComplete(){
state = NONE; //狀態設為正常狀態
isFlag = false; //標誌設為false
refreshViewByState();
//設定提示更新時間間隔
Time t = new Time();
t.setToNow();
time = t.hour*60+t.minute-updateTime;
updateTime = t.hour*60+t.minute;
TextView lastUpdateTime = (TextView)findViewById(R.id.time);
lastUpdateTime.setText(time+"分鐘前更新");
}
執行效果圖