您的位置:首页 > 移动开发 > Android开发

自定义View——利用下拉刷新组件实现上拉加载

2016-04-21 13:47 926 查看
注:本文demo已经提交github,地址完整代码如下,demo工程已经上传至GitHub,

github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout

感谢大家支持!

在Android开发中,我们经常会用到列表下拉刷新和上拉加载的功能。

Google在support.v4包中提供了一个组件可以用来进行下来刷新,这个组件是SwipeRefreshLayout。

下面我们来看一下这个组件的使用:

在布局文件中加上xml代码

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>

</android.support.v4.widget.SwipeRefreshLayout>


在MainActivity.java代码中添加代码:

/**
* 添加变量
* /
private SwipeRefreshLayout swipeRefreshLayout;
private ListView listView;
private ArrayList<String> list = new ArrayList<>();
private ArrayAdapter<String> adapter;


在Activity的onCreate方法中初始化实例

/**
* 实例化变量,此处省略initList()方法
* 此方法内容为添加了N个字符串"item"
*/
initList();
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
listView = (ListView) findViewById(R.id.list_view);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(adapter);


接下来给SwipeRefreshLayout添加OnRefreshListener监听

swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
Toast.makeText(MainActivity.this, "is refreshing!",          Toast.LENGTH_SHORT).show();
}
});


运行效果如图所示



但是在日常开发中,我们不仅需要下拉刷新的功能,还应该有上拉继续加载的功能,但是Google并没有给我们提供上拉加载的方法,那怎么办呢?我们可以自定义一个View,自己添加上拉加载方法。

我们模仿Swiperefreshlayout的使用下拉刷新的方法,去设计上拉加载的方法。添加了上拉加载的代码形式大约为

view.setOnLoadListener(new OnLoadListener() {
@Override
public void onLoad() {
//在此执行上拉加载的逻辑
view.setloading(false);   //逻辑执行完毕后停止上拉加载
}
});


所以,我们在重写下拉刷新组件前先写一个接口OnLoadListener

public interface OnLoadListener {
public void onLoad();  //需要用户重写的方法
}


我们写一个UpLoadSwipeRefreshLayout类,继承SwipeRefershLayout类。我们应该在什么时候开始上拉加载呢?当然是在子ListView滑动到最底部的时候,所以,我们可以开始进行上拉加载的时候应该满足一下条件:

① 判断listview已经滑动到了最底部

② 判断手指是在向上滑动

③ 当前上拉加载不正在进行

编写类

代码如下:

public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout {

private ListView mListView;  //子view

private boolean isCanLoading = false;  //是否正可以加载,默认不可用

private OnLoadListener mOnLoadListener;

private View mFooterView;  //上拉加载的视图

private int firstY;
private int lastY;

private int mTouchSlop;  //最短的滑动距离

private int footerResource;  //加载视图的资源ID

public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop =    ViewConfiguration.get(getContext()).getScaledTouchSlop();  //获取系统默认的最短滑动距离
}
}


因为ListView是此组件的子view,那么我们应该在开始的时候去检测,子view是否是listview。

重写onLayout()方法,获取子类

@Override
protected void onLayout(boolean changed, int left, int        top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mListView == null) {
getListView();  //获取子view
}
}


构造一个getListView方法

private void getListView() {
int childs = getChildCount();
if (childs > 0) {
View childView = getChildAt(0);  //获取第一个子view
if (childView instanceof ListView) {
/**
* 如果子view的类型是ListView
*/
mListView = (ListView) childView;
//给子listview设置滑动监听
mListView.setOnScrollListener(this);
}
}
}


重写事件拦截事件,防止滑动事件发生冲突

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
/**
* 获得触摸事件刚刚开始的时候,手指触摸的坐标
*/
firstY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
/**
* 获得触摸事件结束的时候,手指离开时候的坐标
*/
lastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (isCanLoad()) {
loadData();
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}


为了获取子listview的滑动情况,使用我们应该让组件实现OnScrollListener接口。



class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout                  implements AbsListView.OnScrollListener {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}

/**
* 重写滑动时间
* /
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isCanLoad()) {
/**
* 滑动过程中如果可以加载数据
* 就执行加载数据的函数
*/
loadData();
}
}
}


编写isCanLoad()方法,这里就用到了上方我们需要满足的三个条件

private boolean isCanLoad() {
return isBottom() && isPushUp() && !isCanLoading;
}


依次编写2个方法

/**
* 判断listview是否到了底部
*/
private boolean isBottom() {
if (mListView != null && mListView.getAdapter() != null) {
/**
* 当前可加的最后一项就是listview的末尾项
* 说明滑动到了最底部
*/
return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
}
return false;
}


/**
* 判断是否正在上拉
*/
private boolean isPushUp() {
/**
* 往上滑动的距离大于系统默认的滑动距离
*/
return firstY - lastY >= mTouchSlop;
}


这时候我们还需要编写loadData()方法

/**
* 加载数据
*/
private void loadData() {
if (mOnLoadListener != null) {
setLoading(true);
/**
* 处理加载的逻辑
* 具体逻辑在回调中完成
*/
mOnLoadListener.onLoad();
}
}


