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

android仿QQ个人主页下拉回弹效果

程序员文章站 2023-12-17 15:06:58
先看效果: 效果不错吧! 进入主题之前,先了解imageview的scaletype的center_crop,网络上说的已经很清楚了 : 以下抄自网络: 1...

先看效果:

android仿QQ个人主页下拉回弹效果

效果不错吧!

进入主题之前,先了解imageview的scaletype的center_crop,网络上说的已经很清楚了 : 以下抄自网络:

1.android:scaletype=”centercrop”
以填满整个imageview为目的,将原图的中心对准imageview的中心,等比例放大原图,直到填满imageview为止(指的是imageview的宽和高都要填满),原图超过imageview的部分作裁剪处理。

均衡的缩放图像(保持图像原始比例),使图片的两个坐标(宽、高)都大于等于 相应的视图坐标(负的内边距)。图像则位于视图的*。 在xml 中可以使用的语法:android:scaletype=”centercrop”。

不说废话,直接进入主题!!

思路

1.先将topview的布局和listview平级,然后将topview以及topview包裹的imageview中传listview,即一般是activity的layout
2.重写listview的ontoucevent()方法,但不做任何拦截,只在action时,控制imageview以及topview的高度,使其重新layout然后重新布局就可以了。
3.以上是大概思路,这里具体分析:当action_down时记录其初始位置,action_move时得到dy,通过dy来判断是上啦还是下拉:
(1)dy>0,则是下拉,不断重新设置topview和imageview的高度,又因为imageview的scaletype=center_crop,所以图片会按照这个规则进行等比拉伸,当到达图片最大时就会有不断放大的过程
当松开手或者手指移出屏幕外时(action_up|action_outside|action_cancel)时让其回到初始位置,并伴着回弹过程,这里通过自定义动画让其具备回弹效果
(2)dy<0,则是上拉,上推的过程,由于topview和imageview不具备滚动的效果,所以上推也是通过控制topview和imageview的高度,并且当topview和imageview滑出屏幕时就不在更改高度防止不断的绘制,提高性能。
ok,大体思路就这样。具体分析代码如下:

实现:

activity的xml
stretch_act.xml:

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

 <!--这是topview-->
 <relativelayout
  android:id="@+id/rl_top"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  >

  <!--这是imageview,一定要设置scaletype为centercrop-->
  <imageview
   android:id="@+id/iv_stretch_pic"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:scaletype="centercrop"
   android:src="@drawable/stretch_s"
   />

  <textview
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_alignbottom="@id/iv_stretch_pic"
   android:text="你最美,你最酷…………^^"
   android:textsize="16sp"/>

 </relativelayout>
<!--这是自定义的listview-->
 <com.example.zwr.myapplication.widget.stretchlistview
  android:id="@+id/listview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:cachecolorhint="@null"
  android:divider="@null"
  android:listselector="#00000000"/>


</linearlayout>

看看stretchlistview:

/***
  * @param topview
  * @param imgresid 图片id
  */
 public void settopview(view topview, int imgresid) {
  if (null != topview) {
   this.topview = topview;
   imageview = (imageview) topview.findviewbyid(imgresid);
  }
 }

通过这个对外的方法,将topview以及imageview的id传进来

分析:重新listview的ontouchevent():

action_down:
  case motionevent.action_down:
    starty = ev.getrawy();
    if (!hadinit) {//初始化,只要初始化一次就够了
     childat0top = getchildat(0).gettop();
     ivinitheight = imageview.getheight();
     hadinit = true;
    }
    break;

只是进行一些初始化操作:

 1. starty:相对于屏幕顶部的高度
 2. childat0top,获取listview的第一个view的top距离、
 3. ivinitheight:获取imageview的初始高度,即刚进来时的高度
action_move:

case motionevent.action_move:
    log.d(tag, "dy = " + dy);
    dy = ev.getrawy() - starty;
    if (dy > 0 && 0 == getfirstvisibleposition() && 
    childat0top == getchildat(0).gettop()) {//(1)手指从上往下拉:下拉
     int tempdy = (int) (dy + 0.5);
     //一定也要给topview增加一定的高度,否则从上啦到下拉就不会显示
     imageview.getlayoutparams().height = imageview.getheight() + tempdy;
     topview.getlayoutparams().height = topview.getheight() + tempdy;
     topview.requestlayout();
     ischangedheight = true;

    } else {//(2)手指从下往上拉:上拉
     int tempdy = (int) (dy - 0.5);
     int currheight = imageview.getheight();
     float translationy = getnegativemaxvalue(tempdy, -currheight, 0);
     if (translationy <= 0 && currheight > 0) {
      linearlayout.layoutparams lp = 
      (linearlayout.layoutparams) topview.getlayoutparams();
      //一定要减去titlebar,如果没有去掉winow.xxx.title,还要减去这个高度,否则会显示不全
      lp.height = topview.getheight() + (int) translationy;
      topview.requestlayout();//
      ischangedheight = true;
     }
    }
    //用这个getrawy而不是用gety,是因为listview也会随着改变,
    //而gety获取的就是listview本身的y,所以基本是变化不大的,
    // 而使用getrawy相对于屏幕的距离,保证滑动了多大的距离就改变多大的距离
    starty = ev.getrawy();
    break;

