利用SmartRefreshLayout自定义下拉刷新View

SmartRefreshLayout是一个扩展性非常强的下拉刷新控件。
以下是官方开源地址的说明:

  • 支持下拉刷新、上拉加载、二级刷新、淘宝二楼、RefreshLayout、OverScroll,
  • Android智能下拉刷新框架,支持越界回弹、越界拖动,具有极强的扩展性,
  • 集成了几十种炫酷的Header和 Footer。

嗯,的确很好用的,就不讲怎么用了,教怎么用的文章太多了。要不自己也写一个下拉刷新View吧!

要相信,星星之火可以燎原~~


SmartRefreshLayout简单使用教程

前面说不写怎么使用,不过要自定义view,还是说一说吧。简单一点的。

引入依赖库

猜得不错的话,简单的列表就需要以下两个库

1
2
3
4
//下拉刷新
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.5'
//列表
implementation 'com.android.support:recyclerview-v7:27.1.1'

xml 布局引入SmartRefreshLayout

这里的HappyDrawable,就是今天要将的自定义刷新View

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
<?xml version="1.0" encoding="utf-8"?>
<com.scwang.smartrefresh.layout.SmartRefreshLayout 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:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srlDragRate="0.7"
app:srlEnablePreviewInEditMode="true"
app:srlHeaderMaxDragRate="1.3"
app:srlHeaderTriggerRate="0.5">

<top.goluck.refreshlayout2018_06_10.HappyDrawable
android:id="@+id/gifview"
android:layout_width="match_parent"
android:layout_height="60dp"
app:layout_srlBackgroundColor="@android:color/transparent"
app:layout_srlSpinnerStyle="Translate" />

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:overScrollMode="never"
android:paddingTop="?attr/actionBarSize" />

</com.scwang.smartrefresh.layout.SmartRefreshLayout>

java代码实现下拉刷新相关代码

都挺简单的,看注释吧!

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
@Override
public class MainActivity extends AppCompatActivity {

private HappyDrawable gifview;
private SmartRefreshLayout refreshLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 自定义的头部动画
gifview = findViewById(R.id.gifview);
// 下拉刷新控件
refreshLayout = findViewById(R.id.refreshLayout);
// 开启自动刷新
refreshLayout.autoRefresh();
// 延时完成刷新动画
finishRefresh();
}

private Handler mHandler;
private void finishRefresh() {
if (mHandler == null) {
mHandler = new Handler();
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//结束下拉刷新动画
refreshLayout.finishRefresh();
}
}, 5000);
}

}

相关全面介绍推荐

自定义HappyDrawable实现步骤

实现逻辑介绍

这里有两张图片,图我是不会做的,这辈子,我也不知道我会不会做,多半还是不会做;嗯,就是不会做图的。

需要达到什么效果呢?如下

  • 这是一只happy的傻僵尸,一张图包含了他运动的各个状态,我要达到的目的就是让它行动起来。嗨起来
  • 这是一张背景图,嗯,我还是要让它动起来,但需要保证占满屏幕宽度

自定义HappyDrawable具体步骤

实现RefreshHeader接口

这里必须实现的一些方法介绍

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
public class HappyDrawable extends View implements RefreshHeader {

//不用多解释
@NonNull
@Override
public View getView() {
return this;
}

/**
* 顶部和底部的组件在拖动时候的变换方式,这里用平移模式
*/
@NonNull
@Override
public SpinnerStyle getSpinnerStyle() {
return SpinnerStyle.Translate;
}

/**
* 状态改变事件 {@link RefreshState}
* 这里获取了3个状态,
* * 往下拉绘制静态的背景
* * 正在刷新的时候,绘制动态的背景 and 让happy的傻僵尸动起来减肥
* * 刷新完成,停止绘制
* @param refreshLayout RefreshLayout
* @param oldState 改变之前的状态
* @param newState 改变之后的状态
*/
@Override
public void onStateChanged(RefreshLayout refreshLayout, RefreshState oldState, RefreshState newState) {
switch (newState) {
//开始往下拉
case PullDownToRefresh:
begin();
break;
//正在刷新
case Refreshing:
start();
break;
//刷新完成
case RefreshFinish:
stop();
break;
}
}
}
//以下是一些状态码相关的逻辑

/**
* 下拉准备中,显示第一帧
*/
private void begin() {
//代表下拉出来可见,可绘制静态的背景
isShow = true;
invalidate();
}

/**
* 开始动画
*/
private void start() {
//执行动画逻辑
setRuning(true);
invalidate();
}

