QQ,新浪个人中心页面

news/2025/2/5 20:00:24/

今天要写的这个效果属于刷新类,比较实用,像很多流行的 app 都是用了这种效果,大家熟知的QQ空间、微博个人主页等。

本篇思路其实是完全按照android中已有的思路去实现的这种效果。

1.那么在开始之前,请大家看一下实现的效果图。


2.那么我们一起来分析下实现的原理吧!

图片放大的原理是什么呢? 
通过改变图片显示控件 ImageView 的父控件的高度,比如这里的头部 View 是一个 FrameLayout;

FrameLayout 中再通过 add 方法把图片添加进去:addView(ImageView);

其中ImageView有几个属性是要特别注意的,ImageView 的放缩类型为从中间截取 

setScaleType(ImageView.ScaleType.CENTER_CROP); 
并且宽高设为匹配父控件;所以想要图片有放大效果,只需设置 FrameLayout 的 LayoutParams 中的 height值,通过改变 height 的值从而改变 ImageView 的显示高度。

如果你是对手势事件处理很了解的朋友,对这个效果的实现应该没有什么难度,唯一的一点就是判断何时该放大图片,何时该滚动ListView。 

3.原理分析完了,那么大家一起来看下代码吧:

/** 
* Created by guaju on 16/09/17. 
*/ 
public class PullZoomListView extends ListView {/*头部View 的容器*/
private FrameLayout mHeaderContainer;
/*头部View 的图片*/
private ImageView mHeaderImg;
/*屏幕的高度*/
private int mScreenHeight;
/*屏幕的宽度*/
private int mScreenWidth;private int mHeaderHeight;/*无效的点*/
private static final int INVALID_POINTER = -1;
/*滑动动画执行的时间*/
private static final int MIN_SETTLE_DURATION = 200; // ms
/*定义了一个时间插值器,根据ViewPage控件来定义的*/
private static final Interpolator sInterpolator = new Interpolator() {public float getInterpolation(float t) {t -= 1.0f;return t * t * t * t * t + 1.0f;}
};/*记录上一次手指触摸的点*/
private float mLastMotionX;
private float mLastMotionY;/*当前活动的点Id,有效的点的Id*/
protected int mActivePointerId = INVALID_POINTER;
/*开始滑动的标志距离*/
private int mTouchSlop;/*放大的倍数*/
private float mScale;
/*上一次放大的倍数*/
private float mLastScale;/*最大放大的倍数*/
private final float mMaxScale = 2.0f;
/*是否需要禁止ListView 的事件响应*/
private boolean isNeedCancelParent;/*这个不解释*/
private OnScrollListener mScrollListener ;/*下拉刷新的阈值*/
private final float REFRESH_SCALE = 1.20F;/*下拉刷新监听*/
private OnRefreshListener mRefreshListener ;public PullZoomListView(Context context) {super(context);init(context);
}public PullZoomListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);
}public PullZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);
}private void init(Context context) {/*这里获取的是一个无用值,可忽略*/final ViewConfiguration configuration = ViewConfiguration.get(context);mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);/*创建头部View 的容器*/mHeaderContainer = new FrameLayout(context);/*获取屏幕的像素值*/DisplayMetrics metrics = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);mScreenHeight = metrics.heightPixels;mScreenWidth = metrics.widthPixels;/*设置头部View 的初始大小*/mHeaderHeight = (int) ((9 * 1.0f / 16) * mScreenWidth);LayoutParams absLayoutParams = new LayoutParams(mScreenWidth, mHeaderHeight);mHeaderContainer.setLayoutParams(absLayoutParams);/*创建图片显示的View*/mHeaderImg = new ImageView(context);FrameLayout.LayoutParams imgLayoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);mHeaderImg.setScaleType(ImageView.ScaleType.CENTER_CROP);mHeaderImg.setLayoutParams(imgLayoutParams);mHeaderContainer.addView(mHeaderImg);/*增加头部View*/addHeaderView(mHeaderContainer);/*设置监听事件*/super.setOnScrollListener(new InternalScrollerListener() );}
/*处理事件用*/@Override
public boolean onTouchEvent(MotionEvent ev) {final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;switch (action) {case MotionEvent.ACTION_DOWN:/*计算 x,y 的距离*/int index = MotionEventCompat.getActionIndex(ev);mActivePointerId = MotionEventCompat.getPointerId(ev, index);if (mActivePointerId == INVALID_POINTER)break;mLastMotionX = MotionEventCompat.getX(ev, index);mLastMotionY = MotionEventCompat.getY(ev, index);// 结束动画,目前没做处理,可忽略abortAnimation();/*计算算一次放缩的比例*/mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);/*当按下的时候把这个标志为设为有效*/isNeedCancelParent = true ;break;case MotionEvent.ACTION_MOVE:int indexMove = MotionEventCompat.getActionIndex(ev);mActivePointerId = MotionEventCompat.getPointerId(ev, indexMove);if (mActivePointerId == INVALID_POINTER) {/*这里相当于松手*/finishPull();isNeedCancelParent = true ;} else {/*这是一个关键值,通过这个值来判断是否需要放大图片*/if (mHeaderContainer.getBottom() >= mHeaderHeight) {ViewGroup.LayoutParams params = this.mHeaderContainer.getLayoutParams();final float y = MotionEventCompat.getY(ev, indexMove);float dy = y - mLastMotionY;float f = ((y - this.mLastMotionY + this.mHeaderContainer.getBottom()) / this.mHeaderHeight - this.mLastScale)/ 2.0F + this.mLastScale;if ((this.mLastScale <= 1.0D) && (f <= this.mLastScale)) {params.height = this.mHeaderHeight;this.mHeaderContainer.setLayoutParams(params);return super.onTouchEvent(ev);}/*这里设置紧凑度*/dy = dy * 0.5f * (mHeaderHeight * 1.0f / params.height);mLastScale = (dy + params.height) * 1.0f / mHeaderHeight;mScale = clamp(mLastScale, 1.0f, mMaxScale);
// Log.v(“zgy”, “=======mScale=====” + mLastScale+”,f = “+f);params.height = (int) (mHeaderHeight * mScale);mHeaderContainer.setLayoutParams(params);mLastMotionY = y;/*这里,如果图片有放大,则屏蔽ListView 的其他事件响应*/if(isNeedCancelParent ){isNeedCancelParent = false;MotionEvent motionEvent = MotionEvent.obtain(ev);motionEvent.setAction(MotionEvent.ACTION_CANCEL);super.onTouchEvent(motionEvent);}return true;}mLastMotionY = MotionEventCompat.getY(ev, indexMove);}break;case MotionEvent.ACTION_UP:/*结束事件响应,做相应的操作*/finishPull();break;case MotionEvent.ACTION_POINTER_UP:/*这里需要注意,多点处理,这里的处理方式是:如果有两个手指按下,抬起的是后按下的手指,则不做处理* 如果抬起的是最先按下的手指,则复原图片效果。* */int pointUpIndex = MotionEventCompat.getActionIndex(ev);int pointId = MotionEventCompat.getPointerId(ev, pointUpIndex);if (pointId == mActivePointerId) {/*松手执行结束拖拽操作*//*结束事件响应,做相应的操作*/finishPull();}break;}return super.onTouchEvent(ev);
}@Override
public void setOnScrollListener(OnScrollListener l) {mScrollListener = l ;
}private void abortAnimation() {/*啥都没做,暂时没做而已*/
}private void finishPull() {mActivePointerId = INVALID_POINTER;/*这是一个阈值,如果成立,则表示图片已经放大了,在手指抬起的时候需要复原图片*/if (mHeaderContainer.getBottom() > mHeaderHeight){
// Log.v(“zgy”, “===super====onTouchEvent========”); 
/这里是下拉刷新的阈值,当达到了,则表示需要刷新,可以添加刷新动画/ 
if (mScale > REFRESH_SCALE){ 
if (mRefreshListener != null){ 
mRefreshListener.onRefresh(); 
} 
} 
/图片复原动画/ 
pullBackAnimation(); 
} 
}/*** 这是属性动画的知识,不懂的可以去看看属性动画的知识*/
private void pullBackAnimation(){ValueAnimator pullBack = ValueAnimator.ofFloat(mScale , 1.0f);pullBack.setInterpolator(sInterpolator);pullBack.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float value = (float) animation.getAnimatedValue();LayoutParams params = (LayoutParams) mHeaderContainer.getLayoutParams();params.height = (int) (mHeaderHeight * value);mHeaderContainer.setLayoutParams(params);}});pullBack.setDuration((long) (MIN_SETTLE_DURATION*mScale));pullBack.start();}/*** 通过事件和点的 id 来获取点的索引** @param ev* @param id* @return*/
private int getPointerIndex(MotionEvent ev, int id) {int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);if (activePointerIndex == -1)mActivePointerId = INVALID_POINTER;return activePointerIndex;
}public void setOnRefreshListener(OnRefreshListener l){mRefreshListener = l ;
}public ImageView getHeaderImageView() {return this.mHeaderImg;
}private float clamp(float value, float min, float max) {return Math.min(Math.max(value, min), max);
}private class InternalScrollerListener implements OnScrollListener{@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (mScrollListener != null){mScrollListener.onScrollStateChanged(view,scrollState);}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {float diff = mHeaderHeight - mHeaderContainer.getBottom();if ((diff > 0.0F) && (diff < mHeaderHeight)) {int i = (int) (0.3D * diff);mHeaderImg.scrollTo(0, -i);} else if (mHeaderImg.getScrollY() != 0) {mHeaderImg.scrollTo(0, 0);}Log.v("zgy","=========height==="+getScrolledY());if (mScrollListener != null){mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);}}
}public int getScrolledY() {View c = getChildAt(0);if (c == null) {return 0;}int firstVisiblePosition = getFirstVisiblePosition();int top = c.getTop();int headerHeight = 0;if (firstVisiblePosition >= 1) {headerHeight = mHeaderHeight;}return -top + firstVisiblePosition * c.getHeight() + headerHeight;
}public interface OnRefreshListener {void onRefresh() ;
}public void computeRefresh(){if (mActivePointerId != INVALID_POINTER){}
}
}





