Android自定義控制(五)仿新浪微博的下拉重新整理
阿新 • • 發佈:2019-02-02
網上有很多很有名的開源框架,這裡就來拉拉PullToRefresh這個框架,也就是我們平時用的下拉重新整理啦,當然你問我這個有什麼用啊?別人已經寫好了,這裡主要是學習以及練習,練習的次數多了,一切就順其自然的會了.
廢話少說,先上圖,再上程式碼:
1.要想實現下拉重新整理功能必須要有個下拉重新整理的佈局,是吧?
<?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:paddingBottom="10dip" android:paddingTop="10dip" > <LinearLayout android:id="@+id/layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/state" /> <TextView android:id="@+id/lastupdate_time" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toLeftOf="@id/layout" android:layout_marginRight="20dip" android:visibility="gone" /> <ImageView android:id="@+id/arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/layout" android:layout_marginRight="19dp" android:layout_toLeftOf="@+id/layout" android:src="@drawable/arrow_down" /> </RelativeLayout> </LinearLayout>
2.你要把它加入到佈局裡面吧!
headView=layoutInflater.from(context).inflate(R.layout.header_layout, null);
this.addHeaderView(headView);
3.加入到佈局直接顯示出來也不符合需求啊,所以這一步需要隱藏佈局,當然不能和前一篇部落格(Android自定義控制元件(四)仿網易客戶端上拉載入更多)一樣直接隱藏,直接隱藏滿足不了如圖的要求,我們這裡採取的是設定頭部佈局的高度為實際高度的負值,這樣就實現了隱藏功能,當下拉的時候,還不至於一次就全部顯示出來,ok這種辦法能夠實現圖中的要求headerHeight = headView.getMeasuredHeight(); setHeaderViewHeight(-headerHeight);
/**
* 設定頭部佈局的高度
*
* @param i
*/
private void setHeaderViewHeight(int headerHeight) {
headView.setPadding(headView.getPaddingLeft(), headerHeight,
headView.getPaddingRight(), headView.getPaddingBottom());
//重繪
headView.invalidate();
}
4.跑起來之後,白瞎了,沒實現想要的功能,也就是說沒有把頭部佈局隱藏掉,哎,跟蹤程式碼之後發現高度為0,郭神說過在measure之前,getMeasureWidth()和getMeasureHeight()都為零,好吧,那就先measure吧!
private void measureView(View view) { ViewGroup.LayoutParams lp = view.getLayoutParams(); if (lp == null) { lp = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width); int height; int tempHeight=lp.height; if (tempHeight > 0) { height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); } else { height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } view.measure(width, height); }
5.隱藏成功了,下面就要實現具體的功能了,下拉重新整理,鬆開重新整理,正在重新整理這三個狀態,是通過手勢改變狀態,所以這裡要實現onTouch,當然還有OnScrollListener
需要用到firstVisibleItem判斷Listview向上滑動還是向下滑動,如果firstVisibleItem==0說明到達ListView的頭部了,當然你還需要一個布林值判斷是否按下滑動
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItem=firstVisibleItem;
}
重寫onTouchEvent,通過firstVisibleItem和布林值判斷按下,擡起,滑動@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisibleItem == 0) {
isRemark = true;
startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if (state==RELEASE) {
state=REFRASH;
reflashViewByState();
//更新資料
isRefreshListener.onRefresh();
}else if (state==PULL) {
state=NONE;
isRemark=false;
refreshDrawableState();
}
break;
}
return super.onTouchEvent(ev);
}
程式碼不全,先解釋一下,後面附上全部程式碼
按下時,如果firstVisibleItem為0,說明到達listview的頂部,並且可以按下,把isRemark賦值為true,滑動時改變頭部佈局的狀態
/**
* 移動過程的狀態變換
*
* @param ev
*/
private void onMove(MotionEvent ev) {
if (!isRemark) {
return;
}
int tempY = (int) ev.getY();
int space = tempY - startY;
int topPadding = space - headerHeight;
switch (state) {
case NONE:
if (space>0) {
state=PULL;
reflashViewByState();
}
break;
case PULL:
setHeaderViewHeight(topPadding);
if (space>headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
state=RELEASE;
reflashViewByState();
}
break;
case RELEASE:
setHeaderViewHeight(topPadding);
if (space<headerHeight+30) {
state=PULL;
reflashViewByState();
}else if (space<=0) {
state=NONE;
reflashViewByState();
}
break;
}
}
根據滑動之後和動畫前y值的變化判斷滑動狀態,當space大於零時,當前狀態變為下拉重新整理,如果space大於某個值時,當前狀態變為鬆開可以重新整理,當space大於零小於某個值時,當前狀態為下拉重新整理狀態,當space小於零時,當前狀態變為正常狀態.當然狀態改變時,介面也要隨著改變
/**
*根據狀態重新整理當前頁面
*/
private void reflashViewByState() {
TextView tip = (TextView) headView.findViewById(R.id.tip);
ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(500);
anim.setFillAfter(true);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
switch (state) {
case NONE:
setHeaderViewHeight(-headerHeight);
arrow.clearAnimation();
break;
case PULL:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("下拉重新整理");
arrow.clearAnimation();
arrow.setAnimation(anim1);
break;
case RELEASE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("鬆開重新整理");
arrow.clearAnimation();
arrow.setAnimation(anim);
break;
case REFRASH:
setHeaderViewHeight(50);
arrow.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
tip.setText("正在重新整理");
arrow.clearAnimation();
break;
}
}
介面上主要改變的就是提示,箭頭和progress,正常狀態下,介面不可見,下拉重新整理狀態下,箭頭可見並且朝下,提示資訊為下拉重新整理並且progress不可見,鬆開重新整理狀態,箭頭朝上,progress不可見,提示資訊為下拉重新整理,正在載入狀態箭頭不可見,progress可見,提示資訊改為正在重新整理
當然,在變成正在載入狀態時,還要載入更過資料
public interface IsRefreshListener{
public void onRefresh();
}
public void setIsRefreshListener(IsRefreshListener isRefreshListener){
this.isRefreshListener=isRefreshListener;
}
載入完資料後,還要通知listview重新整理結束
/**
* 獲取完資料;
*/
public void reflashComplete() {
state = NONE;
isRemark = false;
reflashViewByState();
TextView lastupdatetime = (TextView) headView
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
}
自定義下拉重新整理控制元件就這樣完成了,不懂得留言吧,我儘量給你解答,自定義這東西,寫多了也就知道怎麼寫了
下面附上自定義下拉重新整理控制元件的全部程式碼:
package com.sdufe.thea.guo.view;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import com.sdufe.thea.guo.R;
public class PullToRefreshListView extends ListView implements OnScrollListener {
View headView;
int headerHeight;
int firstVisibleItem;
int scrollState;
boolean isRemark;
int startY;
int state;
final int NONE = 0;
final int PULL = 1;
final int RELEASE = 2;
final int REFRASH = 3;
IsRefreshListener isRefreshListener;
public PullToRefreshListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public PullToRefreshListView(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
headView = LayoutInflater.from(context).inflate(R.layout.header_layout,
null);
measureView(headView);
headerHeight = headView.getMeasuredHeight();
setHeaderViewHeight(-headerHeight);
addView(headView);
setOnScrollListener(this);
}
/**
* 計算寬高
*
* @param view
*/
private void measureView(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null) {
lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int height;
int tempHeight = lp.height;
if (tempHeight > 0) {
height = MeasureSpec.makeMeasureSpec(tempHeight,
MeasureSpec.EXACTLY);
} else {
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
/**
* 設定頭部佈局的高度
*
* @param i
*/
private void setHeaderViewHeight(int headerHeight) {
headView.setPadding(headView.getPaddingLeft(), headerHeight,
headView.getPaddingRight(), headView.getPaddingBottom());
// 重繪
headView.invalidate();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisibleItem == 0) {
isRemark = true;
startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if (state==RELEASE) {
state=REFRASH;
reflashViewByState();
//更新資料
isRefreshListener.onRefresh();
}else if (state==PULL) {
state=NONE;
isRemark=false;
refreshDrawableState();
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 移動過程的狀態變換
*
* @param ev
*/
private void onMove(MotionEvent ev) {
if (!isRemark) {
return;
}
int tempY = (int) ev.getY();
int space = tempY - startY;
int topPadding = space - headerHeight;
switch (state) {
case NONE:
if (space>0) {
state=PULL;
reflashViewByState();
}
break;
case PULL:
setHeaderViewHeight(topPadding);
if (space>headerHeight+30&&scrollState==SCROLL_STATE_IDLE) {
state=RELEASE;
reflashViewByState();
}
break;
case RELEASE:
setHeaderViewHeight(topPadding);
if (space<headerHeight+30) {
state=PULL;
reflashViewByState();
}else if (space<=0) {
state=NONE;
reflashViewByState();
}
break;
}
}
/**
* 獲取完資料;
*/
public void reflashComplete() {
state = NONE;
isRemark = false;
reflashViewByState();
TextView lastupdatetime = (TextView) headView
.findViewById(R.id.lastupdate_time);
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date date = new Date(System.currentTimeMillis());
String time = format.format(date);
lastupdatetime.setText(time);
}
/**
*根據狀態重新整理當前頁面
*/
private void reflashViewByState() {
TextView tip = (TextView) headView.findViewById(R.id.tip);
ImageView arrow = (ImageView) headView.findViewById(R.id.arrow);
ProgressBar progress = (ProgressBar) headView.findViewById(R.id.progress);
RotateAnimation anim = new RotateAnimation(0, 180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(500);
anim.setFillAfter(true);
RotateAnimation anim1 = new RotateAnimation(180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
anim1.setDuration(500);
anim1.setFillAfter(true);
switch (state) {
case NONE:
setHeaderViewHeight(-headerHeight);
arrow.clearAnimation();
break;
case PULL:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("下拉重新整理");
arrow.clearAnimation();
arrow.setAnimation(anim1);
break;
case RELEASE:
arrow.setVisibility(View.VISIBLE);
progress.setVisibility(View.GONE);
tip.setText("鬆開重新整理");
arrow.clearAnimation();
arrow.setAnimation(anim);
break;
case REFRASH:
setHeaderViewHeight(50);
arrow.setVisibility(View.GONE);
progress.setVisibility(View.VISIBLE);
tip.setText("正在重新整理");
arrow.clearAnimation();
break;
}
}
public interface IsRefreshListener{
public void onRefresh();
}
public void setIsRefreshListener(IsRefreshListener isRefreshListener){
this.isRefreshListener=isRefreshListener;
}
}
下面就是怎麼用了<com.sdufe.thea.guo.view.PullToRefreshListView
android:id="@+id/pull_to_refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
下面的用法就跟listview差不多了,提示一點要實現IsRefreshListener介面,在onRefresh()裡面載入更多資料
package com.sdufe.thea.guo;
import java.util.ArrayList;
import java.util.List;
import com.sdufe.thea.guo.view.PullToRefreshListView;
import com.sdufe.thea.guo.view.PullToRefreshListView.IsRefreshListener;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.ArrayAdapter;
public class MainActivity extends Activity implements IsRefreshListener{
PullToRefreshListView listView;
ArrayAdapter<String> adapter;
List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (PullToRefreshListView) findViewById(R.id.pull_to_refresh);
initData();
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);
}
private void initData() {
list = new ArrayList<String>();
list.add("123456789");
list.add("123456789");
list.add("123456789");
}
@Override
public void onRefresh() {
list.add("爸爸");
list.add("媽媽");
list.add("我");
adapter.notifyDataSetChanged();
listView.reflashComplete();
}
}
今天就到此結束啦,不懂的,留言