欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-12-10 19:58:10
app开发中下拉刷新是最常接触到的一个功能,也有很多开源的框架,封装的非常棒。前段时间了解了一下viewdraghelper,遂用它实现了下拉刷新的功能。 大概和我之...

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

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

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

首先,在onlayout中将tv置于屏幕上方,将lv充满屏幕;

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

上图中蓝色部分是整个手机的屏幕,红色部分是下拉提示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。先这样吧。像上拉加载更多,我感觉也可以这么实现。有时间试试吧,大家有时间也可以动动手试试。

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

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