系统控件SwipeRefreshLayout浅析
下拉刷新(上拉刷新)是近两三年android APP比较流行的实现页面更新的控件,可以使用android.support.v4.widget包的原生控件SwipeRefreshLayout实现下拉刷新功能,本demo实现了每次执行下拉刷新后生成一个1-100之间的随机整数,效果如下:
向下拉动
正在刷新
刷新完毕
xml布局文件
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_world" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新以生成一个随机数:"
android:textSize="23sp" />
<TextView
android:id="@+id/text_view_generate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#69FF0000"
android:textSize="25sp" />
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
xml布局代码如上所示,在引入SwipeRefreshLayout控件时,需要写全限定类名;另外需要注意的是,该控件只能嵌套一个直接子控件,而且这个子控件必须是具有滑动功能的AdapterView(如ListView,Spinner,GridView等)。
activity业务逻辑
public class MainActivity extends Activity {
private SwipeRefreshLayout mSwipeRefreshLayout;
private TextView mTextView;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text_view_generate);
mHandler = new Handler();
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
mSwipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh() {
mSwipeRefreshLayout.setRefreshing(true);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(false);
int i = (int) (Math.random() * 100 + 1);
mTextView.setText(String.valueOf(i));
}
}, 3000);
}
});
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
activity代码如上所示,首先声明SwipeRefreshLayout控件并绑定setOnRefreshListener监听器,参数是一个OnRefreshListener类型的接口对象,需要实现接口中未实现的方法onRefresh,在该方法中首先调用SwipeRefreshLayout的方法setRefreshing(true)方法,表示开启动画效果,接着在Handler的postDelay方法中传入两个参数,第一个参数是一个匿名Runnable接口对象,第二个参数是一个int类型整数,本demo中传入3000,代表3秒,该方法表示动画显示的持续时长,然后在run方法中关闭动画效果,并进行相应的UI操作。
系统自带的原生下拉刷新控件SwipeRefreshLayout的优点是比较简洁,没有过多花哨的动画效果,android版chrome浏览器的下拉刷新功能便使用的该控件(chrome是我最喜欢的android浏览器),然而缺点也显而易见,那就是扩展性不够好,对于现今这个APP也看脸的时代,没有炫酷的动画已经很难满足用户的需要,另外,该控件不支持上拉刷新,如果打算实现上拉刷新和更多动画效果,需要下面介绍的第三方控件。
第三方控件PullToRefresh介绍
PullToRefresh是一个开源控件,不仅支持下拉刷新,还支持上拉刷新,而且可以利用刷新时的动画效果为用户提供有用信息(如刷新时间,手势操作提示等),很多商业级别的应用已经才用了这个控件,该控件有以下特点:
- 支持上拉刷新、下拉刷新或同时支持上拉刷新和下拉刷新;
- 支持所有设备的动画滑动;
- Android2.3版本以上支持整屏下拉(上拉);
- 支持下列控件作为直接子控件
-ListView
-ExpandableListView
-GridView
-WebView
-ScrollView
-HorizontalScrollView
-ViewPager - 当滑到底部时可以弹出信息提示;
- 支持Maven;
- 当某个页面具有下拉刷新(上拉刷新)的功能时,给提示用户;
- 许多自定义属性;
- 支持ListFragment。
该控件的demo源程序可以在github上下载,地址是:https://github.com/chrisbanes/Android-PullToRefresh。下面将浅析其中的部分代码。
PullToRefresh效果
先看看该控件包含一个ListView的效果:
向下拉动,显示”放开以刷新”的提示和上次刷新的时间,左边显式圆形箭头图标
松手后,显示”正在载入…”,左侧的圆形箭头按钮不断转动
刷新完毕后,ListView在第一行增加一条内容”Added after refresh…”
当滑到底部时,Toast出”End of List!”
PullToRefresh的xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.handmark.pulltorefresh.library.PullToRefreshListView
android:id="@+id/pull_refresh_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:divider="#19000000"
android:dividerHeight="4dp"
android:fadingEdge="none"
android:fastScrollEnabled="false"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
android:smoothScrollbar="true" />
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
xml布局控件做了封装,控件com.handmark.pulltorefresh.library.PullToRefreshListView里面含有一个ListView。
PullToRefresh的activity
public final class PullToRefreshListActivity extends ListActivity {
static final int MENU_MANUAL_REFRESH = 0;
static final int MENU_DISABLE_SCROLL = 1;
static final int MENU_SET_MODE = 2;
static final int MENU_DEMO = 3;
private LinkedList<String> mListItems;
private PullToRefreshListView mPullRefreshListView;
private ArrayAdapter<String> mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ptr_list);
mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);
new GetDataTask().execute();
}
});
mPullRefreshListView.setOnLastItemVisibleListener(new OnLastItemVisibleListener() {
@Override
public void onLastItemVisible() {
Toast.makeText(PullToRefreshListActivity.this, "End of List!", Toast.LENGTH_SHORT).show();
}
});
ListView actualListView = mPullRefreshListView.getRefreshableView();
registerForContextMenu(actualListView);
mListItems = new LinkedList<String>();
mListItems.addAll(Arrays.asList(mStrings));
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems);
mPullRefreshListView.setOnPullEventListener(soundListener);
actualListView.setAdapter(mAdapter);
}
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
@Override
protected String[] doInBackground(Void... params) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
}
return mStrings;
}
@Override
protected void onPostExecute(String[] result) {
mListItems.addFirst("Added after refresh...");
mAdapter.notifyDataSetChanged();
mPullRefreshListView.onRefreshComplete();
super.onPostExecute(result);
}
}
private String[] mStrings = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
"Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
"Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
"Allgauer Emmentaler" };
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
首先为PullToRefresh控件绑定监听器setOnRefreshListener,参数是匿名接口OnRefreshListener对象,注意,若希望同时实现上拉刷新和下拉刷新,需实现匿名接口OnRefreshListener2;在onRefresh方法中首先设置了显示刷新的时间,然后执行异步任务AsyncTask,在onPostExecute方法中为ListView增加一行,并通知Adapter刷新页面,最后调用onRefreshComplete方法完成刷新;绑定setOnLastItemVisibleListener监听器用于监听ListView滑到底部时的情况。
利用PullToRefresh实现自定义demo
利用PullToRefresh控件,实现了每次下拉刷新(或上拉刷新)后,生成一组双色球号码的demo。
效果如下所示:
向下拉动,显示文字提示”放开以刷新” 左侧有Android机器人图标转动
松开手,显示”正在载入…”,左侧Android机器人图标转动
刷新完成,生成一组双色球随机号码
向上拉动,底部提示”放开以刷新”,左侧Android机器人图标转动
松开手,显示”正在载入…”,左侧Android机器人图标转动
刷新完成,生成第二组双色球随机号码
xml布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<com.handmark.pulltorefresh.library.PullToRefreshListView
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:id="@+id/pull_refresh_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:divider="#19000000"
android:dividerHeight="4dp"
android:fadingEdge="none"
android:fastScrollEnabled="false"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
android:smoothScrollbar="true"
ptr:ptrMode="both"
ptr:ptrDrawable="@drawable/ic_launcher" />
</RelativeLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
注意要引用控件的全限定类名com.handmark.pulltorefresh.library.PullToRefreshListView。
activity业务逻辑
public class MainActivity extends Activity {
private PullToRefreshListView mPullToRefresh;
private ArrayAdapter<String> mArrayAdapter;
private List<String> mList;
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mList = new ArrayList<String>();
mArrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mList);
mPullToRefresh = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
mListView = mPullToRefresh.getRefreshableView();
mListView.setAdapter(mArrayAdapter);
mPullToRefresh.setOnRefreshListener(new OnRefreshListener2() {
@Override
public void onPullDownToRefresh(PullToRefreshBase refreshView) {
new GetDataTask().execute();
}
@Override
public void onPullUpToRefresh(PullToRefreshBase refreshView) {
new GetDataTask().execute();
}
});
}
private class GetDataTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return generateData();
}
@Override
protected void onPostExecute(String result) {
mList.add(result);
mArrayAdapter.notifyDataSetChanged();
mPullToRefresh.onRefreshComplete();
super.onPostExecute(result);
}
private String generateData()
{
Set<String> treeSet = new TreeSet<String>();
do {
treeSet.add(generateARedBall());
} while (treeSet.size() <= 6);
String[] _string = new String[6];
_string = treeSet.toArray(_string);
StringBuilder _stringBuilder = new StringBuilder();
_stringBuilder.append("【第" + (mList.size() + 1) + "组】红:");
for (int _i = 0; _i < 6; _i++) {
_stringBuilder.append(_string[_i]);
_stringBuilder.append(", ");
}
_stringBuilder = _stringBuilder.delete(_stringBuilder.length() - 2,
_stringBuilder.length() - 1);
_stringBuilder.append("蓝:" + generateABlueBall());
return _stringBuilder.toString();
}
private String generateARedBall() {
int i = (int) (Math.random() * 99 + 1);
if (i < 10) {
return "0" + String.valueOf(i);
} else {
return String.valueOf(i);
}
}
private String generateABlueBall() {
int i = (int) (Math.random() * 16 + 1);
if (i < 10) {
return "0" + String.valueOf(i);
} else {
return String.valueOf(i);
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
代码实现了上述图片所示效果,该程序通过匿名接口OnRefreshListener2对象实现了上拉刷新和下拉刷新两种手势操作,生成双色球随机