http://www.ppmy.cn/news/905601.html

相关文章

vue实现好看的相册、图片网站

目录 一、效果图 1.项目访问地址 2.画虫官方效果图&#xff1a; 3.作者实现的效果图&#xff1a; 二、代码实现 1.项目结构截图 2.路由配置代码&#xff1a; 3. 头部底部主页面内容显示容器的代码 4.首页&#xff0c;即标签页的代码 三、项目启动说明 四、总结 一、…

类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件

类似QQ空间&#xff0c;微信朋友圈&#xff0c;微博主页等&#xff0c;展示图片的九宫格控件&#xff0c;自动根据图片的数量确定图片大小和控件大小&#xff0c;使用Adapter模式设置图片&#xff0c;对外提供接口回调&#xff0c;使用接口加载图片,支持任意的图片加载框架,如 …

快速收集图片并批量重命名(在线收集表、QQ收集表、腾讯收集表+方方格子)(图片批量重命名)

很多人会遇到收集图片的问题&#xff0c;但是如果人数一多&#xff0c;工作量就特别大&#xff0c;每个图片重命名&#xff0c;会让你崩溃&#xff0c;因此很多人首先想到在线文档。不知道你们有没有测试过在线文档收集图片&#xff0c;效果真是惨不忍睹&#xff0c;别人的图片…

