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

Android中ScrollView 滑到头部或尾部可伸缩放大效果

程序员文章站 2023-12-05 13:19:10
最近做项目,想要这么一个效果,就是scrollview 滑动到顶部,当不能在滑动的时候,图片可以下拉放大,松开又恢复。滑到底部没有内容的时候,也有伸缩效果,先看看效果图吧。...

最近做项目,想要这么一个效果,就是scrollview 滑动到顶部,当不能在滑动的时候,图片可以下拉放大,松开又恢复。滑到底部没有内容的时候,也有伸缩效果,先看看效果图吧。

Android中ScrollView 滑到头部或尾部可伸缩放大效果

就是如上图这么个效果。系统提供的scrollview 是不能做到这个效果的,所以需要自己自定义,网上找了一些资料。也参考了下其他人的做法。自己也整合了一下。希望对大家有所帮助。

核心的控件就是下面的这段代码:

package com.kokjuis.travel.customview; 
import android.animation.objectanimator; 
import android.animation.valueanimator; 
import android.content.context; 
import android.graphics.rect; 
import android.util.attributeset; 
import android.view.motionevent; 
import android.view.view; 
import android.view.viewgroup; 
import android.view.animation.translateanimation; 
import android.widget.scrollview; 
/** 
 * 注意使用的时候需要放大的view,一般是第一个relativelayout或者linearlayout。要加上 android:layout_gravity="center_horizontal" 
 * <p> 
 * created by kokjuis on 2017/3/14. 189155278@qq.com 
 */ 
