English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
L'article suivant partage avec vous le code spécifique de l'animation de l'image en haut du ListView d'Android, pour votre référence, le contenu spécifique est le suivant
En regardant le code des experts sur git, j'ai découvert qu'il s'agit de la décompilation du code d'autrui, sans commentaires ajoutés, et le code n'est pas complètement compilé. Par conséquent, j'ai ajouté des commentaires simples ici, à usage exclusif de l'apprentissage.
Explication des variables
Ici, la variable contient : l'accélération d'animation personnalisée, le thread d'animation personnalisé, la vue d'image de l'en-tête, l'abscisse y finale, le ratio terminé, le ratio agrandi, etc.
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;//En-tête private int mHeaderHeight;//头部图片的高度 private ImageView mHeaderImage;//Image de l'en-tête 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;//Affecter la hauteur de l'écran this.mHeaderContainer = new FrameLayout(paramContext);//En-tête this.mHeaderImage = new ImageView(paramContext);//Image de l'en-tête int screenWidth = metrics.widthPixels;//Largeur de l'écran //Définir le style de la View d'en-tête, la largeur de l'écran, la hauteur maximale du style est la hauteur de l'écran9/16 setHeaderViewSize(screenWidth, (int) (9.0F * (screenWidth / 16.0F))); this.mShadow = new ImageView(paramContext);//Masque FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); layoutParams.gravity = Gravity.CENTER; this.mShadow.setLayoutParams(layoutParams);//Définir le style de masque //Ajouter des View à l'en-tête this.mHeaderContainer.addView(this.mHeaderImage); this.mHeaderContainer.addView(this.mShadow); //Ajouter l'en-tête addHeaderView(this.mHeaderContainer); //Initialiser le retour de l'animation this.mScalingRunnalable = new ScalingRunnalable(); //Définir l'écouteur super.setOnScrollListener(this); }
Démarrer l'animation : vérifier la position du bas du layout actuel de l'en-tête - est-ce que cela dépasse la hauteur initiale de l'image.
/** * Démarrer l'animation */ private void endScraling() { if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) { Log.d(TAG, "this.mScalingRunnalable.startAnimation("200L)") this.mScalingRunnalable.startAnimation(200L); } }
Lors d'un toucher multipoint, assigner la valeur du doigt 0.
/** * Lors d'un toucher multipoint, appuyer, lorsque le doigt 0 est relevé, puis à nouveau appuyé, utiliser l'identifiant du doigt appuyé comme identifiant de doigt actuel * * @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); }
Réinitialiser toutes les données
/** * Réinitialiser toutes les données */ private void reset() { this.mActivePointerId = INVALID_VALUE; this.mLastMotionY = INVALID_VALUE; this.mMaxScale = INVALID_VALUE; this.mLastScale = INVALID_VALUE; }
Quand on fait défiler vers le haut, modifier le style de mise en page
@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)) {//Si c'est un défilement vers le haut int toUpScroll = (int) (0.65D * bottomSpacing); this.mHeaderImage.scrollTo(0, -toUpScroll); Log.d(TAG, "onScroll Défilement vers le haut 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); } }
Traitement des événements différents, modification du style du layout
@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(); //Obtenir l'ID du premier doigt du pointeur 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: //Lorsque le deuxième doigt est appuyé ou relâché, déclencher cet événement 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: //Lorsque le deuxième doigt est appuyé ou relâché Log.d(TAG, "onTouchEvent_Po ACTION_POINTER_UP "); break; } return super.onTouchEvent(motionEvent); }
Animation lors du retour vers le haut
/** * Animation de retour vers le haut */ class ScalingRunnalable implements Runnable { long mDuration;//Durée boolean mIsFinished = true;//Si terminé float mScale;//Rapport long mStartTime;//Heure de début ScalingRunnalable() { } /** * Arrêter l'animation */ public void abortAnimation() { this.mIsFinished = true; } /** * Arrêt * * @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;//Style d'en-tête //Déterminer si l'animation doit être arrêtée et si elle a glissé au-delà de la taille par défaut 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); } }
C'est tout pour cet article, j'espère que cela pourra aider à votre apprentissage, et j'espère que vous soutiendrez également le tutoriel criant.
Déclaration : le contenu de cet article est issu du réseau, et les droits d'auteur appartiennent à leurs auteurs respectifs. Le contenu est apporté par les utilisateurs d'Internet de manière spontanée et est téléversé par eux-mêmes. Ce site n'appartient pas à la propriété, n'a pas été édité par l'homme, et n'assume pas la responsabilité des responsabilités juridiques pertinentes. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournissez des preuves pertinentes. Une fois confirmé, ce site supprimera immédiatement le contenu suspect de violation de droits d'auteur.)