标签:
效果
开始用Android Studio写了,还有挺多不明白这IDE用法的地方。。。。蛋疼
主要思路
1. 添加了自定义的头布局
2. 默认让头布局隐藏setPadding.设置 -自身的高度
3. ListView下拉的时候, 修改paddingTop, 让头布局显示出来
4. 触摸动态修改头布局, 根据paddingTop.
- paddingTop = 0 完全显示
- paddingTop < 不完全显示 -64(自身高度)完全隐藏
- paddingTop > 0 顶部空白
5. 松手之后根据当前的paddingTop决定是否执行刷新
- paddingTop < 0 不完全显示, 恢复
- paddingTop >= 0 完全显示, 执行正在刷新...
其实就是通过设置padding控制头布局和脚步局的位置
一:先写头布局和脚步局
layout_header
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <FrameLayout android:layout_margin="5dp" android:layout_width="50dp" android:layout_height="50dp" > <ImageView android:id="@+id/iv_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/common_listview_headview_red_arrow" /> <ProgressBar android:id="@+id/pb" android:layout_width="match_parent" android:layout_height="match_parent" android:indeterminateDrawable="@drawable/shape_progress" android:visibility="invisible" /> </FrameLayout> <LinearLayout android:layout_margin="5dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical" > <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="下拉刷新" android:textColor="#F00" android:textSize="18sp" /> <TextView android:id="@+id/tv_desc_last_refresh" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:singleLine="true" android:text="最后刷新时间: 2015-10-11 09:20:35" android:textColor="#666" android:textSize="14sp" /> </LinearLayout> </LinearLayout>
进度条的 shape_progress.xml,效果是旋转的圆环
——<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="-360" > <!-- android:innerRadius="20dp" --> <!-- android:thickness="5dp" --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:innerRadiusRatio="2.5" android:shape="ring" android:thicknessRatio="10" android:useLevel="false" > <gradient android:centerColor="#44FF0000" android:endColor="#00000000" android:startColor="#FF0000" android:type="sweep" /> </shape> </rotate>
layout_footer
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" > <ProgressBar android:layout_margin="5dp" android:layout_width="50dp" android:layout_height="50dp" android:indeterminateDrawable="@drawable/shape_progress" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加载更多..." android:textColor="#F00" android:layout_marginLeft="15dp" android:textSize="18sp" /> </LinearLayout>
二。接下来是自定义的RefreshListView
import java.text.SimpleDateFormat; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.itheima74.refreshlist.R; /** * 包含下拉刷新功能的ListView * @author * */ public class RefreshListView extends ListView implements OnScrollListener{ private View mHeaderView; // 头布局 private float downY; // 按下的y坐标 private float moveY; // 移动后的y坐标 private int mHeaderViewHeight; // 头布局高度 public static final int PULL_TO_REFRESH = 0;// 下拉刷新 public static final int RELEASE_REFRESH = 1;// 释放刷新 public static final int REFRESHING = 2; // 刷新中 private int currentState = PULL_TO_REFRESH; // 当前刷新模式 private RotateAnimation rotateUpAnim; // 箭头向上动画 private RotateAnimation rotateDownAnim; // 箭头向下动画 private View mArrowView; // 箭头布局 private TextView mTitleText; // 头布局标题 private ProgressBar pb; // 进度指示器 private TextView mLastRefreshTime; // 最后刷新时间 private OnRefreshListener mListener; // 刷新监听 private View mFooterView; // 脚布局 private int mFooterViewHeight; // 脚布局高度 private boolean isLoadingMore; // 是否正在加载更多 public RefreshListView(Context context) { super(context); init(); } public RefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } /** * 初始化头布局, 脚布局 * 滚动监听 */ private void init() { initHeaderView(); initAnimation(); initFooterView(); setOnScrollListener(this); } /** * 初始化脚布局 */ private void initFooterView() { mFooterView = View.inflate(getContext(), R.layout.layout_footer_list, null); mFooterView.measure(0, 0); mFooterViewHeight = mFooterView.getMeasuredHeight(); // 隐藏脚布局 mFooterView.setPadding(0, -mFooterViewHeight, 0, 0); addFooterView(mFooterView); } /** * 初始化头布局的动画 */ private void initAnimation() { // 向上转, 围绕着自己的中心, 逆时针旋转0 -> -180. rotateUpAnim = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateUpAnim.setDuration(300); rotateUpAnim.setFillAfter(true); // 动画停留在结束位置 // 向下转, 围绕着自己的中心, 逆时针旋转 -180 -> -360 rotateDownAnim = new RotateAnimation(-180f, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateDownAnim.setDuration(300); rotateDownAnim.setFillAfter(true); // 动画停留在结束位置 } /** * 初始化头布局 */ private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null); mArrowView = mHeaderView.findViewById(R.id.iv_arrow); pb = (ProgressBar) mHeaderView.findViewById(R.id.pb); mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title); mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh); // 提前手动测量宽高 mHeaderView.measure(0, 0);// 按照设置的规则测量 mHeaderViewHeight = mHeaderView.getMeasuredHeight(); System.out.println(" measuredHeight: " + mHeaderViewHeight); // 设置内边距, 可以隐藏当前控件 , -自身高度 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); // 在设置数据适配器之前执行添加 头布局/脚布局 的方法. addHeaderView(mHeaderView); } @Override public boolean onTouchEvent(MotionEvent ev) { // 判断滑动距离, 给Header设置paddingTop switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = ev.getY(); System.out.println("downY: " + downY); break; case MotionEvent.ACTION_MOVE: moveY = ev.getY(); System.out.println("moveY: " + moveY); // 如果是正在刷新中, 就执行父类的处理 if(currentState == REFRESHING){ return super.onTouchEvent(ev); } float offset = moveY - downY; // 移动的偏移量 // 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部 if(offset > 0 && getFirstVisiblePosition() == 0){ // int paddingTop = -自身高度 + 偏移量 int paddingTop = (int) (- mHeaderViewHeight + offset); mHeaderView.setPadding(0, paddingTop, 0, 0); if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示 System.out.println("切换成释放刷新模式: " + paddingTop); // 切换成释放刷新模式 currentState = RELEASE_REFRESH; updateHeader(); // 根据最新的状态值更新头布局内容 }else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示 System.out.println("切换成下拉刷新模式: " + paddingTop); // 切换成下拉刷新模式 currentState = PULL_TO_REFRESH; updateHeader(); // 根据最新的状态值更新头布局内容 } return true; // 当前事件被我们处理并消费 } break; case MotionEvent.ACTION_UP: // 根据刚刚设置状态 if(currentState == PULL_TO_REFRESH){ // - paddingTop < 0 不完全显示, 恢复 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); }else if(currentState == RELEASE_REFRESH){ // - paddingTop >= 0 完全显示, 执行正在刷新... mHeaderView.setPadding(0, 0, 0, 0); currentState = REFRESHING; updateHeader(); } break; default: break; } return super.onTouchEvent(ev); } /** * 根据状态更新头布局内容 */ private void updateHeader() { switch (currentState) { case PULL_TO_REFRESH: // 切换回下拉刷新 // 做动画, 改标题 mArrowView.startAnimation(rotateDownAnim); mTitleText.setText("下拉刷新"); break; case RELEASE_REFRESH: // 切换成释放刷新 // 做动画, 改标题 mArrowView.startAnimation(rotateUpAnim); mTitleText.setText("释放刷新"); break; case REFRESHING: // 刷新中... mArrowView.clearAnimation(); mArrowView.setVisibility(View.INVISIBLE); pb.setVisibility(View.VISIBLE); mTitleText.setText("正在刷新中..."); if(mListener != null){ mListener.onRefresh(); // 通知调用者, 让其到网络加载更多数据. } break; default: break; } } /** * 刷新结束, 恢复界面效果 */ public void onRefreshComplete() { if(isLoadingMore){ // 加载更多 mFooterView.setPadding(0, -mFooterViewHeight, 0, 0); isLoadingMore = false; }else { // 下拉刷新 currentState = PULL_TO_REFRESH; mTitleText.setText("下拉刷新"); // 切换文本 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏头布局 pb.setVisibility(View.INVISIBLE); mArrowView.setVisibility(View.VISIBLE); String time = getTime(); mLastRefreshTime.setText("最后刷新时间: " + time); } } private String getTime() { long currentTimeMillis = System.currentTimeMillis(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(currentTimeMillis); } public interface OnRefreshListener{ void onRefresh(); // 下拉刷新 void onLoadMore();// 加载更多 } public void setRefreshListener(OnRefreshListener mListener) { this.mListener = mListener; } // public static int SCROLL_STATE_IDLE = 0; // 空闲 // public static int SCROLL_STATE_TOUCH_SCROLL = 1; // 触摸滑动 // public static int SCROLL_STATE_FLING = 2; // 滑翔 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 状态更新的时候 System.out.println("scrollState: " + scrollState); if(isLoadingMore){ return; // 已经在加载更多.返回 } // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多 if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){ isLoadingMore = true; System.out.println("scrollState: 开始加载更多"); mFooterView.setPadding(0, 0, 0, 0); setSelection(getCount()); // 跳转到最后一条, 使其显示出加载更多. if(mListener != null){ mListener.onLoadMore(); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 滑动过程 } }
思路是,先设置padding让头隐藏,然后根据 onTouchEvent的动作,判断出头布局应该处于的状态。脚步局也是如此。
这里,定义了接口
public interface OnRefreshListener{
void onRefresh(); // 下拉刷新
void onLoadMore();// 加载更多
}
这些方法是为了让调用者使用
@Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 状态更新的时候 System.out.println("scrollState: " + scrollState); if(isLoadingMore){ return; // 已经在加载更多.返回 } // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多 if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){ isLoadingMore = true; System.out.println("scrollState: 开始加载更多"); mFooterView.setPadding(0, 0, 0, 0); setSelection(getCount()); // 跳转到最后一条, 使其显示出加载更多. if(mListener != null){ mListener.onLoadMore(); } } }
这里我们看到有个 if(mListener != null){ mListener.onLoadMore(); }
但是onLoadMore()方法到底在复写的呢,我们可以看下mainactivity
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.BaseAdapter; import android.widget.TextView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private RefreshListView listview; private ArrayList<String> listDatas; private MyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listDatas = new ArrayList<String>(); for (int i = 0; i < 30; i++) { listDatas.add("这是一条ListView数据: " + i); } listview = (RefreshListView) findViewById(R.id.listview); listview.setRefreshListener(new RefreshListView.OnRefreshListener() { @Override public void onRefresh() { new Thread(){ public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } listDatas.add(0,"我是下拉刷新出来的数据!"); runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); listview.onRefreshComplete(); } }); }; }.start(); } @Override public void onLoadMore() { new Thread(){ public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } listDatas.add("我是加载更多出来的数据!1"); listDatas.add("我是加载更多出来的数据!2"); listDatas.add("我是加载更多出来的数据!3"); runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); listview.onRefreshComplete(); } }); }; }.start(); } }); adapter = new MyAdapter(); listview.setAdapter(adapter); } class MyAdapter extends BaseAdapter { @Override public int getCount() { return listDatas.size(); } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView textView = new TextView(parent.getContext()); textView.setTextSize(18f); textView.setText(listDatas.get(position)); return textView; } @Override public Object getItem(int position) { return listDatas.get(position); } @Override public long getItemId(int position) { return position; } } }
在这里,通过自定义Listview的setRefreshListener的方法,传入一个匿名类进行复写了接口要实现的方法。
这是一种回调机智。
简单的说像是,listView内部给定了一个接口,像是规定了一个规范,然后使用listview的类,如果想要实现某种功能,必须实现这个规范中的方法。
像是我们考试,老师给试卷上印好了姓名和学号填空栏目,我们使用者每个人在栏目里写上自己的姓名和学号,再交试卷
比如,我们button的点击事件
btn1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Toast tst = Toast.makeText(TestButtonActivity.this, "111111111", Toast.LENGTH_SHORT); tst.show(); } });
老师=谷歌开发人员,写button类的
试卷=button
姓名学号栏目=接口OnClickListener中onclick方法
我们姓名学号=我们自己复写的onclick具体执行什么
标签:
原文地址:http://www.cnblogs.com/xurui1995/p/5745560.html