当前位置: 移动技术网 > IT编程>移动开发>Android > Android自定义StickinessView粘性滑动效果

Android自定义StickinessView粘性滑动效果

2019年07月24日  | 移动技术网IT编程  | 我要评论

茼蒿的营养价值,金三维视频网,疯狂坦克2red

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


大家看到效果了,这里我是继承了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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网