/**
* 结束动画
*/
private void stop() {
//动画结束,回收所有状态
isShow = false;
setRuning(false);
invalidate();
}

绘制准备属性值

该 Happy可在初始化的地方,调用赋值,准备待用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Happy {
//happy傻僵尸大图
public Bitmap happy;
//happy背景大图
public Bitmap happy_bg;
//背景图片宽度高度
public int happy_bg_width;
public int happy_bg_height;
//图片宽度高度 (每帧)
public int happy_width;
public int happy_height;

public Happy(Context context) {

happy_bg = BitmapFactory.decodeResource(context.getResources(), R.drawable.shi);
happy_bg_width = happy_bg.getWidth();
happy_bg_height = happy_bg.getHeight();

happy = BitmapFactory.decodeResource(context.getResources(), R.drawable.happy);
happy_width = happy.getWidth() / 11;
happy_height = happy.getHeight() / 2;
}

}

绘制从右往左的动画

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

// 绘制代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isShow) {
int viewWidth = getWidth();
int viewHeight = getHeight();
//绘制背景图片
drawBg(canvas, viewWidth);
//这里延时150毫秒执行一次绘制逻辑
postInvalidateDelayed(150);
}
}

// 背景总共移动的位置
private int nowX=0;
// 背景每次移动的距离
private int speed=5;
// 运动状态,运动的时候为 true
private boolean isRuning = false;

private void drawBg(Canvas canvas, int viewWidth) {
int imgW = mHappy.happy_bg_width;
if (isRuning) {
// 图片左边不可见部分小于图片的宽度
if (-nowX < imgW) {
nowX -= speed;
} else {
nowX = 0;
}
}
int index = 0;
//画布移动到0,0坐标
canvas.translate(0, 0);
// index表示绘制了多少张图片,直到绘制的图片张数大于等于屏幕给定的宽度为止
while (index * imgW + nowX < viewWidth) {
canvas.drawBitmap(mHappy.happy_bg, index * imgW + nowX, 0, null);
index++;
}
}

主要实现逻辑介绍:

  • 每隔150毫秒在可见状态下,绘制一次静态或动态背景图
  • 绘制铺满屏幕逻辑:利用已绘制图片宽度和屏幕宽度相比,不够就继续绘制到铺满位置。
  • 绘制动态背景图逻辑:利用自定义移动速度,自定义移动距离,在移动超过一张图的时候把移动距离归0

绘制移动的小怪兽

第一问:嗯,你没有看错,这只是一张图,不切图可以实现么?

第一答:嗯,系统提供了该方法,好像可以实现

  • Bitmap 要绘制的图片
  • Rect src 指定图片需要绘制的区域
  • Rect dst 指定图片在屏幕上显示的区域
  • Paint paint 画笔
    1
    2
    canvas.drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
    @Nullable Paint paint)

第二问:怎么绘制到指定的位置

第二答:系统还提供了该方法:

1
2
// 将画布坐标系移动到画布中央
canvas.translate(float dx, float dy);

第三问:怎么计算切割出来的图片

第三答:

这张图片总共需要切割为22张。
由两排组成。
切割逻辑可如下实现:

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
private int index = 0 ;
//
//viewWidth 可绘制宽
//viewHeight 可绘制高
//
private void drawHappy(Canvas canvas, int viewWidth, int viewHeight){

//判断当前该绘制第几行
int row = index < 11 ? 0 : 1;

// 将画布坐标系移动到画布中央
canvas.translate(viewWidth / 2 - mHappy.happy_width / 2, viewHeight / 2 - mHappy.happy_height / 2);


// 移动x,y的坐标,第一个行的时候 index * imgW
// 第二行,去掉第一行的数列,归0。再开始叠加imgW的倍数
int left = row == 0 ? index * imgW : (index - 11) * imgW;
//距离top的位置,第一行0,第二行,1*imgH高。
int top = row * imgH;

// 指定绘制图片区域的22分之一
Rect src = new Rect(left, top, left + mHappy.happy_width, top + mHappy.happy_height);

// 上面translate已经移动到屏幕中央了,这里指定图片在屏幕上显示的区域,就以translate(0,0)位置绘制即在中央的位置
Rect dst = new Rect(0, 0, mHappy.happy_width, mHappy.happy_height);

// 绘制图片
canvas.drawBitmap(mHappy.happy, src, dst, null);
//计数+1
index++;

//满足一个循环结束
if (index == 22) {
index = 0;
}
}

效果预览图

可具体参考源码

时间紧迫,这里实现的效果比较粗糙。有很大效果偏差
可自行修改,源码链接