English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
This article shares the specific code for the Android ListView pull-down top image enlargement with everyone for reference. The specific content is as follows
When checking the code of the master on git, I found that it is decompiled from others' code without comments, and the code is not completely compiled. Therefore, I have added simple comments here for learning purposes only.
Variable Description
The variable includes: custom return animation acceleration, custom animation thread, header image view, the final y coordinate, the prepared ratio, the enlarged ratio, and so on.
private static final String TAG = "PullToZoomListView"; private static final int INVALID_VALUE = -1;//Reset value //Custom acceleration animation 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;//Current finger ID private FrameLayout mHeaderContainer;//Header private int mHeaderHeight;//Header image height private ImageView mHeaderImage;//Header image float mLastMotionY = INVALID_VALUE;//Final y coordinate float mLastScale = INVALID_VALUE;//Final scale float mMaxScale = INVALID_VALUE;//Maximum scale private OnScrollListener mOnScrollListener;//Scroll listener private ScalingRunnalable mScalingRunnalable;//Animation thread private int mScreenHeight;//Screen height private ImageView mShadow;//Shadow mask
Custom View initialization: Sets the header and mask and sets the listener.
/** * Initialization * @param paramContext */ private void init(Context paramContext) { DisplayMetrics metrics = new DisplayMetrics(); ((Activity) paramContext).getWindowManager().getDefaultDisplay().getMetrics(metrics); this.mScreenHeight = metrics.heightPixels;//Assign screen height this.mHeaderContainer = new FrameLayout(paramContext);//Header this.mHeaderImage = new ImageView(paramContext);//Header image int screenWidth = metrics.widthPixels;//Screen width //Set the style of the header view Set screen width, the maximum style height is the height of the screen9/16 setHeaderViewSize(screenWidth, (int) (9.0F * (screenWidth / 16.0F))); this.mShadow = new ImageView(paramContext);//Mask FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); layoutParams.gravity = Gravity.CENTER; this.mShadow.setLayoutParams(layoutParams);//Set mask style //Add View to header this.mHeaderContainer.addView(this.mHeaderImage); this.mHeaderContainer.addView(this.mShadow); //Add header addHeaderView(this.mHeaderContainer); //Initialize return animation this.mScalingRunnalable = new ScalingRunnalable(); //Set listener super.setOnScrollListener(this); }
Start animation: Determine the bottom position of the current header layout - whether it is greater than the initial height of the image.
/** * Start animation */ private void endScraling() { if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) { Log.d(TAG, "this.mScalingRunnalable.startAnimation("200L)") this.mScalingRunnalable.startAnimation(200L); } }
Assign the first finger when multiple fingers touch.
/** * When multiple touches occur, press down, when the first finger is lifted, and then another finger is pressed down, the finger pointer of the pressed event is set as the current finger pointer * * @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); }
Reset all the data
/** * Reset all data */ private void reset() { this.mActivePointerId = INVALID_VALUE; this.mLastMotionY = INVALID_VALUE; this.mMaxScale = INVALID_VALUE; this.mLastScale = INVALID_VALUE; }
Modify the layout style when scrolling up
@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)) {//If it is a upward scroll int toUpScroll = (int) (0.65D * bottomSpacing); this.mHeaderImage.scrollTo(0, -toUpScroll); Log.d(TAG, "onScroll Upward scrolling 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); } }
Handle different events and modify the layout style
@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(); //Obtain the ID of the first finger pointer 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); //Get the current pointer of the phone with the specified id int pointer = motionEvent.findPointerIndex(this.mActivePointerId); //Check if the pointer is not null if (pointer == INVALID_VALUE) { Log.e(TAG, "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent"); } else { //If no value is assigned at the beginning, it needs to be assigned if (this.mLastMotionY == INVALID_VALUE) { this.mLastMotionY = motionEvent.getY(pointer); } if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) { //Get header style 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)) { //If the final ratio is less than the default and the current ratio is less than the last ratio, then modify the height of the header headerParams.height = this.mHeaderHeight; this.mHeaderContainer.setLayoutParams(headerParams); return super.onTouchEvent(motionEvent); } else { //Otherwise, assign the current ratio to the last ratio this.mLastScale = Math.min(Math.max(currentScale, 1.0F), this.mMaxScale); headerParams.height = ((int) (this.mHeaderHeight * this.mLastScale)); //Determine if the modified height is less than the screen height if (headerParams.height < this.mScreenHeight) { this.mHeaderContainer.setLayoutParams(headerParams); } //Record the last y-coordinate this.mLastMotionY = motionEvent.getY(pointer); return true; } } this.mLastMotionY = motionEvent.getY(pointer); } break; case MotionEvent.ACTION_UP: Log.d(TAG, "onTouchEvent ACTION_UP Reset"); //Reset reset(); //When the finger lifts, settle the stretch and judge whether the animation is enabled endScraling(); break; case MotionEvent.ACTION_CANCEL: int actionIndex = motionEvent.getActionIndex();//Get the current topmost pointer this.mLastMotionY = motionEvent.getY(actionIndex);//Get the last y-coordinate this.mActivePointerId = motionEvent.getPointerId(actionIndex);//Get the finger of the topmost pointer Log.d(TAG, "onTouchEvent ACTION_CANCEL actionIndex = ") + actionIndex + " mLastMotionY = " + mLastMotionY + " mActivePointerId = " + mActivePointerId); break; case MotionEvent.ACTION_POINTER_DOWN: //When the second finger is pressed or released, trigger this event 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: //When the second finger is pressed or released Log.d(TAG, "onTouchEvent_Po ACTION_POINTER_UP "); break; } return super.onTouchEvent(motionEvent); }
Animation when returning upwards
/** * Animation returning upwards */ class ScalingRunnalable implements Runnable { long mDuration;//Duration boolean mIsFinished = true;//Whether to end float mScale;//Ratio long mStartTime;//Start time ScalingRunnalable() { } /** * Stop animation */ public void abortAnimation() { this.mIsFinished = true; } /** * Whether to stop * * @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;//Header style //Determine whether to stop and if it has slid over the default size 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); } }
That's all for this article. I hope it will be helpful to everyone's learning and I also hope everyone will support the Naya Tutorial.
Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been edited by humans, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)