通過(guò)這個(gè)文章你可以學(xué)習(xí)到:
listview的使用
觸摸事件監(jiān)聽
回調(diào)函數(shù)的簡(jiǎn)單應(yīng)用
線程的使用
最后肯定是實(shí)現(xiàn)下拉刷新啦
先讓我們看看最終效果
那么我們就開始吧
1.因?yàn)橐玫阶远x的listview,我們這里先新建一個(gè)view繼承l(wèi)istview
(ps:如果要在XML配置該View的話,我們至少要實(shí)現(xiàn)前面兩個(gè)構(gòu)造方法)
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.創(chuàng)建一個(gè)listview的item布局,然后我們?cè)僦鞑季治募惺褂梦覀冏约旱腣iew
(ps:listview_item.xml,該布局可以自由擴(kuò)展,我這里就用最簡(jiǎn)單的)
<?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:gravity="center_vertical">
<ImageView
android:layout_marginLeft="10dp"
android:id="@+id/listview_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:paddingTop="15dp"
android:layout_marginLeft="10dp"
android:id="@+id/listview_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="111"
android:textSize="20dp"/>
</LinearLayout>
(ps:activity_main)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.a455.mypurefresh.MainActivity">
<com.example.a455.mypurefresh.MyListView
android:id="@+id/myview"
android:layout_width="368dp"
android:layout_height="wrap_content"
tools:layout_editor_absoluteY="0dp"
tools:layout_editor_absoluteX="8dp">
</com.example.a455.mypurefresh.MyListView>
</android.support.constraint.ConstraintLayout>
3.我們給listview添加適配器,這里使用的simpleadapter
(ps:我這里就直接貼代碼,很簡(jiǎn)單的,就是調(diào)用了三個(gè)簡(jiǎn)單的方法)
public class MainActivity extends AppCompatActivity {
MyListView myListView;
SimpleAdapter simpleAdapter;
List<Map<String,Object>> data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
initData();
initListView();
}
/**
* @methodName: initListView
* @Description: listview添加設(shè)配器
* @param
* @return
* @throws
*/
private void initListView() {
simpleAdapter=new SimpleAdapter(
this, //上下文
data, //數(shù)據(jù)集
R.layout.listview_item, //item布局文件
new String[]{"text","image"}, //map集合中的鍵值
new int[]{R.id.listview_text,R.id.listview_image} //item布局文件中的控件id
myListView.setAdapter(simpleAdapter);
}
/**
* @methodName: initData
* @Description: 對(duì)數(shù)據(jù)初始化
* @param
* @return
* @throws
*/
private void initData() {
data=new ArrayList<>();
for (int i = 0; i < 20; i++) {
Map<String,Object> map=new HashMap<String,Object>();
map.put("text",i);
map.put("image",R.mipmap.ic_launcher);
data.add(map);
}
}
private void findView() {
myListView=(MyListView)findViewById(R.id.myview);
}
到這里我們就顯示出一個(gè)列表了,前面都是配菜,現(xiàn)在開始主菜
4. 先創(chuàng)建我們的下拉頭部布局文件
(ps:header.xml,這里主要注意的是progressBar中的visibility:gone
gone意味隱藏并且不占用空間)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_centerInParent="true"
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉可刷新"/>
<TextView
android:layout_marginTop="5dp"
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/header_image"
android:layout_marginRight="10dp"
android:src="@drawable/pull_down"
android:layout_toLeftOf="@id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
5. ok,頭部文件有了,那么我們要把它加到我們自定義view的頂部
(這里我就寫關(guān)鍵代碼啦,記得在構(gòu)造方法中調(diào)用該方法)
View header;
int headerHight;
private void init(Context context) {
//解析header布局文件
header = LayoutInflater.from(context).inflate(R.layout.header, null);
//測(cè)量header的高寬,具體可以查看MeasureSpec包
int width = View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int hight = View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
header.measure(width, hight);
//獲取頭部的高度,用于后面隱藏我下拉
headerHight=header.getMeasuredHeight();
//將header添加到listview的頂部
this.addHeaderView(header);
}
那我們的布局現(xiàn)在是這樣的:
我們用一個(gè)方法把它隱藏:
/**
* 根據(jù)傳入的高度設(shè)置header的paddingtop
*/
private void toPadding(int i) {
header.setPadding(
header.getPaddingLeft(),
i,
header.getPaddingRight(),
header.getPaddingBottom()
);
header.invalidate();
}
然后我們只需要在addHeaderView前面調(diào)用該方法
//隱藏頭部文件
toPadding(-headerHight);
//將header添加到listview的頂部
this.addHeaderView(header);
6. 那么接下來(lái)我們就是該監(jiān)聽觸摸下拉的事件了,這里是難點(diǎn)
(ps:首先添加滑動(dòng)監(jiān)聽接口,重寫它要實(shí)現(xiàn)的方法,定義兩個(gè)變量來(lái)存儲(chǔ)信息
不要忘了添加回調(diào)接口喲)
public class MyListView extends ListView implements AbsListView.OnScrollListener{
int scrollState; //當(dāng)前view的滾動(dòng)狀態(tài)
int firstVisibleItem; //可見的第一個(gè)item
.....//之前的內(nèi)容省略啦
private void init(Context context) {
.....//之前的內(nèi)容省略啦
this.setOnScrollListener(this);
}
@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;
}
然后我們?cè)偬砑訋讉€(gè)變量用來(lái)表示下拉的狀態(tài):
public class MyListView extends ListView implements AbsListView.OnScrollListener{
int startY; //觸摸屏幕時(shí)的高度
boolean isRemak; //是否可以刷新
int state=0; //當(dāng)前header狀態(tài)
final int NONE=0; //正常狀態(tài)
final int PULL=1; //下拉狀態(tài)
final int RELEASE=2; //提示刷新狀態(tài)
final int REFRESHING=3; //正在刷新狀態(tài)
......
重頭戲來(lái)啦,我們重寫onTouche方法:
/*
*觸摸事件處理,分三種情況,按下,移動(dòng),抬起
* 按下:判斷當(dāng)前是否為listview頂部,如果是記錄按下位置
* 移動(dòng):調(diào)用onMove方法處理移動(dòng)情況
* 抬起:如果當(dāng)前狀態(tài)為提示刷新狀態(tài)就對(duì)header進(jìn)行更新,并調(diào)用刷新內(nèi)容接口
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
if(firstVisibleItem==0){
isRemak=true;
startY=(int)ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev); //判斷state的狀態(tài)
break;
case MotionEvent.ACTION_UP:
if(state==RELEASE){
state=REFRESHING;
refreshByState();//刷新header
//iRefreshen.onRefresh(); //刷新listview,這里是通過(guò)接口回調(diào)的方法來(lái)實(shí)現(xiàn)的我們先注釋掉,后面再來(lái)實(shí)現(xiàn)
}else if(state==PULL){
state=NONE;
isRemak=false;
refreshByState();
}
break;
}
return super.onTouchEvent(ev);
}
onMove方法是監(jiān)聽當(dāng)我們?cè)倩瑒?dòng)的時(shí)候,我們的state是處于什么狀態(tài)
/*
*觸摸移動(dòng)的時(shí)候判斷移動(dòng)的位置
* 若下拉高過(guò)或低于設(shè)定的數(shù)值,則改變提示信息
*/
private void onMove(MotionEvent ev) {
if(!isRemak){
return;
}
int tempY=(int) ev.getY();
int distance=tempY-startY;
int toPadding=distance-headerHight;
switch (state){
case NONE:
if(distance>0){
state=PULL;
refreshByState();
}
break;
case PULL:
toPadding(toPadding);
if(distance>(headerHight+150) && scrollState==SCROLL_STATE_TOUCH_SCROLL){
state=RELEASE;
refreshByState();
}
break;
case RELEASE:
toPadding(toPadding);
if(distance<(headerHight+150)){
state=PULL;
refreshByState();
}
break;
}
}
refreshByState就是我們?cè)诨瑒?dòng)的時(shí)候根據(jù)state的狀態(tài)來(lái)更新header的高度和內(nèi)容信息
(ps:這里我用的是動(dòng)畫將圖片旋轉(zhuǎn)了,小伙伴也可以用兩張圖片,一張下拉,一張上拉
還有控件的獲取要在解析header布局文件后獲取)
/*
*通過(guò)當(dāng)前state狀態(tài)改變header的顯示布局
*/
private void refreshByState() {
switch (state){
case NONE:
// imageView.clearAnimation();
toPadding(-headerHight);
imageView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
break;
case PULL:
imageView.clearAnimation();
imageView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
imageView.setAnimation(anim2);
tip.setText("下拉可刷新");
break;
case RELEASE:
imageView.clearAnimation();
imageView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
imageView.setAnimation(anim1);
tip.setText("松開可刷新");
break;
case REFRESHING:
toPadding(50);
imageView.clearAnimation();
imageView.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
tip.setText("正在刷新");
break;
}
}
7. 到這里我們就實(shí)現(xiàn)了監(jiān)聽下拉刷新了,現(xiàn)在來(lái)做刷新后的事情
(ps:上面部分肯比較多,慢慢消化,亂的話可以看看源碼)
我們寫一個(gè)刷新后把headr布局,各種信息還原并且更新時(shí)間的方法
/*
*刷新完成后調(diào)用此方法重新設(shè)置參數(shù),并設(shè)置上次刷新時(shí)間
*/
public void refreshComplete(){
state=NONE;
isRemak=false;
refreshByState();
Date date=new Date(System.currentTimeMillis());
SimpleDateFormat format=new SimpleDateFormat("yy年mm月dd日 HH:MM:SS");
String time=format.format(date);
this.time.setText(time);
}
8. 最后一部,我們預(yù)留一個(gè)接口,讓外面來(lái)完成刷新后的事情
public class MyListView extends ListView implements AbsListView.OnScrollListener{
........
......
.....
public void setInterface(IRefreshen iRefreshen){
this.iRefreshen=iRefreshen;
}
public interface IRefreshen{
public void onRefresh();
}
}
主方法去實(shí)現(xiàn)該接口
(PS: //這里使用handler延遲2秒來(lái)模擬刷新時(shí)候的等待)
public class MainActivity extends AppCompatActivity implements MyListView.IRefreshen{
private void findView() {
......
myListView.setInterface(this);
}
@Override
public void onRefresh() {
Handler handler=new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Map<String,Object> map=new HashMap<String,Object>();
map.put("text","刷新");
map.put("image",R.mipmap.ic_launcher);
data.add(0,map);
myListView.setAdapter(simpleAdapter);
simpleAdapter.notifyDataSetChanged();
myListView.refreshComplete();
}
},2000);
}
至此,恭喜大家實(shí)現(xiàn)下拉刷新了
我來(lái)總結(jié)下思路:
- 創(chuàng)建自定義Listview布局,在主布局中使用該布局
- 創(chuàng)建header布局并添加進(jìn)Listview的頭部,并用paddingTop來(lái)隱藏
- 觸摸事件的監(jiān)聽和處理
- 用回調(diào)接口,實(shí)現(xiàn)在MainAcitivity中刷新內(nèi)容
其實(shí)總體思路很簡(jiǎn)單,難點(diǎn)就在于觸摸的時(shí)候?qū)ξ恢玫谋O(jiān)聽和處理,這里比較繁瑣,但是慢慢理解就不會(huì)覺(jué)得很難了
這是我第一次分享,希望大家支持!!!
有發(fā)現(xiàn)問(wèn)題的可以留言,謝謝大家觀賞,你的點(diǎn)贊是我繼續(xù)分享的動(dòng)力!!!