Android自定义控件实现下拉刷新效果

app开发中下拉刷新是最常接触到的一个功能,也有很多开源的框架,封装的非常棒。前段时间了解了一下ViewDragHelper,遂用它实现了下拉刷新的功能。

大概和我之前的ViewDragHelper之拖动加载(类似淘宝)这篇代码类似。只是做了相关改动。具体的可以看一下那篇博文了解一下用到的ViewDragHelper的一些知识点。该界面主要是一个LinearLayout,上面的下拉刷新是一个textview(用TV代替),当然这个可以定制,在此只是用一个textview代替,实现简单的功能,下面是一个listview(用LV代替),当然listview也是可以定制的,可以使gridview或者其他你想要的都可以,在此也是只用Listview代替。大概的讲讲吧:

首先,在onLayout中将TV置于屏幕上方,将LV充满屏幕;

上图中蓝色部分是整个手机的屏幕,红色部分是下拉提示TV。TV是置于屏幕之外的是不显示的。

@Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
  if (pullText.getTop() == 0) {
   viewHeight = pullText.getMeasuredHeight();
   pullText.layout(l, 0, r, b);
   myList.layout(l, 0, r, b);
   pullText.offsetTopAndBottom(-viewHeight);
  } else {
   pullText.layout(l, pullText.getTop(), r, pullText.getBottom());
   myList.layout(l, myList.getTop(), r, myList.getBottom());
  }
 }

上面的代码段中,pullText即是TV,myList是LV。这样在下拉LV的时候,TV就会跟着往下走,所以就会出现在屏幕中实现我们想要的效果。

 /**
  * 这是拖拽效果的主要逻辑
  */
 private class DragHelperCallback extends ViewDragHelper.Callback { 

  @Override
  public void onViewPositionChanged(View changedView, int left, int top,
    int dx, int dy) {
   int childIndex = 1;
   if (changedView == myList) {
    childIndex = 2;
   }
   onViewPosChanged(childIndex, top);
  } 

  @Override
  public boolean tryCaptureView(View child, int pointerId) {
   return true;
  } 

  @Override
  public int getViewVerticalDragRange(View child) {
   return 1;
  } 

  @Override
  public void onViewReleased(View releasedChild, float xvel, float yvel) {
   refreshOrNot(releasedChild, yvel);
  } 

  @Override
  public int clampViewPositionVertical(View child, int top, int dy) {
   int finalTop = top;
   if (child == pullText) {
    if (top > 0) {
     finalTop = 0;
    }
   } else if (child == myList) {
    if (top < 0) {
     finalTop = 0;
    }
    if(top >= viewHeight){
     pullText.setText("松开刷新");
    }else{
     pullText.setText("下拉刷新");
    }
   }
   return child.getTop() + (finalTop - child.getTop()) / 2;
  }
 }

上面的代码段中,主要是在clampViewPositionVertical中判断滑动的位置,作用的子view。其他就不多说了,大致和之前的博客相同。主要说说onViewReleased吧。在此函数中是在用户手势抬起时响应的,所以我们在此实现下拉后的刷新。我们先定义一个接口,以便在刷新的时候调用。

public interface pulltorefreshNotifier {
  public void onPull();
 }
public void setpulltorefreshNotifier(pulltorefreshNotifier pullNotifier) {
  this.pullNotifier = pullNotifier;
 }
private void refreshOrNot(View releasedChild, float yvel) {
  int finalTop = 0;
  if (releasedChild == pullText) {
   // 拖动第一个view松手
   if (yvel < -50) {
    finalTop = 0;
   } else {
    finalTop = viewHeight;
   }
  } else {
   // 拖动第二个view松手
   if (yvel > viewHeight - 5 || releasedChild.getTop() >= viewHeight) {
    finalTop = viewHeight;
    if (null != pullNotifier) {
     pullNotifier.onPull();
    }
    pullText.setText("正在刷新");
   }
  } 

  if (VDH.smoothSlideViewTo(myList, 0, finalTop)) {
   ViewCompat.postInvalidateOnAnimation(this);
  }
 }

拖动第二个view时,也就是LV时,我们判断一下是否需要刷新,需要刷新则执行onPull();
然后我们来看一下主要的Activity:

package com.maxi.pulltorefreshtest; 

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message; 

import com.maxi.pulltorefreshtest.adapter.ProjectAdapter;
import com.maxi.pulltorefreshtest.widget.MyListView;
import com.maxi.pulltorefreshtest.widget.PullToRefreshGroup;
import com.maxi.pulltorefreshtest.widget.PullToRefreshGroup.pulltorefreshNotifier; 

