前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android ListView实现下拉顶部图片变大效果

Android ListView实现下拉顶部图片变大效果

作者头像
砸漏
发布2020-10-29 22:17:27
9120
发布2020-10-29 22:17:27
举报
文章被收录于专栏:恩蓝脚本恩蓝脚本

本文实例为大家分享了Android ListView下拉顶部图片变大的具体代码,供大家参考,具体内容如下

在git上查看牛人的代码,发现是反编译别人的代码,还没加注释,代码也没有完全编译完整,所以这里我做的简单的注释,仅供学习。

变量说明

这里变量包含了:自定义返回动画加速度、自定义动画线程、头部图片view,最后的y坐标,做好的比例,做大的比例等。

private static final String TAG = "PullToZoomListView";

 private static final int INVALID_VALUE = -1;//重置值

 //自定义加速度动画
 private static final Interpolator sInterpolator = new Interpolator() {
  public float getInterpolation(float interpolator) {
   float f = interpolator - 1.0F;
   return 1.0F + f * (f * (f * (f * f)));
  }
 };
 private int mActivePointerId = INVALID_VALUE;//当前手指的Id
 private FrameLayout mHeaderContainer;//头部
 private int mHeaderHeight;//头部图片的高度
 private ImageView mHeaderImage;//头部图片
 float mLastMotionY = INVALID_VALUE;//最后y坐标
 float mLastScale = INVALID_VALUE;//最后的比例
 float mMaxScale = INVALID_VALUE;//最大的比例
 private OnScrollListener mOnScrollListener;//滑动监听
 private ScalingRunnalable mScalingRunnalable;//动画线程
 private int mScreenHeight;//屏幕高度
 private ImageView mShadow;//阴影遮罩

自定义View初始化:设置了头部的头部和遮罩并且设置了监听。

/**
  * 初始化
  * @param paramContext
  */
 private void init(Context paramContext) {
  DisplayMetrics metrics = new DisplayMetrics();
  ((Activity) paramContext).getWindowManager().getDefaultDisplay().getMetrics(metrics);

  this.mScreenHeight = metrics.heightPixels;//屏幕高度赋值
  this.mHeaderContainer = new FrameLayout(paramContext);//头部
  this.mHeaderImage = new ImageView(paramContext);//头部图片
  int screenWidth = metrics.widthPixels;//屏幕宽度

  //设置头部View的样式 设置屏幕宽度,最大样式高度为屏幕高度的9/16
  setHeaderViewSize(screenWidth, (int) (9.0F * (screenWidth / 16.0F)));

  this.mShadow = new ImageView(paramContext);//遮罩
  FrameLayout.LayoutParams layoutParams =
    new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
      ViewGroup.LayoutParams.MATCH_PARENT);
  layoutParams.gravity = Gravity.CENTER;
  this.mShadow.setLayoutParams(layoutParams);//设置遮罩样式
  //头部添加View
  this.mHeaderContainer.addView(this.mHeaderImage);
  this.mHeaderContainer.addView(this.mShadow);
  //添加头部
  addHeaderView(this.mHeaderContainer);
  //初始化返回动画
  this.mScalingRunnalable = new ScalingRunnalable();
  //设置监听
  super.setOnScrollListener(this);
 }

开启动画:判断当前的头部布局底部的位置–是否大于图片的初始化高度。

/**
  * 开启动画
  */
 private void endScraling() {
  if (this.mHeaderContainer.getBottom()  = this.mHeaderHeight) {
   Log.d(TAG, "this.mScalingRunnalable.startAnimation(200L)");
   this.mScalingRunnalable.startAnimation(200L);
  }
 }

多指触碰时将第0个手指赋值。

/**
  * 多点触碰的时候按下,当第0个有手指抬起,再次有手指按下后,将按下的事件的手指指针作为当前手指指针
  *
  * @param motionEvent
  */
 private void onSecondaryPointerUp(MotionEvent motionEvent) {
  Log.d(TAG, "onSecondaryPointerUp motionEvent.getPointerId(0) = " + motionEvent.getPointerId(0));
  Log.d(TAG, "onSecondaryPointerUp this.mActivePointerId = " + this.mActivePointerId);
  if (motionEvent.getPointerId(0) == this.mActivePointerId) {
   this.mLastMotionY = motionEvent.getY(0);
   this.mActivePointerId = motionEvent.getPointerId(0);
  }
  Log.d(TAG, "onSecondaryPointerUp mLastMotionY = " + mLastMotionY);
  Log.d(TAG, "onSecondaryPointerUp mActivePointerId = " + mActivePointerId);
 }

