English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android ScrollView Implementation for下拉放大 Top Header Image

Introduction

When I was working on a project before, I needed to implement a ScrollView effect similar to the Weibo personal homepage, where the top image is zoomed when you continue to pull down at the top. Then I found a related implementation on the Internet, which has a very good effect and the code is concise and easy to understand. (Link: Custom scrollView to implement top image zoom when pulling down at the top), so here I just made a few modifications based on it, such as controlling the image to be centered in the code, adding dynamic setting of the zoomed control widget, using a custom maximum zoom factor, etc., which are all simple modifications, and also added a sliding listener callback (required for the project).

The effect is as follows:

Idea

As usual, let's talk about the idea first, because the idea is the most important. The specific steps are as follows:
1. Obtain the control widget to be zoomed and get its width and height;
2. Continue to pull down at the top, and change the width and height of the widget through LayoutParams;
3. Initialize various parameters when the finger is lifted, and use property animation to rebound the control widget.

Implementation

Directly look at the code

public class HeadZoomScrollView extends ScrollView {
 public HeadZoomScrollView(Context context) {
  super(context);
 }
 public HeadZoomScrollView(Context context, AttributeSet attrs) {
  super(context, attrs);
 }
 public HeadZoomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
 }
 // Used to record the scroll position
 private float y = 0f;
 // The original width and height of zoomView
 private int zoomViewWidth = 0;
 private int zoomViewHeight = 0;
 // Whether the view is currently being magnified
 private boolean mScaling = false;
 // The view to be magnified, defaulting to the first child view
 private View zoomView;
 public void setZoomView(View zoomView) {
  this.zoomView = zoomView;
 }
 // Sliding magnification coefficient, the larger the coefficient, the greater the magnification during sliding
 private float mScaleRatio = 0.4f;
 public void setmScaleRatio(float mScaleRatio) {
  this.mScaleRatio = mScaleRatio;
 }
 // The maximum magnification factor
 private float mScaleTimes = 2f;
 public void setmScaleTimes(int mScaleTimes) {
  this.mScaleTimes = mScaleTimes;
 }
 // Rebound time coefficient, the smaller the coefficient, the faster the rebound
 private float mReplyRatio = 0.5f;
 public void setmReplyRatio(float mReplyRatio) {
  this.mReplyRatio = mReplyRatio;
 }
 @Override
 protected void onFinishInflate() {
  super.onFinishInflate();
//  Prevent excessive scrolling, otherwise there will be some blank areas after scrolling up and down
  setOverScrollMode(OVER_SCROLL_NEVER);
//  Obtain the default first view
  if (getChildAt(0) != null && getChildAt(0) instanceof ViewGroup && zoomView == null) {
   ViewGroup vg = (ViewGroup) getChildAt(0);
   if (vg.getChildCount() > 0) {
    zoomView = vg.getChildAt(0);
   }
  }
 }
 @Override
 public boolean onTouchEvent(MotionEvent ev) {
  if (zoomViewWidth <= 0 || zoomViewHeight <= 0) {
   zoomViewWidth = zoomView.getMeasuredWidth();
   zoomViewHeight = zoomView.getMeasuredHeight();
  }
  if (zoomView == null || zoomViewWidth <= 0 || zoomViewHeight <= 0) {
   return super.onTouchEvent(ev);
  }
  switch (ev.getAction()) {
   case MotionEvent.ACTION_MOVE:
    if (!mScaling) {
     if (getScrollY() == 0) {
      y = ev.getY();//Record position when sliding to the top
     }
      break;
     }
    }
    int distance = (int) ((ev.getY() - y)*mScaleRatio);
    if (distance < 0) break;//If sliding down
    mScaling = true;
    setZoom(distance);
    return true;
   case MotionEvent.ACTION_UP:
    mScaling = false;
    replyView();
    break;
  }
  return super.onTouchEvent(ev);
 }
 /**Zoom view*/
 private void setZoom(float s) {
  float scaleTimes = (float) ((zoomViewWidth+s)/(zoomViewWidth*1.0));
//  If it exceeds the maximum zoom factor, return directly
  if (scaleTimes > mScaleTimes) return;
  ViewGroup.LayoutParams layoutParams = zoomView.getLayoutParams();
  layoutParams.width = (int) (zoomViewWidth + s);
  layoutParams.height = (int)(zoomViewHeight*((zoomViewWidth+s)/);
//  set control horizontal center
  ((MarginLayoutParams) layoutParams).setMargins(-(layoutParams.width) - zoomViewWidth) / 2, 0, 0, 0);
  zoomView.setLayoutParams(layoutParams);
 }
 /**Rebound*/
 private void replyView() {
  final float distance = zoomView.getMeasuredWidth() - zoomViewWidth;
  // Set Animation
  ValueAnimator anim = ObjectAnimator.ofFloat(distance, 0.0F).setDuration((long) (distance * mReplyRatio));
  anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    setZoom((Float) animation.getAnimatedValue());
   }
  });
  anim.start();
 }
 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
  super.onScrollChanged(l, t, oldl, oldt);
  if (onScrollListener != null) onScrollListener.onScroll(l, t, oldl, oldt);
 }
 private OnScrollListener onScrollListener;
 public void setOnScrollListener(OnScrollListener onScrollListener) {
  this.onScrollListener = onScrollListener;
 }
 /**Scrolling Listener*/
 public interface OnScrollListener{
  void onScroll(int scrollX, int scrollY, int oldScrollX, int oldScrollY);
 }
}

You can see that in the onTouchEvent method, first judge whether the current state is zoomed in. If not, record the position of the touch event at the top. Of course, it is also possible to write it in the ACTION_DOWN event, and do not process if it is not at the top.

After that, calculate the sliding distance. If it is sliding down, do not process it. It should be noted that this distance refers to the distance between the current position and the ACTION_DOWN action at the beginning, so when this distance is less than 0, it is 'not zoomed in && sliding down'. At this time, the ScrollView should be scrolled. Well, no problem. When the distance is not less than 0, start to zoom in on the control. You can see that the setZoom method is called. Note that in fact, the control's pull-down zooming, pull-up recovery, and rebound are all done here. Rebound actually also calls this method.

Rebound when lifting your hand, which does not need to be said.

The code is relatively simple overall. If you need other implementations, it can also be easily added, for example, when we need to zoom in on an image like Weibo, how to refresh the data when we release our hands? You can completely do this in
Add a callback interface in onTouchEvent, and then implement the specific logic externally.

Usage

You can use it directly like a normal ScollView, so there is no need to elaborate on this.

Source Code:Download Address

That's all for this article. I hope it will be helpful to everyone's learning, and I also hope everyone will support the Yelling Tutorial more.

Declaration: 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, does not edit the content manually, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to notice#w3Please send an email to codebox.com (replace # with @ when sending an email) to report any infringement, and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.

You May Also Like