【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();
浙公网安备 33010602011771号