编写设置可加载的方法setLoading()

public void setLoading(boolean isCanLoad) {
this.isCanLoading = isCanLoad;
if (isCanLoad == true) {
/**
* 加载,添加底部的正在加载视图
*/
mListView.addFooterView(mFooterView);
} else {
/**
* 不在加载中,将底部的加载中视图移除
*/
mListView.removeFooterView(mFooterView);
firstY = 0;
lastY = 0;
}
}


对了,还没添加设置上拉加载监听的方法!

编写setOnUpLoadListener()方法

public void setOnLoadListener(OnLoadListener onLoadListener) {
this.mOnLoadListener = onLoadListener;
}


这个类我对外提供了一个变量,代表listview的底视图的资源id

可以通过setFooterResource方法进行添加并实例化此底部view

编写此方法

public void setFooterResource(int footerResource) {
this.footerResource = footerResource;
/**
* 实例化布局view
*/
this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false);
}


这时候这个类我们就编写好了,完整的代码我会在博客的结尾放出。

我们先来看看这段代码的使用

将刚才的布局文件修改成如下:

<com.example.chenglei.myapplication.UpLoadSwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"</ListView>

</com.example.chenglei.myapplication.UpLoadSwipeRefreshLayout>


添加尾视图的资源id文件,layout_up_load_view.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@android:color/white">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">

<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:indeterminateDuration="1500" />

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="5dp"
android:gravity="center"
android:text="正在加载中..." />

</LinearLayout>

</RelativeLayout>


修改MainActivity.java的代码

swipeRefreshLayout.setFooterResource(R.layout.layout_up_load_view);  //添加正在加载中的视图
swipeRefreshLayout.setOnLoadListener(new OnLoadListener() {
@Override
public void onLoad() {
//上拉加载的操作
}
});


运行效果如图:



完整代码如下,demo工程已经上传至GitHub

github地址https://github.com/wsclwps123/UpLoadSwipeRefreshLayout

package com.example.chenglei.myapplication;

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ListView;

/**
* Created by chenglei on 2016/4/20.
* 重写下拉刷新组件,添加上拉加载的功能
*/
public class UpLoadSwipeRefreshLayout extends SwipeRefreshLayout implements AbsListView.OnScrollListener {

private ListView mListView; //子view

private boolean isCanLoading = false; //是否正可以加载,默认不可用

private OnLoadListener mOnLoadListener;

private View mFooterView; //上拉加载的视图

private int firstY;
private int lastY;

private int mTouchSlop; //最短的滑动距离

private int footerResource;

public UpLoadSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); //获取系统默认的最短滑动距离
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mListView == null) {
getListView();
}
}

/**
* 获取子view
*/
private void getListView() { int childs = getChildCount(); if (childs > 0) { View childView = getChildAt(0); //获取第一个子view if (childView instanceof ListView) { /** * 如果子view的类型是ListView */ mListView = (ListView) childView; //给子listview设置滑动监听 mListView.setOnScrollListener(this); } } }

/**
* 触摸事件拦截
*/
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: /** * 获得触摸事件刚刚开始的时候,手指触摸的坐标 */ firstY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: /** * 获得触摸事件结束的时候,手指离开时候的坐标 */ lastY = (int) ev.getRawY(); break; case MotionEvent.ACTION_UP: if (isCanLoad()) { loadData(); } break; default: break; } return super.onInterceptTouchEvent(ev); }

/**
* 判断是否可以进行加载
*/
private boolean isCanLoad() { return isBottom() && isPushUp() && !isCanLoading; }

/** * 判断listview是否到了底部 */ private boolean isBottom() { if (mListView != null && mListView.getAdapter() != null) { /** * 当前可加的最后一项就是listview的末尾项 * 说明滑动到了最底部 */ return mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1); } return false; }

/** * 判断是否正在上拉 */ private boolean isPushUp() { /** * 往上滑动的距离大于系统默认的滑动距离 */ return firstY - lastY >= mTouchSlop; }

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isCanLoad()) {
/**
* 滑动过程中如果可以加载数据
* 就执行加载数据的函数
*/
loadData();
}
}

/** * 加载数据 */ private void loadData() { if (mOnLoadListener != null) { setLoading(true); /** * 处理加载的逻辑 * 具体逻辑在回调中完成 */ mOnLoadListener.onLoad(); } }

/**
* 设置可加载
*/
public void setLoading(boolean isCanLoad) { this.isCanLoading = isCanLoad; if (isCanLoad == true) { /** * 加载,添加底部的正在加载视图 */ mListView.addFooterView(mFooterView); } else { /** * 不在加载中,将底部的加载中视图移除 */ mListView.removeFooterView(mFooterView); firstY = 0; lastY = 0; } }

/**
* 设置上拉加载监听
*/
public void setOnLoadListener(OnLoadListener onLoadListener) { this.mOnLoadListener = onLoadListener; }

/**
* 设置上拉加载的布局
*/
public void setFooterResource(int footerResource) { this.footerResource = footerResource; /** * 实例化布局view */ this.mFooterView = LayoutInflater.from(getContext()).inflate(footerResource, null, false); }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android xml android开发