当下拉时:主要条件如下:

1.dy > 0 && 0 == getfirstvisibleposition() && childat0top ==
getchildat(0).gettop()
意思是当下拉时,并且listview的第一个位置显示全了,才能下拉放大图片,这是避免,listview已经发生滚动了,需要回到初始位置才能下拉放大,否则会出现,立即下拉放大,体验不好
2.当上拉时 主要条件
if (translationy <= 0 && currheight > 0)
currheight>0:当前imageview的高度,如果已经滚动到顶部或者超出,则不再进行滚动,防止已经滚出屏幕不可视了,还在进行滚动。
translationy<=0: 这个值是滚动的距离,这个距离不能超过imageview的高度,由于上拉时dy是负值,所以要判断是否小于0;其主要方法如下:

 float translationy = getnegativemaxvalue(tempdy, -currheight, 0);
 /***
  * 手指上移过程dy是负数
  * 返回负数最大值:0是最大值,不可以超过
  *
  * @param value   移动的最终距离:上次的位置+当次移动的偏移量之和,就是本次要移动的最终的偏移量
  * @param canmovemaxvalue 可移动的最大值
  * @param maxvalue
  * @return
  */
 public static float getnegativemaxvalue(float value,float canmovemaxvalue, float maxvalue) {
  return math.min(maxvalue, math.max(canmovemaxvalue, value));
 }

action_up:

case motionevent.action_outside:
   case motionevent.action_cancel:
   case motionevent.action_up:
    if (ischangedheight) {
     if (imageview.getheight() > ivinitheight) {// (1)手指从上往下拉:下拉
      resetanimation resetanimation = 
      new resetanimation(ivinitheight, imageview, topview);
      resetanimation.setduration(200);
      imageview.startanimation(resetanimation);
     } else {//(2)手指从下往上拉:上拉。。。这个不用处理。。。因为上拉后松开让其topview固定

     }
     ischangedheight = false;
    }
    break;

ischangedheight:当发生imageview发生改变,并且是下拉时,这是松开手或者手指移出屏幕,则让其回弹到初始位置;这里是通过自定义动画来改变其变化的高度,达到回弹效果 代码如下

 /**
  * 自定义回弹动画,使imageview和topview过渡回弹到初始位置
  */
 static class resetanimation extends animation {
  private view topview;
  private int topcurrheight;

  private imageview ivstretch;
  private int ivinitheight;
  private int ivcurrheight;

  public resetanimation(int ivinitheiht, imageview ivstretch, view topview) {

   this.ivinitheight = ivinitheiht;
   this.ivcurrheight = ivstretch.getheight();
   this.topcurrheight = topview.getheight();
   this.ivstretch = ivstretch;
   this.topview = topview;
  }

  @override
  protected void applytransformation(float interpolatedtime, transformation t) {
   int dy = (int) ((ivcurrheight - ivinitheight) * interpolatedtime);
   log.d(tag, "anim dy = " + dy);
   ivstretch.getlayoutparams().height = ivcurrheight - dy;
   topview.getlayoutparams().height = topcurrheight - dy;
   topview.requestlayout();
  }
 }

其实主要是applytransformation(float interpolatedtime, transformation t) 这个方法

主要是通过这个渐变因子interpolatedtime来控制,其值范围是(0~1) 所以计算渐变的高度如下
int dy = (int) ((ivcurrheight - ivinitheight) * interpolatedtime);
然后一定要记得调用topview.requestlayout(),让其重新布局绘制。这样就完成了,所有代码,也就一百行代码左右,是不是很简单。而且通过这个demo,可以很好的拓展到scrollview中。

注意:
网上有些demo是通过overscrollby()这个方法中搞事情,因为

/***
  * 这个方法是在滑出屏幕时回调,但是由于android系统国内厂商修改的面目全非,有些机型是不会回调的,比如vivo
  * 所以不要使用这个方法搞事情
  *
  * @param scrollx
  * @param scrolly
  */
 @override
 protected boolean overscrollby(int deltax, int deltay, int scrollx, int scrolly, 
 int scrollrangex, int scrollrangey, int maxoverscrollx, int maxoverscrolly, 
 boolean istouchevent) {
  log.d(tag, "deltax = " + deltax + " deltay = " + deltay);
  return super.overscrollby(deltax, deltay, scrollx, scrolly, scrollrangex,
   scrollrangey, maxoverscrollx, maxoverscrolly, istouchevent);
 }


其自带dy,还有一些其它的参数,应有尽有。但是由于android系统是开源的导致有些系统是无法回调这个方法的,以至于无法实现回弹效果(比如:vivox5)等等。所以在ontouchevent()搞事情才是王道
ok!,以上有什么问题,请不吝指正!!!

demo:android个人主页下拉回弹

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

上一篇:

下一篇: