【0059】自定义控件-2-完全自定义控件.(ViewGroup)-下拉刷新

1. 下拉刷新的效果及逻辑

【下拉刷新】下拉的时候出多加载出一条数据;

【上拉刷新】上拉的时候从网络加载更多的数据

【优点】这样做的好处是:可以节省内存,不至于一次加载很多数据道中内存崩溃;同样也可以节省流量,不至于一次加载几十万条数据之后流量耗干了;

  理解为:分批加载,循环联动;

2.listView增加头部按钮

2.1 头部按钮的效果

2.2 源码

 1 package com.itheima74.refreshlist;
 2 
 3 import java.util.ArrayList;
 4 
 5 import android.app.Activity;
 6 import android.os.Bundle;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 import android.view.Window;
10 import android.widget.BaseAdapter;
11 import android.widget.TextView;
12 
13 import com.itheima74.refreshlist.ui.RefreshListView;
14 import com.itheima74.refreshlist.ui.RefreshListView.OnRefreshListener;
15 
16 public class MainActivity extends Activity {
17 
18     private RefreshListView listview;
19     private ArrayList<String> listDatas;
20     private MyAdapter adapter;
21 
22     @Override
23     protected void onCreate(Bundle savedInstanceState) {
24         super.onCreate(savedInstanceState);
25         requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题
26         setContentView(R.layout.activity_main);
27 
28         listview = (RefreshListView) findViewById(R.id.listview);
29         
30         //在设置数据适配器之前执行添加方法
31         Button button = new Button(this);
32         button.setText("我是头部按钮");
33         listView.addHeaderView(button);
34         
35 
36         listDatas = new ArrayList<String>();
37         for (int i = 0; i < 30; i++) {
38             listDatas.add("这是一条ListView数据: " + i);
39         }
40         
41         // 设置数据适配器
42         
43         adapter = new MyAdapter();
44         listview.setAdapter(adapter);
45 
46         
47     }
48     
49     class MyAdapter extends BaseAdapter {
50 
51         @Override
52         public int getCount() {
53             return listDatas.size();
54         }
55         @Override
56         public View getView(int position, View convertView, ViewGroup parent) {
57             TextView textView = new TextView(parent.getContext());
58             textView.setTextSize(18f);
59             textView.setText(listDatas.get(position));
60             
61             return textView;
62         }
63         
64         @Override
65         public Object getItem(int position) {
66             return listDatas.get(position);
67         }
68 
69         @Override
70         public long getItemId(int position) {
71             return position;
72         }
73         
74     }
75 }

【注意】

 3.头布局的添加

3.1 头布局添加之后的效果

【说明】头部布局:在下拉之后会出现箭头、进度圈、文字、时间等等;

3.2 头部布局

【布局源码】

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="horizontal" >
 6 
 7     <!-- 使用帧布局,可以进行ImaeView箭头和进度圈的切换 -->
 8 
 9     <FrameLayout
10         android:layout_width="50dp"
11         android:layout_height="50dp"
12         android:layout_margin="5dp" >
13 
14         <!-- 箭头 -->
15 
16         <ImageView
17             android:id="@+id/iv_arrow"
18             android:layout_width="wrap_content"
19             android:layout_height="wrap_content"
20             android:layout_gravity="center"
21             android:src="@drawable/common_listview_headview_red_arrow" />
22         <!-- 进度圈 -->
23 
24         <ProgressBar
25             android:id="@+id/pb"
26             android:layout_width="match_parent"
27             android:layout_height="match_parent"
28             android:indeterminateDrawable="@drawable/shape_progress"
29             android:visibility="invisible" />
30     </FrameLayout>
31     <!-- 右侧文字 -->
32 
33     <LinearLayout
34         android:layout_width="match_parent"
35         android:layout_height="wrap_content"
36         android:layout_margin="5dp"
37         android:gravity="center_horizontal"
38         android:orientation="vertical" >
39 
40         <TextView
41             android:id="@+id/tv_title"
42             android:layout_width="wrap_content"
43             android:layout_height="wrap_content"
44             android:layout_marginTop="5dp"
45             android:text="下拉刷新"
46             android:textColor="#F00"
47             android:textSize="18sp" />
48 
49         <TextView
50             android:id="@+id/tv_desc_last_refresh"
51             android:layout_width="wrap_content"
52             android:layout_height="wrap_content"
53             android:layout_marginTop="5dp"
54             android:singleLine="true"
55             android:text="最后刷新时间: 2015-10-11 09:20:35"
56             android:textColor="#666"
57             android:textSize="14sp" />
58     </LinearLayout>
59 
60 </LinearLayout>

 

【源码】

 1 package com.itheima74.refreshlist.ui;
 2 
 3 import java.text.SimpleDateFormat;
 4 
 5 import android.content.Context;
 6 import android.util.AttributeSet;
 7 import android.view.MotionEvent;
 8 import android.view.View;
 9 import android.view.animation.Animation;