重置所有的数据

/**
 * 重置所有数据
*/
 private void reset() {
  this.mActivePointerId = INVALID_VALUE;
  this.mLastMotionY = INVALID_VALUE;
  this.mMaxScale = INVALID_VALUE;
  this.mLastScale = INVALID_VALUE;
 }

向上滚动时修改布局样式

@Override
 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  Log.d(TAG, "onScroll");

  float bottomSpacing = this.mHeaderHeight - this.mHeaderContainer.getBottom();
  Log.d(TAG, "onScroll bottomSpacing = " + bottomSpacing);

  if ((bottomSpacing   0.0F) && (bottomSpacing < this.mHeaderHeight)) {//如果是向上滑动
   int toUpScroll = (int) (0.65D * bottomSpacing);
   this.mHeaderImage.scrollTo(0, -toUpScroll);
   Log.d(TAG, "onScroll 向上滑动 toUpScroll = " + toUpScroll);
  } else if (this.mHeaderImage.getScrollY() != 0) {
   Log.d(TAG, "onScroll this.mHeaderImage.getScrollY() = " + this.mHeaderImage.getScrollY());
   this.mHeaderImage.scrollTo(0, 0);
  }
  if (this.mOnScrollListener != null) {
   this.mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
  }
 }

不同事件处理,修改布局样式

@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_DOWN:
if (!this.mScalingRunnalable.mIsFinished) {
this.mScalingRunnalable.abortAnimation();
}
this.mLastMotionY = motionEvent.getY();
//获取第一个手指指针的ID
this.mActivePointerId = motionEvent.getPointerId(0);
this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);
Log.d(TAG, "onTouchEvent ACTION_DOWN mLastMotionY = " + mLastMotionY);
Log.d(TAG, "onTouchEvent ACTION_DOWN mActivePointerId = " + mActivePointerId);
Log.d(TAG, "onTouchEvent ACTION_DOWN mMaxScale = " + mMaxScale);
Log.d(TAG, "onTouchEvent ACTION_DOWN mLastScale = " + mLastScale);
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouchEvent ACTION_MOVE mActivePointerId" + mActivePointerId);
//获取当前id的手机指针
int pointer = motionEvent.findPointerIndex(this.mActivePointerId);
//判断指针不为空
if (pointer == INVALID_VALUE) {
Log.e(TAG, "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent");
} else {
//如果开始没有赋值,则需要赋值
if (this.mLastMotionY == INVALID_VALUE) {
this.mLastMotionY = motionEvent.getY(pointer);
}
if (this.mHeaderContainer.getBottom()  = this.mHeaderHeight) {
//获取头部样式
ViewGroup.LayoutParams headerParams = this.mHeaderContainer.getLayoutParams();
float currentScale = ((motionEvent.getY(pointer) - this.mLastMotionY + this.mHeaderContainer.getBottom())
/ this.mHeaderHeight - this.mLastScale)
/ 2.0F + this.mLastScale;
if ((this.mLastScale <= 1.0D) && (currentScale < this.mLastScale)) {
//最后比例小于默认并且当前的比例要小于上次的比例,则修改头部的高度
headerParams.height = this.mHeaderHeight;
this.mHeaderContainer.setLayoutParams(headerParams);
return super.onTouchEvent(motionEvent);
} else {
//否则,将当前的比例赋值为最后一次的比例
this.mLastScale = Math.min(Math.max(currentScale, 1.0F), this.mMaxScale);
headerParams.height = ((int) (this.mHeaderHeight * this.mLastScale));
//判断修改后的高度小于屏幕的高度
if (headerParams.height < this.mScreenHeight) {
this.mHeaderContainer.setLayoutParams(headerParams);
}
//记录最后的y坐标
this.mLastMotionY = motionEvent.getY(pointer);
return true;
}
}
this.mLastMotionY = motionEvent.getY(pointer);
}
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "onTouchEvent ACTION_UP 重置");
//重置
reset();
//当手指起来的时候,结算拉伸,判断是否开启动画
endScraling();
break;
case MotionEvent.ACTION_CANCEL:
int actionIndex = motionEvent.getActionIndex();//获取当前最上层的指针
this.mLastMotionY = motionEvent.getY(actionIndex);//获取最后的y坐标
this.mActivePointerId = motionEvent.getPointerId(actionIndex);//获取最上层指针的手指
Log.d(TAG, "onTouchEvent ACTION_CANCEL actionIndex = " + actionIndex + " mLastMotionY = " + mLastMotionY + " mActivePointerId = " + mActivePointerId);
break;
case MotionEvent.ACTION_POINTER_DOWN:
//当第二个手指按下或者放开触发这个事件
onSecondaryPointerUp(motionEvent);
this.mLastMotionY = motionEvent.getY(motionEvent.findPointerIndex(this.mActivePointerId));
Log.d(TAG, "onTouchEvent_Po ACTION_POINTER_DOWN mLastMotionY = " + mLastMotionY);
break;
case MotionEvent.ACTION_POINTER_UP:
//当第二个手指按下或者放开
Log.d(TAG, "onTouchEvent_Po ACTION_POINTER_UP ");
break;
}
return super.onTouchEvent(motionEvent);
}

