Android自定义View-下拉刷新控件
来源:互联网 发布:矩阵论杨明课后答案 编辑:程序博客网 时间:2024/05/16 02:18
Android自定义View-下拉刷新控件
下拉刷新是android开发过程中很常见的功能,github上面有许多下拉刷新的开源控件可以使用。但有时候这些开源控件不能完全符合我们的项目要求,这时就需要自己进行修改,这时候我们就需要了解下拉刷新的原理,才能自由的修改它的功能,因此我自己写了一个简单的下拉刷新控件,以了解其原理
下拉刷新原理
下拉刷新控件主要由两部分组成,内容部分,与下拉头部分
其主要流程:初始时将下拉头的位置设置到屏幕上方,当内容部分滑动到顶部时则根据手指滑动的y轴方向参数,将下拉头滑动到屏幕中,当滑动距离达到某一个值时则可以刷新,这时手指松开,则进入刷新状态,刷新完成之后回到初始位置
实现
大概原理已经清楚了,那我们就要开始实现一个下拉刷新控件了
先写一个下拉头的布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp"> <ImageView android:id="@+id/arrow" android:layout_width="wrap_content" android:layout_height="60dp" android:layout_centerInParent="true" android:src="@mipmap/arrow" /> <ProgressBar android:id="@+id/progress_bar" android:layout_width="30dip" android:layout_height="30dip" android:layout_centerInParent="true" android:visibility="gone" /></RelativeLayout>
然后新建一个RefreshableView的类,这个继承自LinearLayout,之所以继承LinearLayout是因为我们的下拉刷新控件是一个线性布局的ViewGroup
添加下拉头布局
public RefreshableView(Context context, AttributeSet attrs) { super(context, attrs); //添加下拉头布局 header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh , null , true); arrow = (ImageView)header.findViewById(R.id.arrow); progressBar = (ProgressBar)header.findViewById(R.id.progress_bar); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); setOrientation(VERTICAL); addView(header , 0); mScroller = new Scroller(context);}
在Layout时设置下拉头的初始位置
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(changed && !loadOnce){ //下拉头向上偏移,隐藏下拉头 hideHeaderHeight = -header.getHeight(); ((MarginLayoutParams)header.getLayoutParams()).topMargin = hideHeaderHeight; //设置ListView的触摸事件 listView = (ListView)getChildAt(1); listView.setOnTouchListener(this); loadOnce = true; }}
ListView滑动监听的onTouch方法
@Override public boolean onTouch(View view, MotionEvent motionEvent) { //判断ListView是否滑动到顶部 isAbleToPull(motionEvent); if(ableToPull){ switch (motionEvent.getAction()){ case MotionEvent.ACTION_DOWN: yDown = motionEvent.getRawY(); mLastY = motionEvent.getRawY(); break; case MotionEvent.ACTION_MOVE: //手指上滑不处理事件 if(motionEvent.getRawY() - yDown < 0 && getScrollY() == 0){ return false; } //小于最小滑动距离 if(motionEvent.getRawY() - yDown < touchSlop){ return false; } //手指是下滑并且下拉头完全隐藏执行下滑 if(motionEvent.getRawY() - yDown > 0 && getScrollY() >= 0){ //滑动距离大于下拉头高度松手可刷新 if(getScrollY() < hideHeaderHeight){ currentStatus = STATUS_RELEASE_TO_REFRESH; }else{ currentStatus = STATUS_PULL_TO_REFRESH; } scrollBy(0 , -(int)(motionEvent.getRawY() - mLastY)); } mLastY = motionEvent.getRawY(); break; case MotionEvent.ACTION_UP: default: if(currentStatus == STATUS_PULL_TO_REFRESH){ //隐藏下拉头 smoothScrollBy( 0 , -getScrollY()); } if(currentStatus == STATUS_RELEASE_TO_REFRESH){ //刷新 onRefresh(); } break; } // 时刻记得更新下拉头中的信息 if (currentStatus == STATUS_PULL_TO_REFRESH || currentStatus == STATUS_RELEASE_TO_REFRESH) { updateHeaderView(); // 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态 listView.setPressed(false); listView.setFocusable(false); listView.setFocusableInTouchMode(false); lastStatus = currentStatus; // 当前正处于下拉或释放状态,通过返回true屏蔽掉ListView的滚动事件 return true; } } return false; }
isAbleToPull方法判断ListView是否滑动到顶部
public void isAbleToPull(MotionEvent motionEvent){ View fristChiler = listView.getChildAt(0); if(fristChiler != null) { //如果ListView滑动到顶部可以下拉下拉头 if (listView.getFirstVisiblePosition() == 0 && fristChiler.getTop() == 0){ if(!ableToPull) { yDown = motionEvent.getRawY(); } ableToPull = true; }else{ ableToPull = false; } }else{ //ListView为空,也可以下拉下拉头 ableToPull = true; }}
rotateArrow方法根据当前状态旋转箭头
private void rotateArrow() { float pivotX = arrow.getWidth() / 2f; float pivotY = arrow.getHeight() / 2f; float fromDegrees = 0f; float toDegrees = 0f; if (currentStatus == STATUS_PULL_TO_REFRESH) { fromDegrees = 180f; toDegrees = 360f; } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) { fromDegrees = 0f; toDegrees = 180f; } RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY); animation.setDuration(100); animation.setFillAfter(true); arrow.startAnimation(animation); }
updateHeaderView根据当前状态更新下拉头的信息
private void updateHeaderView() { if (lastStatus != currentStatus) { if (currentStatus == STATUS_PULL_TO_REFRESH) { arrow.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); rotateArrow(); } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) { arrow.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); rotateArrow(); } else if (currentStatus == STATUS_REFRESHING) { progressBar.setVisibility(View.VISIBLE); arrow.clearAnimation(); arrow.setVisibility(View.GONE); } } }
这段代码实现平滑滑动
public void smoothScrollBy(int dx , int dy){ mScroller.startScroll( 0 , getScrollY() , 0 , dy , SCROLL_TIME); invalidate(); } @Override public void computeScroll() { if(mScroller.computeScrollOffset()){ scrollTo(mScroller.getCurrX() , mScroller.getCurrY()); postInvalidate(); } }
定义一个下拉刷新监听借口
public interface PullToRefreshListener { /** * 刷新时会去回调此方法,在方法内编写具体的刷新逻辑。注意此方法是在子线程中调用的, 你可以不必另开线程来进行耗时操作。 */ void onRefresh(); }
刷新完成方法,设置刷新事件监听器,刷新方法
//刷新结束后调用的方法 public void finishRefresh(){ currentStatus = STATUS_REFRESH_FINISHED; smoothScrollBy( 0 , -getScrollY()); } //设置刷新事件监听 public void setRefreshListener(PullToRefreshListener l){ this.mListener = l; } //刷新方法 public void onRefresh(){ smoothScrollBy( 0 , (-getScrollY()) + hideHeaderHeight); if(mListener != null){ mListener.onRefresh(); } }
控件已经写完了,接下来我们要看一下实现效果如果
在activity布局文件中加入如下布局
<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=".MainActivity" > <com.example.pulltorefreshtest.RefreshableView android:id="@+id/refreshable_view" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/list_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbars="none" > </ListView> </com.example.pulltorefreshtest.RefreshableView></RelativeLayout>
在MainActivity中设置下拉刷新事件
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); refreshableView = (RefreshableView) findViewById(R.id.refreshable_view); listView = (ListView) findViewById(R.id.list_view); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items); listView.setAdapter(adapter); refreshableView.setRefreshListener(new RefreshableView.PullToRefreshListener() { @Override public void onRefresh() { refreshableView.finishRefresh(); } }); }
源码地址:https://github.com/GameT/PullToRefreshDemo
0 0
- Android自定义View-下拉刷新控件
- Android自定义View实现下拉刷新控件
- Android自定义控件下拉刷新
- Android PullToRefreshView自定义下拉刷新控件
- Android自定义控件之仿美团下拉刷新
- Android自定义控件之仿美团下拉刷新
- Android自定义控件--下拉刷新的实现
- Android仿天猫下拉刷新自定义控件
- Android自定义控件之仿美团下拉刷新
- Android自定义控件下拉刷新实例代码
- Android(自定义控件)下拉刷新上拉加载,所有View通用.(直接拿来用)
- 自定义控件--下拉刷新
- 自定义控件-下拉刷新
- 自定义控件--下拉刷新
- 自定义下拉刷新控件
- 自定义控件:下拉刷新
- Android自定义控件实现下拉列表刷新,上拉刷新
- Android所有View通用下拉刷新上拉加载控件
- Jni开发所遇见的问题
- MyEclipse2015创建Maven Web聚合工程实现分层思想
- 蓝桥基础练习 杨辉三角形
- SEED实验:缓冲区溢出漏洞实验__山东大学网络攻防实验
- 队列的基本操作(顺序结构)C/C++
- Android自定义View-下拉刷新控件
- android handler与eventbus对比
- 狄利克雷过程(Dirichlet Process)
- android studio 2.2改变主题字体大小
- 以备查阅
- GsonFormat,Json实体类快速生产的插件
- SVN学习笔记
- 蓝桥基础练习 杨辉三角形
- 有一个已排好序的数组,要求输入一个数后,按原来的规律插入数组