public class bouncezoomscrollview extends scrollview { 
 private static final string tag = "bouncescrollview"; 
 //----头部收缩属性-------- 
 // 记录首次按下位置 
 private float mfirstposition = 0; 
 // 头部图片是否正在放大 
 private boolean mscaling = false; 
 private view dropzoomview;//需要被放大的view 
 private int dropzoomviewwidth; 
 private int dropzoomviewheight; 
 //----头部收缩属性end-------- 
 //------尾部收缩属性-------- 
 private view inner;// 子view 
 private float y;// 点击时y坐标 
 private rect normal = new rect();// 矩形(这里只是个形式,只是用于判断是否需要动画.) 
 private boolean iscount = false;// 是否开始计算 
 //最后的坐标 
 private float lastx = 0; 
 private float lasty = 0; 
 //当前坐标 
 private float currentx = 0; 
 private float currenty = 0; 
 //移动的坐标量 
 private float distancex = 0; 
 private float distancey = 0; 
 private boolean updownslide = false; //判断上下滑动的flag 
 //------尾部收缩属性end-------- 
 public bouncescrollview(context context, attributeset attrs) { 
 super(context, attrs); 
 } 
 //初始化 
 private void init() { 
 setoverscrollmode(over_scroll_never); 
 if (getchildat(0) != null) { 
  inner = getchildat(0);//这个是底部收缩的view 
  //头部收缩的 
  viewgroup vg = (viewgroup) getchildat(0); 
  if (vg.getchildat(0) != null) { 
  dropzoomview = vg.getchildat(0); 
  } 
 } 
 } 
 /*** 
 * 生成视图工作完成.该函数在生成视图的最后调用,在所有子视图添加完之后. 即使子类覆盖了 onfinishinflate 
 * 方法,也应该调用父类的方法,使该方法得以执行. 
 */ 
 @override 
 protected void onfinishinflate() { 
 //初始化 
 init(); 
 super.onfinishinflate(); 
 } 
 @override 
 public boolean dispatchtouchevent(motionevent ev) { 
 //这里只是计算尾部坐标 
 currentx = ev.getx(); 
 currenty = ev.gety(); 
 switch (ev.getaction()) { 
  case motionevent.action_move: 
  distancex = currentx - lastx; 
  distancey = currenty - lasty; 
  if (math.abs(distancex) < math.abs(distancey) && math.abs(distancey) > 12) { 
   updownslide = true; 
  } 
  break; 
 } 
 lastx = currentx; 
 lasty = currenty; 
 if (updownslide && inner != null) commontouchevent(ev); 
 return super.dispatchtouchevent(ev); 
 } 
 /*** 
 * 触摸事件 
 * 
 * @param ev 
 */ 
 public void commontouchevent(motionevent ev) { 
 //头部缩放计算 
 if (dropzoomviewwidth <= 0 || dropzoomviewheight <= 0) { 
  dropzoomviewwidth = dropzoomview.getmeasuredwidth(); 
  dropzoomviewheight = dropzoomview.getmeasuredheight(); 
 } 
 switch (ev.getaction()) { 
  case motionevent.action_up: 
  //手指离开后头部恢复图片 
  mscaling = false; 
  replyimage(); 
  // 手指松开尾部恢复 
  if (isneedanimation()) { 
   animation(); 
   iscount = false; 
  } 
  clear0(); 
  break; 
  //这里头尾分开处理,互不干扰 
  case motionevent.action_move: 
  //尾部处理 
  final float prey = y;// 按下时的y坐标 
  float nowy = ev.gety();// 时时y坐标 
  int deltay = (int) (prey - nowy);// 滑动距离 
  if (!iscount) { 
   deltay = 0; // 在这里要归0. 
  } 
  y = nowy; 
  // 当滚动到最上或者最下时就不会再滚动,这时移动布局 
  if (isneedmove()) { 
   // 初始化头部矩形 
   if (normal.isempty()) { 
   // 保存正常的布局位置 
   normal.set(inner.getleft(), inner.gettop(), 
    inner.getright(), inner.getbottom()); 
   } 
   // 移动布局 
   inner.layout(inner.getleft(), inner.gettop() - deltay / 2, 
    inner.getright(), inner.getbottom() - deltay / 2); 
  } 
  iscount = true; 
  //尾部处理end 
  //头部处理 
  if (!mscaling) { 
   if (getscrolly() == 0) { 
   mfirstposition = ev.gety();// 滚动到顶部时记录位置,否则正常返回 
   } else { 
   break; 
   } 
  } 
  int distance = (int) ((ev.gety() - mfirstposition) * 0.6); // 滚动距离乘以一个系数 
  if (distance < 0) { // 当前位置比记录位置要小,正常返回 
   break; 
  } 
  // 处理放大 
  mscaling = true; 
  setzoom(1 + distance); 
  //头部处理end 
  break; 
 } 
 } 
 /*** 
 * 回缩动画,尾部往下缩动画 
 */ 
 public void animation() { 
 // 开启移动动画 
 translateanimation ta = new translateanimation(0, 0, inner.gettop(), 
  normal.top); 
 ta.setduration(200); 
 inner.startanimation(ta); 
 // 设置回到正常的布局位置 
 inner.layout(normal.left, normal.top, normal.right, normal.bottom); 
 normal.setempty(); 
 } 
 // 是否需要开启动画 
 public boolean isneedanimation() { 
 return !normal.isempty(); 
 } 
 // 回弹动画,header往上缩动画 (使用了属性动画) 
 public void replyimage() { 
 final float distance = dropzoomview.getmeasuredwidth() - dropzoomviewwidth; 
 // 设置动画 
 valueanimator anim = objectanimator.offloat(0.0f, 1.0f).setduration((long) (distance * 0.7)); 
 anim.addupdatelistener(new valueanimator.animatorupdatelistener() { 
  @override 
  public void onanimationupdate(valueanimator animation) { 
  float cval = (float) animation.getanimatedvalue(); 
  setzoom(distance - ((distance) * cval)); 
  } 
 }); 
 anim.start(); 
 } 
 //头部缩放 
 public void setzoom(float s) { 
 if (dropzoomviewheight <= 0 || dropzoomviewwidth <= 0) { 
  return; 
 } 
 viewgroup.layoutparams lp = dropzoomview.getlayoutparams(); 
 lp.width = (int) (dropzoomviewwidth + s); 
 lp.height = (int) (dropzoomviewheight * ((dropzoomviewwidth + s) / dropzoomviewwidth)); 
 dropzoomview.setlayoutparams(lp); 
 } 
 /*** 
 * 是否需要移动布局 inner.getmeasuredheight():获取的是控件的总高度 
 * 
 * getheight():获取的是屏幕的高度 
 * 
 * @return 
 */ 
 public boolean isneedmove() { 
 int offset = inner.getmeasuredheight() - getheight(); 
 int scrolly = getscrolly(); 
 // 0是顶部,后面那个是底部 
 if (scrolly == 0 || scrolly == offset) { 
  return true; 
 } 
 return false; 
 } 
 //清理尾部属性值 
 private void clear0() { 
 lastx = 0; 
 lasty = 0; 
 distancex = 0; 
 distancey = 0; 
 updownslide = false; 
 } 
}