向上返回时的动画

/**
* 向上返回的动画
*/
class ScalingRunnalable implements Runnable {
long mDuration;//持续时间
boolean mIsFinished = true;//是否结束
float mScale;//比例
long mStartTime;//开始时间
ScalingRunnalable() {
}
/**
* 中止动画
*/
public void abortAnimation() {
this.mIsFinished = true;
}
/**
* 是否中止
*
* @return
*/
public boolean isFinished() {
return this.mIsFinished;
}
public void run() {
Log.d(TAG, "ScalingRunnalable mIsFinished = " + this.mIsFinished + " this.mScale = " + this.mScale);
float currentScale;
ViewGroup.LayoutParams mHeaderContainerParams;//头部样式
//判断是否中止和已经滑动超过的默认大小
if ((!this.mIsFinished) && (this.mScale   1.0D)) {
float currentTime = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime) / (float) this.mDuration;
currentScale = this.mScale - (this.mScale - 1.0F) * PullToZoomListView.sInterpolator.getInterpolation(currentTime);
Log.d(TAG, "ScalingRunnalable currentTime = " + currentTime + " currentScale = " + currentScale);
mHeaderContainerParams = PullToZoomListView.this.mHeaderContainer.getLayoutParams();
if (currentScale   1.0F) {
Log.d(TAG, "ScalingRunnalable currentScale   1.0 -- 修改头部高度");
mHeaderContainerParams.height = PullToZoomListView.this.mHeaderHeight;
mHeaderContainerParams.height = ((int) (currentScale * PullToZoomListView.this.mHeaderHeight));
PullToZoomListView.this.mHeaderContainer.setLayoutParams(mHeaderContainerParams);
PullToZoomListView.this.post(this);//循环执行
} else {
Log.d(TAG, "ScalingRunnalable currentScale < 1.0 -- 中止");
this.mIsFinished = true;
}
}
}
public void startAnimation(long paramLong) {
Log.d(TAG, "ScalingRunnalable 开始执行动画");
this.mStartTime = SystemClock.currentThreadTimeMillis();
this.mDuration = paramLong;
this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer.getBottom()) / PullToZoomListView.this.mHeaderHeight);
this.mIsFinished = false;
Log.d(TAG, "ScalingRunnalable this.mStartTime = " + this.mStartTime);
Log.d(TAG, "ScalingRunnalable this.mDuration = " + this.mDuration);
Log.d(TAG, "ScalingRunnalable this.mScale = " + this.mScale);
Log.d(TAG, "ScalingRunnalable this.mIsFinished = " + this.mIsFinished);
PullToZoomListView.this.post(this);
}
}

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档