打开图片链接直接打开QQ对话框

<a href"tencent://message/?uin7530****&SiteQQ交谈&Menuyes" target"blank"> <img border"0" src"http://wpa.qq.com/pa?p1:7530*****:7" alt"有事Q我吧" width"71" height"2…

2.9 权限管理

文章目录 2.9 权限管理权限的概念常见的权限类型权限表示方法权限管理操作最小权限原则总结 2.9 权限管理 权限的概念 在计算机系统中&#xff0c;权限是指授予用户或用户组对特定资源或操作的访问权力。权限管理是确保合适的用户获得适当的访问权限&#xff0c;以保护系统的…

前端---HTML QQ空间主页制作

今天给大家讲解一个qq空间主页制作的讲解,我们先来看代码和示意图: 代码如下: <html> <head><title>QQ空间制作练习</title><meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /><link href"qqzon…

HTML制作qq空间主页

我们在前面已经做过一些简单的网页&#xff0c;接下来让我们做一些较为复杂的东西。 这就是我的空间主页了。 <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"> <title> 生…

redis数据类型和数据结构你了解吗 学习总结篇!

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 这期给大家讲一下关于 Redis 数据类型和数据结构的区别&#xff0c;很多读者包括笔者自己&#xff0c;早期也是傻傻分不清。备注&#xff1a;部分图片借鉴小林哥&…