下面是我自己使用的一个layout例子:

<?xml version="1.0" encoding="utf-8"?> 
<com.kokjuis.travel.customview.bouncezoomscrollview xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:imagecontrol="http://schemas.android.com/apk/res-auto" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:scrollbars="none"> 
 <!-- <linearlayout 
  android:id="@+id/ui_allchatlist_header_relativelayout" 
  android:layout_width="match_parent" 
  android:layout_height="45dp" 
  android:background="@drawable/bar_bg" 
  android:orientation="horizontal" 
  android:paddingbottom="5dp" 
  android:paddingtop="5dp"> 
  <button 
  android:id="@+id/ui_allchatlist_backbtn" 
  android:layout_width="wrap_content" 
  android:layout_height="match_parent" 
  android:layout_marginbottom="3dp" 
  android:layout_marginleft="5dp" 
  android:layout_margintop="3dp" 
  android:background="@null" 
  android:gravity="center" 
  android:text="我" 
  android:textcolor="@color/white" 
  android:textsize="18sp" /> 
 </linearlayout> 
 --> 
 <linearlayout 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:orientation="vertical"> 
 <relativelayout 
  android:layout_width="match_parent" 
  android:layout_height="250dp" 
  android:layout_gravity="center_horizontal"> 
  <imageview 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:layout_marginbottom="40dp" 
  android:background="@drawable/personinfo_bg" /> 
  <com.kokjuis.travel.customview.roundimageview 
  android:id="@+id/headimage" 
  android:layout_width="80dp" 
  android:layout_height="80dp" 
  android:layout_alignparentbottom="true" 
  android:layout_centerhorizontal="true" 
  android:src="@drawable/headimg" 
  imagecontrol:border_inside_color="#fff7f2e9" 
  imagecontrol:border_outside_color="#ffd5d1c8" 
  imagecontrol:border_thickness="2dp" /> 
 </relativelayout> 
 <linearlayout 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:orientation="vertical"> 
  <textview 
  android:id="@+id/name_tv" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center_horizontal" 
  android:layout_margintop="6dp" 
  android:gravity="center_vertical" 
  android:text="昵称:" 
  android:textsize="20sp" /> 
  <textview 
  android:id="@+id/motto_tv" 
  android:layout_width="wrap_content" 
  android:layout_height="40dp" 
  android:layout_gravity="center_horizontal" 
  android:gravity="center_vertical" 
  android:text="座右铭:" 
  android:textsize="11sp" /> 
  <linearlayout 
  android:layout_width="150dp" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center_horizontal" 
  android:orientation="vertical"> 
  <textview 
   android:id="@+id/accounts_tv" 
   android:layout_width="wrap_content" 
   android:layout_height="wrap_content" 
   android:layout_margintop="8dp" 
   android:gravity="center_vertical" 
   android:text="帐号:" 
   android:textsize="12sp" /> 
  <textview 
   android:id="@+id/gender_tv" 
   android:layout_width="wrap_content" 
   android:layout_height="wrap_content" 
   android:layout_margintop="2dp" 
   android:gravity="center_vertical" 
   android:text="性别:" 
   android:textsize="12sp" /> 
  </linearlayout> 
  <button 
  android:id="@+id/logout_btn" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center_horizontal" 
  android:layout_margintop="20dp" 
  android:text="注销" /> 
 </linearlayout> 
 </linearlayout> 
</com.kokjuis.travel.customview.bouncezoomscrollview>

以上所述是小编给大家介绍的android scrollview 滑到头部或尾部可伸缩放大效果,希望对大家有所帮助