10 import android.view.animation.RotateAnimation;
11 import android.widget.AbsListView;
12 import android.widget.AbsListView.OnScrollListener;
13 import android.widget.ListView;
14 import android.widget.ProgressBar;
15 import android.widget.TextView;
16 
17 import com.itheima74.refreshlist.R;
18 
19 /**
20  * 包含下拉刷新功能的ListView
21  * @author poplar
22  *
23  */
24 public class RefreshListView extends ListView implements OnScrollListener{
25 
26     private View mHeaderView; // 头布局
27     private float downY; // 按下的y坐标
28     private float moveY; // 移动后的y坐标
29     private int mHeaderViewHeight; // 头布局高度
30     public static final int PULL_TO_REFRESH = 0;// 下拉刷新
31     public static final int RELEASE_REFRESH = 1;// 释放刷新
32     public static final int REFRESHING = 2; // 刷新中
33     private int currentState = PULL_TO_REFRESH; // 当前刷新模式
34     private RotateAnimation rotateUpAnim; // 箭头向上动画
35     private RotateAnimation rotateDownAnim; // 箭头向下动画
36     private View mArrowView;        // 箭头布局
37     private TextView mTitleText;    // 头布局标题
38     private ProgressBar pb;            // 进度指示器
39     private TextView mLastRefreshTime; // 最后刷新时间
40     private OnRefreshListener mListener; // 刷新监听
41     private View mFooterView;        // 脚布局
42     private int mFooterViewHeight;    // 脚布局高度
43     private boolean isLoadingMore; // 是否正在加载更多
44 
45     public RefreshListView(Context context) {
46         super(context);
47         init();
48     }
49 
50     public RefreshListView(Context context, AttributeSet attrs) {
51         super(context, attrs);
52         init();
53     }
54 
55     public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
56         super(context, attrs, defStyle);
57         init();
58     }
59 
60     /**
61      * 初始化头布局, 脚布局
62      * 滚动监听
63      */
64     private void init() {
65         initHeaderView();        
66         setOnScrollListener(this);
67     }
68 
69     /**
70      * 初始化头布局
71      */
72     private void initHeaderView() {
73         //转化布局为View
74         mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
75         mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
76         
77         // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
78         addHeaderView(mHeaderView);
79     }

【activity_main.xml源码】

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     tools:context=".MainActivity" >
 6     //增加已经添加好的布局
 7     <com.itheima74.refreshlist.ui.RefreshListView
 8         android:id="@+id/listview"
 9         android:layout_width="match_parent"
10         android:layout_height="match_parent" >
11     </com.itheima74.refreshlist.ui.RefreshListView>
12 
13 </RelativeLayout>

2.自定义Progress

2.1 自定义形状shape

【说明】系统中的进度圈的颜色是灰色的,现在我们自定义一个;

 

 

【扫描状】

 

【放射状】

 

 

【线性状】

 

 

【进度条的扫描状】

 

 

【实际使用的进度圈的颜色】

 

 

2.2 圆环的描绘

 

2.3 xml中配置旋转动画

 

 【最终的进度圈的效果】

3.隐藏头布局

3.1 设置进度圈不显示

【说明】在一般的情况下的进度圈是不显示的,只有在下拉刷新进行数据加载的时候才会显示;

 

3.2 隐藏头布局的效果

【原理】修改padding值为负值;

 

       

【高度的测量】真实的高度只有在resume的生命周期中才会表示出来;

       

【隐藏头部】

 

 

4.触摸事件的处理

【说明】在不断下拉刷新的时候更改padding值;

【注意】

 

【原理】根据move的值不停的修改padding值;

 

【具体移动的细节】将downx和movex移动的值相减之后的值加给原来设置的padding值;
例如移动30dp的效果如下图:

 



【源码】

【实际的效果】

【优化1】如果是向上拖动listView则变化量则成为负值,不应再生效;

4. 头部按钮的文字和图标的改变
4.1【效果】头部按钮的文字由下拉刷新变为释放刷新

4.2 状态的判断
【源码】

【注意】动画不能在变化值一改变就进行刷新和文字改变;只有在确定了改变值之后再进行改变;
【优化之后的效果】

4.3 状态更新
【需要完成两件事情】
【1】箭头方向的逆时针动画转变
【2】文字的改变

4.3.1 箭头的方向和文字的改变

 

 

 


4.3.2 松手之后的状态的改变

【正在刷新的状态】需要将头部完全显示出来

【方法1】根据位置判断

【方法2】根据状态判断

【正在刷新的动画】

4.3.3出现的问题
【说明】正在刷新中的时候不允许在上拉或者下拉刷新了

【增加源码的判断】

5.数据加载的模拟
【说明】监听:告诉外部的调用者,现在执行的动作是正在刷新,需要加载内容;

 


【原理分析】中间的信息的传递者就是:mListener;

 

 


6.加载数据之后需要隐藏头部的按钮


【现在的效果】

7.上拉加载更多的功能
7.1 效果
【动作】在上拉之后、必须拉到底部最后、并且松手之后才加载更多,作出效果;

7.2 初始化脚布局


【脚布局的xml源码】

【脚布局的最终布局源码】

【将空间隐藏】

7.3 设置监听
【作用】在监听在滑动过程中要完成需要完成的动作;

 

 


【滑动过程中出现的状体】

【动作】在上拉之后、必须拉到底部最后、并且松手之后才加载更多,作出效果;
【源码】

7.3 上拉加载更多增加内容
【说明】使用街接口,在listViewz中的listener中增加上拉加载更多的内容


【BUG】如果是已经处于正在加载更多的状态,在上拉就不应该在出现加载更多的功能,修改如下;

【源码修改】如果处于正在加载的状态,就直接返回;

【模拟数据的加载】

【增加判断的状态】

【收尾工作】

【上拉刷新的效果】

8. 回调功能的再解释
【说明】相当于一个手机:OnRefreshListener,可以拨打两个号码:onRefresh()和onLoadMore();

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2018-01-08 22:00  OzTaking  阅读(297)  评论(0)    收藏  举报