首页 > 系统 > Android > 正文

Android通过overScrollBy实现下拉视差特效

2019-12-12 02:20:05
字体:
来源:转载
供稿:网友

overScrollBy实现下拉视差特效,效果图如下

先来分析overScrollBy方法的使用,它是View的方法,参数有点多:

/**   * 当滑动的超出上,下,左,右最大范围时回调   *   * @param deltaX     x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正   * @param deltaY     y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正   * @param scrollX    水平方向的永久偏移量   * @param scrollY    竖直方向的永久偏移量   * @param scrollRangeX  水平方向滑动的范围   * @param scrollRangeY  竖直方向滑动的范围   * @param maxOverScrollX 水平方向最大滑动范围   * @param maxOverScrollY 竖直方向最大滑动范围   * @param isTouchEvent  是否是手指触摸滑动, true为手指, false为惯性   * @return   */   @Override   protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,                  int scrollRangeX, int scrollRangeY, int maxOverScrollX,                  int maxOverScrollY, boolean isTouchEvent) {     return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,         scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,         isTouchEvent);   } 

大致步骤如下:

1.这整体是一个ListView,所以需要自定义一个ListView.
2.处理头部布局文件,将其以HeaderView的方式添加到自定义的ListView中
3.需要获取HeaderView的ImageView的初始高度和ImageView中图片的高度.因为这2个高度将决定下来的时候图片拉出的范围,以及松手后图片回弹的动画效果.对应控件宽高的获取,有兴趣的可以看这篇文章浅谈自定义View的宽高获取
4.在overScrollBy方法内通过修改ImageView的LayoutParams的height值来显示更多的图片内容.
5.在onTouchEvent方法内处理ACTION_UP事件,使ImageView有回弹的动画效果,这里介绍2种方式,分别是属性动画和自定义动画.

好了,先来看HeaderView的布局文件:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:orientation="vertical" >   <ImageView     android:id="@+id/imageView"     android:layout_width="match_parent"     android:layout_height="160dp"     <span style="color:#ff0000;">android:scaleType="centerCrop"</span>     android:src="@drawable/header" /> </LinearLayout> 

没什么特别的,就是一个ImageView,通过src设置了一张图片,这里唯一要将的就是scaleType属性,我这边设置了centerCrop,以图片的最小的边开始截取,因为这里选择的图片是高度大于宽度的,所以裁剪的时候会保留完整的宽度,中心裁剪,如下图所示:

自定义ListView代码,整体代码还是比较简短的.

