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

Android自定义StickinessView粘性滑动效果

程序员文章站 2023-12-09 18:05:39
design包的出现,android界面发生了巨大变化,各种滑动配合的效果,下面我就粘性滑动中的一种进行自定义,效果图如下: 大家看到效果了,这里我是继承了...

design包的出现,android界面发生了巨大变化,各种滑动配合的效果,下面我就粘性滑动中的一种进行自定义,效果图如下:

Android自定义StickinessView粘性滑动效果

大家看到效果了,这里我是继承了linerlayout,方便一点,若果是viewgroup的话,也就复杂一点点。这里分为三部分:

1.head1,顶部可移动的layout。
2.head2,固定的头部,不会滑动除屏幕外。
3.可滑动的layout(这里只可以是listview,不过也可以是任何可滑动的view,只要给出head可滑动的时机即可)

本stickinessview的难点在于,解决滑动冲突和事件的拦截处理,接下来我一一道来。

一、首先,要确定headlayout什么时候可以拦截事件,那么就要确定listview到达顶部和底部的时机。

 @override
 public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {
  view v = mlistview.getchildat(0);
  //当firstitem的top为0的时候就认为已经到达listview的顶部了
  if (mlistview.getchildcount() > 0 && firstvisibleitem == 0) {
   //滑动到顶部
   if (v.gettop() == 0) {
    //滑动到顶部了
    islistviewtop = true;
   } else {
    islistviewbottom = false;
   }
  }else if (mlistview.getchildcount()>0&&firstvisibleitem+visibleitemcount==totalitemcount){
   final view bottomchildview = mlistview.getchildat(mlistview.getchildcount()-1);
//当最后一个itemview的bottom>=listview的高度的时候,那么就认为到达底部了
   if    (mlistview.getheight()>=bottomchildview.getbottom()){
    islistviewbottom = true;
   }else {
    islistviewbottom = false;
   }
  }else {
   islistviewbottom = false;
   islistviewtop = false;
  }

原因很简单,因为view的gettop和getbottom方法是相对父容器的位置,熟悉layout方法的,想必就会很明白了。

二、知道了headview拦截事件的时机,我们就要搞清楚在此基础之上,我们到底啥时候拦击点击事件,进行滑动。

 @override
 public boolean onintercepttouchevent(motionevent ev) {
  switch (ev.getaction()) {
   case motionevent.action_down:
    touchy = ev.getrawy();
    isintercept = false;
    break;
   case motionevent.action_move:
    float distant = ev.getrawy() - touchy;
    if (islistviewtop) {
     switch (mheadposition) {
      case top:
       if (distant > 0) isintercept = true;
       break;
      case center:
       isintercept = true;
       break;
     }
    }
    if (islistviewbottom){
     switch (mheadposition) {
      case center:
       isintercept = true;
       break;
      case bottom:
       if (distant < 0) isintercept = true;
       break;
     }
    }

    break;
   case motionevent.action_up:
    isintercept = true;
    break;
  }
  return isintercept;
 }

跟大家讲解一下onintercepttouchevent(motionevent ev),这个方法会最先调用,当一个事件序列拦截一次后,那么这个事件的后续事件动作就不会再调用该方法,也就是说,当该viewgroup决定拦截某个事件后,那么它注定要消费后续的事件动作。这里贴出headview的位置状态

public static final int top = 0;//收缩状态
public static final int center = 1;//中间状态
public static final int bottom = 2;//展开状态

关于细节,想必大家画个图就可以知道了,注意一点:在拦截事件序列的时候,一般action_down事件不可以被拦截,因为拦截的话,没得意义了,后续事件就无法控制了,不可能继续往childview传递事件序列。

三、移动headview。

@override
public boolean ontouchevent(motionevent event) {
 switch (event.getaction()) {
  case motionevent.action_down:
   //获取不到的
   break;
  case motionevent.action_move:
   int distant = (int) (touchy - event.getrawy());
   if (getscrolly() + distant-1 < maxy && getscrolly() + distant > 0) {
    scrollto(0, getscrolly() + distant);
   }
   break;
  case motionevent.action_up:
   if (getscrolly() == 0) mheadposition = bottom;
   if (getscrolly() == maxy) mheadposition = top;
   if (getscrolly() > 0 && getscrolly() < maxy) mheadposition = center;
   if (getscrolly() > maxy / 2) {
    mscroll.startscroll(0, getscrolly(), 0, maxy-getscrolly(),100);
    invalidate();
    mheadposition = top;
   }
   if (getscrolly() < maxy / 2) {
    mscroll.startscroll(0, getscrolly(),0,-getscrolly(),100);
    invalidate();
    mheadposition = bottom;
   }
   break;
 }
 return super.ontouchevent(event);
}

这里为了使得滑动跟家顺畅我使用了scroller这个类,该类是专门处理弹性滑动的工具类,先初始化构造器,在调用startscroll()方法(其中四个参数:滑动的x,滑动的y,滑动x的偏移量,滑动y的偏移量),然后刷新视图,最后重写computescroll()方法,

@override
public void computescroll() {
 super.computescroll();
 if (mscroll.computescrolloffset()){
  scrollto(mscroll.getcurrx(),mscroll.getcurry());
  postinvalidate();
 }
}

好了,基本完成,我们还要第一时间获取headview的高度,那么在onmeasure()中获取比较好,并且只获取一次如下

 if (maxy == -1)
  maxy = mheadsecond.getmeasuredheight();

在onfinishinflate()方法中,该方法的执行标志着所有的view都已经add完毕,这里我们进行初始化是比较妥当的。

 @override
  protected void onfinishinflate() {
  super.onfinishinflate();
  int count = getchildcount();
  //本粘性布局只支持listview
  if (count == 3 && getchildat(2) instanceof listview)
   init();
 }
 /**
  * 初始化
  */
 private void init() {
  //获得子元素
  mheadfiest = getchildat(0);
  mheadsecond = getchildat(1);
  mlistview = (listview) getchildat(2);
  mlistview.setonscrolllistener(this);
  mscroll = new scroller(getcontext());
 }

好了,基本就是这些。
github地址:https://github.com/yzzandroid/lianxinview

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