public class MainActivity extends Activity {
 private PullToRefreshGroup pullListgroup;
 private boolean isDown = false;
 private MyListView myList;
 private ProjectAdapter pa;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  findView();
  init();
 } 

 private void findView() {
  pullListgroup = (PullToRefreshGroup) findViewById(R.id.pulltorefresh);
  myList = pullListgroup.returnMylist();
 } 

 private void init() {
  pulltorefreshNotifier pullNotifier = new pulltorefreshNotifier() {
   @Override
   public void onPull() {
    // TODO Auto-generated method stub
    downLoad();
   }
  };
  pullListgroup.setpulltorefreshNotifier(pullNotifier);
  pa = new ProjectAdapter(this);
  myList.setAdapter(pa);
  pa.notifyDataSetChanged();
 } 

 private void downLoad() {
  if (!isDown) {
   isDown = true;
   new Thread(new Runnable() { 

    @Override
    public void run() {
     // TODO Auto-generated method stub
     try {
      Thread.sleep(2000);
      handler.sendEmptyMessage(1);
     } catch (InterruptedException e) {
//      TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }).start();
  }
 } 

 @SuppressLint("HandlerLeak")
 private Handler handler = new Handler() { 

  @Override
  public void handleMessage(Message msg) {
   // TODO Auto-generated method stub
   super.handleMessage(msg);
   switch (msg.what) {
   case 1:
    pullListgroup.refreshComplete();
    isDown = false;
    break;
   default:
    break;
   }
  } 

 };
}

我们在他刷新的时候执行downLoad();刷新数据。为了达到效果可以看出我让线程暂停2s。然后调用refreshComplete();

public void refreshComplete() {
 if (VDH.smoothSlideViewTo(myList, 0, 0)) {
  ViewCompat.postInvalidateOnAnimation(this);
 }
}

实现刷新好后让TV继续返回屏幕上方。

上段代码中我们发现MyListView是重写的ListView,主要是处理手势事件的。

package com.maxi.pulltorefreshtest.widget; 

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView; 

public class MyListView extends ListView {
 boolean allowDragBottom = true;
 float downY = 0;
 boolean needConsumeTouch = true;
 public MyListView(Context context){
  super(context);
 }
 public MyListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 } 

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  if (ev.getAction() == MotionEvent.ACTION_DOWN) {
   downY = ev.getRawY();
   needConsumeTouch = true;
   if (getMyScrollY() == 0) {
    allowDragBottom = true;
   } else {
    allowDragBottom = false;
   }
  } else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
   if (!needConsumeTouch) {
    getParent().requestDisallowInterceptTouchEvent(false);
    return false;
   } else if (allowDragBottom) {
    if (downY - ev.getRawY() < -2) {
     needConsumeTouch = false;
     getParent().requestDisallowInterceptTouchEvent(false);
     return false;
    }
   }
  }
  getParent().requestDisallowInterceptTouchEvent(needConsumeTouch);
  return super.dispatchTouchEvent(ev);
 } 

 public int getMyScrollY() {
  View c = getChildAt(0);
  if (c == null) {
   return 0;
  }
  int firstVisiblePosition = getFirstVisiblePosition();
  int top = c.getTop();
  return -top + firstVisiblePosition * c.getHeight();
 }
}

ok。先这样吧。像上拉加载更多,我感觉也可以这么实现。有时间试试吧,大家有时间也可以动动手试试。

好吧。大致就这些,有疑问或建议请留言,共同进步,谢谢!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android PullToRefreshLayout下拉刷新控件的终结者

    说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能.有一个叫XListView的,我看别人用过,没看过是咋实现的,看这名字估计是继承自ListView修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了.看了QQ2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果: 不错吧?嗯,是的.一看就知道实现方式不一样.咱们今天就来实现一个下拉刷新控件.由于有时候不仅仅是ListView需要下拉刷新,ExpandableListView和GridView也有这个需求,

  • android开发教程之实现listview下拉刷新和上拉刷新效果

    复制代码 代码如下: public class PullToLoadListView extends ListView implements OnScrollListener { private static final String TAG = PullToLoadListView.class.getSimpleName(); private static final int STATE_NON = 0; private static final int STATE_PULL_TO_REFRE

  • Android官方下拉刷新控件SwipeRefreshLayout使用详解

    可能开发安卓的人大多数都用过很多下拉刷新的开源组件,但是今天用了官方v4支持包的SwipeRefreshLayout觉得效果也蛮不错的,特拿出来分享. 简介: SwipeRefreshLayout组件只接受一个子组件:即需要刷新的那个组件.它使用一个侦听机制来通知拥有该组件的监听器有刷新事件发生,换句话说我们的Activity必须实现通知的接口.该Activity负责处理事件刷新和刷新相应的视图.一旦监听者接收到该事件,就决定了刷新过程中应处理的地方.如果要展示一个"刷新动画",它必须

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android实现上拉加载更多以及下拉刷新功能(ListView)

    首先为大家介绍Andorid5.0原生下拉刷新简单实现. 先上效果图: 相对于上一个19.1.0版本中的横条效果好看了很多.使用起来也很简单. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" and

  • Android下拉刷新上拉加载控件(适用于所有View)

    前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

  • Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能(附源码)

    最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理想.有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到.因此我也是放弃了在网上找现成代码的想法,自己花功夫编写了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下.相信在阅读完本篇文章之后,大家都可以在自己的项目中一分钟引入下拉刷新功能. 首先讲一下实现原理.这里我们将采取的方案是使用组合View的方式,先自定义一个布局

  • Android下拉刷新ListView——RTPullListView(demo)

    下拉刷新在越来越多的App中使用,已经形成一种默认的用户习惯,遇到列表显示的内容时,用户已经开始习惯性的拉拉.在交互习惯上已经形成定性.之前在我的文章<IOS学习笔记34-EGOTableViewPullRefresh实现下拉刷新>中介绍过如何在IOS上实现下拉刷新的功能.今天主要介绍下在Android上实现下拉刷新的Demo,下拉控件参考自Github上开源项目PullToRefresh,并做简单修改.最终效果如下:                         工程结构如下: 使用过程中

  • Android RecyclerView实现下拉刷新和上拉加载

    RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载. 而在下面我们将用两种方式

  • android下拉刷新ListView的介绍和实现代码

    大致上,我们发现,下拉刷新的列表和一般列表的区别是,当滚动条在顶端的时候,再往下拉动就会把整个列表拉下来,显示出松开刷新的提示.由此可以看出,在构建这个下拉刷新的组件的时候,只用继承ListView,然后重写onTouchEvent就能实现.还有就是要能在xml布局文件中引用,还需要一个参数为Context,AttributeSet的构造函数. 表面上的功能大概就这些了.另一方面,刷新的行为似乎还没有定义,在刷新前做什么,刷新时要做什么,刷新完成后要做什么,这些行为写入一个接口中,然后让组件去实

随机推荐