/**  * Created by mChenys on 2015/12/23.  */ public class MyListView extends ListView {   private ImageView mHeaderIv; //HeaderView 的ImageView   private int mOriginalHeight; //最初ImageView的高度   private int mDrawableHeight;//ImageView中图片的高度    public MyListView(Context context) {     this(context, null);   }    public MyListView(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);     init();   }    /**    * 设置头部和获取高度信息    */   private void init() {     //初始化头部文件     View headerView = View.inflate(getContext(), R.layout.view_header, null);     mHeaderIv = (ImageView) headerView.findViewById(R.id.imageView);     //将其添加到ListView的头部     addHeaderView(headerView);     //通过设置监听来获取控件的高度     mHeaderIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {       @TargetApi(Build.VERSION_CODES.JELLY_BEAN)       @Override       public void onGlobalLayout() {         //只需监听一次,否则之后的onLayout方法回调的时候还是会回调这里         mHeaderIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);         mOriginalHeight = mHeaderIv.getMeasuredHeight();//获取ImageView的初始高度         mDrawableHeight = mHeaderIv.getDrawable().getIntrinsicHeight();//获取ImageView中图片的高度       }     });     //去掉下拉到头部后的蓝色线     setOverScrollMode(OVER_SCROLL_NEVER);   }    /**    * 当滑动的超出上,下,左,右最大范围时回调    *    * @param deltaX     x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正    * @param deltaY     y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正    * @param scrollX    水平方向的永久偏移量    * @param scrollY    竖直方向的永久偏移量    * @param scrollRangeX  水平方向滑动的范围    * @param scrollRangeY  竖直方向滑动的范围    * @param maxOverScrollX 水平方向最大滑动范围    * @param maxOverScrollY 竖直方向最大滑动范围    * @param isTouchEvent  是否是手指触摸滑动, true为手指, false为惯性    * @return    */   @Override   protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,                   int scrollRangeX, int scrollRangeY, int maxOverScrollX,                   int maxOverScrollY, boolean isTouchEvent) {     // 手指拉动并且是下拉     if (isTouchEvent && deltaY < 0) {       // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果       if (mHeaderIv.getHeight() <= mDrawableHeight) {         // 高度不超出图片最大高度时,才让其生效         int newHeight = (int) (mHeaderIv.getHeight() + Math.abs(deltaY / 3.0f));//这里除以3是为了达到视差的效果         mHeaderIv.getLayoutParams().height = newHeight;         //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位         mHeaderIv.requestLayout();       }     }     return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);   }    @Override   public boolean onTouchEvent(MotionEvent ev) {     switch (ev.getAction()) {       case MotionEvent.ACTION_UP:         // 执行回弹动画, 方式一: 属性动画/值动画         //获取ImageView在松手时的高度         int currHeight = mHeaderIv.getHeight();         // 从当前高度mHeaderIv.getHeight(), 执行动画到原始高度mOriginalHeight         ValueAnimator animator = ValueAnimator.ofInt(currHeight, mOriginalHeight);         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {           @Override           public void onAnimationUpdate(ValueAnimator animation) {             int value = (int) animation.getAnimatedValue();             mHeaderIv.getLayoutParams().height = value;             //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位             mHeaderIv.requestLayout();           }         });         animator.setDuration(500);         animator.setInterpolator(new OvershootInterpolator());         animator.start();          //方式二,通过自定义动画         /*ResetAnimation animation = new ResetAnimation(mHeaderIv, mHeaderIv.getHeight(), mOriginalHeight);         startAnimation(animation);*/         break;     }     return super.onTouchEvent(ev);   } } 

看看自定义动画:

/**  * 自定义动画  * Created by mChenys on 2015/12/24.  */ public class ResetAnimation extends Animation {   private final ImageView headerIv; //要执行动画的目标ImageView   private final int startHeight;//执行动画的开始时的高度   private final int endHeight;//执行动画结束时的高度   private IntEvaluator mEvaluator; //整型估值器    /**    * 构造方法初始化    *    * @param headerIv  应用动画的目标控件    * @param startHeight 开始的高度    * @param endHeight  结束的高度    */   public ResetAnimation(ImageView headerIv, int startHeight, int endHeight) {     this.headerIv = headerIv;     this.startHeight = startHeight;     this.endHeight = endHeight;     //定义一个int类型的类型估值器,用于获取实时变化的高度值     mEvaluator = new IntEvaluator();     //设置动画持续时间     setDuration(500);     //设置插值器     setInterpolator(new OvershootInterpolator());   }    /**    * 在指定的时间内一直执行该方法,直到动画结束    * interpolatedTime:0-1 标识动画执行的进度或者百分比    *    * @param interpolatedTime    * @param t    */   @Override   protected void applyTransformation(float interpolatedTime, Transformation t) {     int currHeight = mEvaluator.evaluate(interpolatedTime, startHeight, endHeight);     //通过LayoutParams不断的改变其高度     headerIv.getLayoutParams().height = currHeight;     //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位     headerIv.requestLayout();   } } 

MainActivity测试类:

public class MainActivity extends AppCompatActivity {    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     MyListView listView = new MyListView(this);     listView.setDividerHeight(1);     listView.setSelector(new ColorDrawable());     listView.setCacheColorHint(Color.TRANSPARENT);     listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES));     setContentView(listView);   } } 

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表