Android仿京东首页画轴效果

Posted 冷不冷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android仿京东首页画轴效果相关的知识,希望对你有一定的参考价值。

记得之前京东首页有一个效果,有一个画轴,然后可以滚动画轴,去打开画(不知道怎么去形容这个效果,就叫做画轴效果吧- -!),然后去做相关操作,刚开始看到这个效果,想法是动态的去改变一个ImageView的高度,基本效果也就出来了,不过滚动部分的内容,当时接触的也不是很多,只是看过一些大牛的博客,略微了解了一点,当时也忙着写项目,也就没去多想,前些天忽然想到这个效果,就想着实现一下,不过京东新版本好像去掉这个东西了,只能凭着自己的记忆来大概搞一下,也是对滑动这部分内容的一个小练习吧.

先看一下效果图:


一、需求分析

看到效果之后,先来分析一下:
首先是需要一个可以滑动的画轴,并且这个画轴需要一定的滑动空间,有滑动的效果,这个用Scroller帮助完成就可以了.
然后看一下画轴点击移动时候的背景图,是跟随画轴移动,动态改变高度的.这个用一个ImageView来搞定,设置ImageView的scaleType就可以了,不过这个地方有一些小问题,下面会说.

二、具体实现

简单分析完,来实现一下,先来做一下画轴.创建一个ScrollPaintView继承自RelativeLayout,因为需要一定的滑动空间,所以需要是一个ViewGroup.

ScrollPaintView的一些基本属性:


public class ScrollPaintView extends RelativeLayout 
    /**
     * TAG
     */
    private static final String TAG = "ScrollPaintView";
    /**
     * 默认滚轴高度
     */
    private final int DEFAULT_PAINT_SCROLL_HEIGHT = 25;
    /**
     * 默认滚动的速度
     */
    private final int DEFAULT_SCROLL_SPEED = 1000;
    /**
     * 默认分割点高度
     */
    private final int DEFAULT_PARTITION_NODE = 150;
    /**
     * 默认画轴文字大小
     */
    private final int DEFAULT_PAINT_SCROLL_TXT_SIZE = 16;
    /**
     * Scroller
     */
    private Scroller mScroller;
    /**
     * 滚轴Iv
     */
    private ImageView mPaintScrollImageView;
    /**
     * 滚轴Tv
     */
    private TextView mPaintScrollTextView;
    /**
     * 图画Iv
     */
    private ImageView mPaintView;
    /**
     * 画轴图
     */
    private Bitmap mPaintScrollBp;
    /**
     * 画轴高度
     */
    private int mPaintIvHeight;
    /**
     * 画轴文字
     */
    private String mPaintScrollTxt;
    /**
     * 画轴文字大小
     */
    private float mPaintScrollTxtSize;
    /**
     * 画轴文字颜色
     */
    private int mPaintScrollTxtColor;
    /**
     * 图画开始时的高度
     */
    private int mPaintStartHeight;
    /**
     * 上一次获取的Y
     */
    private int mLastY;
    /**
     * 滚动速度
     */
    private int mScrollSpeed;
    /**
     * 分隔节点
     */
    private int partitionNode;
    /**
     * 是否是向上滚动
     */
    private boolean isScrllerTop = false;
    /**
     * 是否正在点击
     */
    private boolean isClick = false;
    /**
     * 布局参数
     */
    private LayoutParams lpp;
    /**
     * 屏幕高度
     */
    private int screenHeight;
    /**
     * 屏幕宽度
     */
    private int screenWidth;
    /**
     * 回调监听
     */
    private ScrollPaintCompleteListener listener;
    /**
     * 上一次滚动的Y值
     */
    private int lastScrollY;

    /**
     * 构造方法
     */
    public ScrollPaintView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        // 获取属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrollPaintView);
        mPaintIvHeight = (int) ta.getDimension(R.styleable.ScrollPaintView_paintScrollHeight, TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, DEFAULT_PAINT_SCROLL_HEIGHT, getResources().getDisplayMetrics()));
        mScrollSpeed = ta.getInteger(R.styleable.ScrollPaintView_scrollSpeed, DEFAULT_SCROLL_SPEED);
        partitionNode = ta.getInteger(R.styleable.ScrollPaintView_scrollPartitionNode, DEFAULT_PARTITION_NODE);
        mPaintScrollBp = drawableToBitamp(ta.getDrawable(R.styleable.ScrollPaintView_paintScrollSrc));
        mPaintScrollTxt = ta.getString(R.styleable.ScrollPaintView_paintScrollTxt);
        mPaintScrollTxtColor = ta.getColor(R.styleable.ScrollPaintView_paintScrollTxtColor, Color.BLACK);
        mPaintScrollTxtSize = px2sp(ta.getDimensionPixelSize(R.styleable.ScrollPaintView_paintScrollTxtSize, DEFAULT_PAINT_SCROLL_TXT_SIZE));
        ta.recycle();
    

看一下创建画轴:

    /**
     * 创建滚轴
     */
    private void makePaintScroll() 
        // 如果已经存在,则不再创建
        if (null != mPaintScrollImageView || null != mPaintScrollTextView) 
            return;
        
        // 创建滚轴
        mPaintScrollImageView = new ImageView(getContext());
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        lp.height = mPaintIvHeight;
        mPaintScrollImageView.setLayoutParams(lp);
        mPaintScrollImageView.setScaleType(ImageView.ScaleType.FIT_XY);
        mPaintScrollImageView.setImageBitmap(null == mPaintScrollBp ? makeDefaultScroll() : mPaintScrollBp);
        addView(mPaintScrollImageView);
        // 创建文字
        mPaintScrollTextView = new TextView(getContext());
        LayoutParams lpt = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        lpt.height = mPaintIvHeight;
        mPaintScrollTextView.setLayoutParams(lpt);
        mPaintScrollTextView.setText(null == mPaintScrollTxt ? "" : mPaintScrollTxt);
        mPaintScrollTextView.setTextSize(mPaintScrollTxtSize);
        mPaintScrollTextView.setTextColor(mPaintScrollTxtColor);
        mPaintScrollTextView.setGravity(Gravity.CENTER);
        addView(mPaintScrollTextView);
    

    /**
     * 设置默认的滚轴
     *
     * @return
     */
    private Bitmap makeDefaultScroll() 
        Bitmap defaultBp = Bitmap.createBitmap(screenWidth, mPaintIvHeight,
                Bitmap.Config.ARGB_8888);
        //填充颜色
        defaultBp.eraseColor(Color.parseColor("#FF0000"));
        return defaultBp;

    

创建了一个画轴ImageView和一个文字TextView作为初始的画轴,如果没有传入画轴的图片,则默认去创建一个画轴.不难理解,接着去配合Scroller,让画轴滚动起来.

简单滚动:

    /**
     * 处理View
     */
    private void handleView() 
        // 初始化Scroller
        mScroller = new Scroller(getContext());
        // 画轴点击效果
        mPaintScrollImageView.setOnTouchListener(new OnTouchListener() 
            @Override
            public boolean onTouch(View view, MotionEvent event) 
                int x = (int) event.getRawX();
                int y = (int) event.getRawY();
                int action = event.getAction();
                switch (action) 
                     case MotionEvent.ACTION_DOWN: 
                        isClick = true;
                        mLastY = y;
                        if (!mScroller.isFinished())  // 如果上次的调用没有执行完就取消。
                            mScroller.abortAnimation();
                        
                     return true;
                     case MotionEvent.ACTION_MOVE: 
                        // 移动的距离
                        int dy = y - mLastY;
                        mLastY = y;
                        // 滑动
                        scrollBy(0, -dy);
                        return true;
                    case MotionEvent.ACTION_UP:
                      mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), 1000);
                        invalidate();
                        return true;
                
                return false;
            
        );
    

    /**
     * 滑动处理
     */
    @Override
    public void computeScroll() 
        if (mScroller.computeScrollOffset())   // 计算新位置,并判断上一个滚动是否完成。 
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
            
    

这样滑动处理就做完了,在布局中引用一下,看一下效果

activity_main:

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#eeeeee"
        tools:context="com.example.junweiliu.scrollpaintdemo.MainActivity">
            <!--画轴控件-->
  <com.example.junweiliu.scrollpaintdemo.widget.ScrollPaintView
            android:id="@+id/spv_paint"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:paintScrollHeight="25dp"
            app:paintScrollSrc="@mipmap/paint_scroll_img"
            app:paintScrollTxt="拉我看看"
            app:paintScrollTxtColor="#FF000000"
            app:paintScrollTxtSize="16sp"
    >
    </com.example.junweiliu.scrollpaintdemo.widget.ScrollPaintView>
</RelativeLayout>

效果图:

滚动条基本能用了,接着来设置一个ImageView来配合使用下,先获取到一个ImageView,然后拖动画轴时,来动态改变ImageView的高度,在做这部分内容前,先来考虑下,因为需求需要的是图画整体不会变形,是一点一点拉开的感觉,就像是打开一幅画一样,那么用哪种scaleType能满足呢.试了好多种,来分别看一下:

相关代码:
提供一个设置ImageView的方法
在MotionEvent.ACTION_MOVE:中动态改变ImageView的高度
滑动时动态改变ImageView的高度

    /**
     * 设置paintView
     *
     * @param paintView
     */
    public void setPaintView(ImageView paintView) 
        if (null == paintView) 
            Log.e(TAG, "设置的View为空");
            return;
        
        mPaintView = paintView;
    

MotionEvent.ACTION_MOVE:

   // 滑动处理   
   case MotionEvent.ACTION_MOVE: 
   ...
     // 动态改变高度
     if (Math.abs(getScrollY()) > 0) 
                            lpp.height = mPaintStartHeight + Math.abs(getScrollY());
                            mPaintView.setLayoutParams(lpp);
                        

滑动处理:

    /**
     * 滑动处理
     */
    @Override
    public void computeScroll() 
        if (mScroller.computeScrollOffset())   // 计算新位置,并判断上一个滚动是否完成。
            // 请求处理点击事件,防止父控件滑动
            requestDisallowInterceptTouchEvent(true);
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            // 重新设置显示的控件高度
            if (0 < Math.abs(mScroller.getCurrY())) 
                if (!isScrllerTop) 
                    lpp.height = mPaintStartHeight + Math.abs(mScroller.getCurrY()) + mPaintIvHeight / 2;
                 else 
                    lpp.height = mPaintStartHeight + Math.abs(mScroller.getCurrY()) - mPaintIvHeight / 2;
                
             else 
                lpp.height = mPaintStartHeight;
            
            mPaintView.setLayoutParams(lpp);
            invalidate();
        

设置不同scaleType效果,简单试几个效果:

fitXY:

centerCrop:

matrix:

观察一下,好像那个效果都不是很好,不过matrix拉开的效果和预期需要的是一样的,不过用matrix之后,就没有办法设置其他scaleType了,没办法把图片进行缩放了,这肿么办呢,其实很简单,只需要把显示的图片提前进行一下缩放就可以了.

处理图片相关:

    /**
     * 设置paintView
     *
     * @param paintView
     */
    public void setPaintView(ImageView paintView) 
        if (null == paintView) 
            Log.e(TAG, "设置的View为空");
            return;
        
        // 处理图片,对图片按照屏幕宽高比进行缩放
        Bitmap bp = drawableToBitamp(paintView.getDrawable());
        paintView.setImageBitmap(scaleBitmal(bp));
        // 设置缩放形式
        paintView.setScaleType(ImageView.ScaleType.MATRIX);
        mPaintView = paintView;
    
    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitamp(Drawable drawable) 
        if (null == drawable) 
            return null;
        
        if (drawable instanceof BitmapDrawable) 
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    


    /**
     * 按照屏幕宽高缩放图片
     *
     * @param bp
     * @return
     */
    private Bitmap scaleBitmal(Bitmap bp) 
        // 宽度比例
        float scaleW = (float) screenWidth / (float) bp.getWidth();
        // 高度比例
        float scaleH = (float) screenHeight / (float) bp.getHeight();
        // 矩阵,用于缩放图片
        Matrix matrix = new Matrix();
        matrix.postScale(scaleW, scaleH);
        // 缩放后的图片
        Bitmap scaleBp = Bitmap.createBitmap(bp, 0, 0, bp.getWidth(), bp.getHeight(), matrix, true);
        return scaleBp;
    

看一下效果:

效果还不错,接下来就是做一下细节上的处理,设置一个临界点,让画轴向上或者向下滚动,设置边界点,让画轴不越界等等.还有一般都是在ScrollView中使用到这个效果,所以要去处理一下事件冲突,当然还有去重写一下onMeasure方法,不然会出现不显示的情况.(相信大家也遇到过ScrollView里边嵌套ListView导致ListView显示不全)这里就不再详细介绍了.直接贴下代码.


三、完整代码

MainActivity:

package com.example.junweiliu.scrollpaintdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.junweiliu.scrollpaintdemo.widget.ScrollPaintView;

public class MainActivity extends AppCompatActivity 
    private static final String TAG = "MainActivity";
    /**
     * 需要显示的IV
     */
    private ImageView mPaintIv;
    /**
     * 画轴
     */
    private ScrollPaintView mScrollPaintView;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

    

    /**
     * 初始化控件
     */
    private void initView() 
        mPaintIv = (ImageView) findViewById(R.id.iv_paint);
        mScrollPaintView = (ScrollPaintView) findViewById(R.id.spv_paint);
        mScrollPaintView.setPaintView(mPaintIv);
        mPaintIv.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Toast.makeText(MainActivity.this, "功夫!", Toast.LENGTH_SHORT).show();
            
        );
        mScrollPaintView.setScrollPaintCompleteListener(new ScrollPaintView.ScrollPaintCompleteListener() 
            @Override
            public void onScrollTouch(TextView tv) 
                mPaintIv.setVisibility(View.VISIBLE);
            

            @Override
            public void onScrollTop(TextView tv) 
//                Log.e(TAG, "收缩了");
                tv.setText("拉我看看");
                if (View.VISIBLE == mPaintIv.getVisibility()) 
                    mPaintIv.setVisibility(View.GONE);
                
            

            @Override
            public void onScrollBottom(TextView tv) 
//                Log.e(TAG, "展开了");
                tv.setText("到底了");
                Intent intent = new Intent(MainActivity.this, DetailActivity.class);
                startActivity(intent);
                // 延迟800毫秒重置位置
                new Handler(new Handler.Callback() 
                    @Override
                    public boolean handleMessage(Message message) 
                        mScrollPaintView.replaceScroll();
                        return false;
                    
                ).sendEmptyMessageDelayed(0, 600);
            

            @Override
            public void onScrollMove(TextView tv) 
//                Log.e(TAG, "移动了");
                tv.setText("请上下拖动");
            
        );
    

ScrollPaintView:

package com.example.junweiliu.scrollpaintdemo.widget;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

import com.example.junweiliu.scrollpaintdemo.R;

/**
 * Created by junweiliu on 16/12/10.
 */
public class ScrollPaintView extends RelativeLayout 
    /**
     * TAG
     */
    private static final String TAG = "ScrollPaintView";
    /**
     * 默认滚轴高度
     */
    private final int DEFAULT_PAINT_SCROLL_HEIGHT = 25;
    /**
     * 默认滚动的速度
     */
    private final int DEFAULT_SCROLL_SPEED = 1000;
    /**
     * 默认分割点高度
     */
    private final int DEFAULT_PARTITION_NODE = 150;
    /**
     * 默认画轴文字大小
     */
    private final int DEFAULT_PAINT_SCROLL_TXT_SIZE = 16;
    /**
     * Scroller
     */
    private Scroller mScroller;
    /**
     * 滚轴Iv
     */
    private ImageView mPaintScrollImageView;
    /**
     * 滚轴Tv
     */
    private TextView mPaintScrollTextView;
    /**
     * 图画Iv
     */
    private ImageView mPaintView;
    /**
     * 画轴图
     */
    private Bitmap mPaintScrollBp;
    /**
     * 画轴高度
     */
    private int mPaintIvHeight;
    /**
     * 画轴文字
     */
    private String mPaintScrollTxt;
    /**
     * 画轴文字大小
     */
    private float mPaintScrollTxtSize;
    /**
     * 画轴文字颜色
     */
    private int mPaintScrollTxtColor;
    /**
     * 图画开始时的高度
     */
    private int mPaintStartHeight;
    /**
     * 上一次获取的Y
     */
    private int mLastY;
    /**
     * 滚动速度
     */
    private int mScrollSpeed;
    /**
     * 分隔节点
     */
    private int partitionNode;
    /**
     * 是否是向上滚动
     */
    private boolean isScrllerTop = false;
    /**
     * 是否正在点击
     */
    private boolean isClick = false;
    /**
     * 布局参数
     */
    private LayoutParams lpp;
    /**
     * 屏幕高度
     */
    private int screenHeight;
    /**
     * 屏幕宽度
     */
    private int screenWidth;
    /**
     * 回调监听
     */
    private ScrollPaintCompleteListener listener;
    /**
     * 上一次滚动的Y值
     */
    private int lastScrollY;

    /**
     * 回调接口
     */
    public interface ScrollPaintCompleteListener 
        /**
         * 点击时的回调
         */
        public void onScrollTouch(TextView tv);

        /**
         * 收缩时的回调
         */
        public void onScrollTop(TextView tv);

        /**
         * 展开时的回调
         */
        public void onScrollBottom(TextView tv);

        /**
         * 滚动中的回调
         */
        public void onScrollMove(TextView tv);

    


    public ScrollPaintView(Context context) 
        this(context, null);
    

    public ScrollPaintView(Context context, AttributeSet attrs) 
        this(context, attrs, 0);
    

    public ScrollPaintView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        // 获取属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ScrollPaintView);
        mPaintIvHeight = (int) ta.getDimension(R.styleable.ScrollPaintView_paintScrollHeight, TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, DEFAULT_PAINT_SCROLL_HEIGHT, getResources().getDisplayMetrics()));
        mScrollSpeed = ta.getInteger(R.styleable.ScrollPaintView_scrollSpeed, DEFAULT_SCROLL_SPEED);
        partitionNode = ta.getInteger(R.styleable.ScrollPaintView_scrollPartitionNode, DEFAULT_PARTITION_NODE);
        mPaintScrollBp = drawableToBitamp(ta.getDrawable(R.styleable.ScrollPaintView_paintScrollSrc));
        mPaintScrollTxt = ta.getString(R.styleable.ScrollPaintView_paintScrollTxt);
        mPaintScrollTxtColor = ta.getColor(R.styleable.ScrollPaintView_paintScrollTxtColor, Color.BLACK);
        mPaintScrollTxtSize = px2sp(ta.getDimensionPixelSize(R.styleable.ScrollPaintView_paintScrollTxtSize, DEFAULT_PAINT_SCROLL_TXT_SIZE));
        ta.recycle();
        init();
        makePaintScroll();
        handleView();
    

    /**
     * 设置paintView
     *
     * @param paintView
     */
    public void setPaintView(ImageView paintView) 
        if (null == paintView) 
            Log.e(TAG, "设置的View为空");
            return;
        
        // 处理图片,对图片按照屏幕宽高比进行缩放
        Bitmap bp = drawableToBitamp(paintView.getDrawable());
        paintView.setImageBitmap(scaleBitmal(bp));
        // 设置缩放形式
        paintView.setScaleType(ImageView.ScaleType.MATRIX);
        mPaintView = paintView;
    

    /**
     * 设置回调
     */
    public void setScrollPaintCompleteListener(ScrollPaintCompleteListener listener) 
        if (null != listener) 
            this.listener = listener;
        
    

    /**
     * 初始化
     */
    private void init() 
        mScroller = new Scroller(getContext());
        lpp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        // 获取屏幕信息
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay()
                .getMetrics(displayMetrics);
        // 屏幕高度
        screenHeight = displayMetrics.heightPixels;
        // 屏幕宽度
        screenWidth = displayMetrics.widthPixels;
    


    /**
     * 创建滚轴
     */
    private void makePaintScroll() 
        // 如果已经存在,则不再创建
        if (null != mPaintScrollImageView || null != mPaintScrollTextView) 
            return;
        
        // 创建滚轴
        mPaintScrollImageView = new ImageView(getContext());
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        lp.height = mPaintIvHeight;
        mPaintScrollImageView.setLayoutParams(lp);
        mPaintScrollImageView.setScaleType(ImageView.ScaleType.FIT_XY);
        mPaintScrollImageView.setImageBitmap(null == mPaintScrollBp ? makeDefaultScroll() : mPaintScrollBp);
        addView(mPaintScrollImageView);
        // 创建文字
        mPaintScrollTextView = new TextView(getContext());
        LayoutParams lpt = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        lpt.height = mPaintIvHeight;
        mPaintScrollTextView.setLayoutParams(lpt);
        mPaintScrollTextView.setText(null == mPaintScrollTxt ? "" : mPaintScrollTxt);
        mPaintScrollTextView.setTextSize(mPaintScrollTxtSize);
        mPaintScrollTextView.setTextColor(mPaintScrollTxtColor);
        mPaintScrollTextView.setGravity(Gravity.CENTER);
        addView(mPaintScrollTextView);
    


    /**
     * 测量方法
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (null != mPaintView && getTop() + mPaintIvHeight != mPaintView.getHeight()) 
            // 重新设置图画高度
            mPaintStartHeight = getTop() + mPaintIvHeight / 2;
            lpp.height = mPaintStartHeight;
            mPaintView.setLayoutParams(lpp);
        
        // 测量状态栏高度
        Rect frame = new Rect();
        ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;
        // 高度为屏幕高度减去状态栏高度和top的高度
        setMeasuredDimension(screenWidth, screenHeight - getTop() - statusBarHeight);
    

    /**
     * 处理View
     */
    private void handleView() 
        mPaintScrollImageView.setOnTouchListener(new OnTouchListener() 
            @Override
            public boolean onTouch(View view, MotionEvent event) 
                if (null == mPaintView) 
                    Log.e(TAG, "设置的View为空");
                    return true;
                
                // 获取点击的XY坐标
                int x = (int) event.getRawX();
                int y = (int) event.getRawY();
                int action = event.getAction();
                switch (action) 
                    case MotionEvent.ACTION_DOWN: 
                        // 请求处理点击事件
                        requestDisallowInterceptTouchEvent(true);
                        isClick = true;
                        mLastY = y;
                        if (!mScroller.isFinished())  // 如果上次的调用没有执行完就取消。
                            mScroller.abortAnimation();
                        
                        if (null != listener) 
                            listener.onScrollTouch(mPaintScrollTextView);
                        
                        return true;
                    
                    case MotionEvent.ACTION_MOVE: 
                        // 移动的距离
                        int dy = y - mLastY;
                        mLastY = y;
                        // 滑动
                        scrollBy(0, -dy);
                        //  如果是向上滑动并且是在初始位置,则不去做处理
                        if (getScrollY() >= 0 && dy <= 0) 
                            lpp.height = mPaintStartHeight;
                            mPaintView.setLayoutParams(lpp);
                            scrollTo(0, 0);
                            return true;
                        
                        // 如果是向下滑动并且超过屏幕高度,则不去处理
                        if (Math.abs(getScrollY()) >= getHeight() - mPaintIvHeight && dy >= 0) 
                            lpp.height = mPaintStartHeight + getHeight() - mPaintIvHeight;
                            mPaintView.setLayoutParams(lpp);
                            scrollTo(0, -(getHeight() - mPaintIvHeight));
                            return true;
                        
                        // 滚动回调
                        if (null != listener) 
                            listener.onScrollMove(mPaintScrollTextView);
                        
                        // 重新设置显示的控件高度
                        if (Math.abs(getScrollY()) > 0) 
                            lpp.height = mPaintStartHeight + Math.abs(getScrollY());
                            mPaintView.setLayoutParams(lpp);
                        
                        return true;
                    
                    case MotionEvent.ACTION_UP:
                        // 恢复事件处理
                        requestDisallowInterceptTouchEvent(false);
                        isClick = false;
                        // 没有发生移动
                        if (getScrollY() >= 0) 
                            if (null != listener) 
                                listener.onScrollTop(mPaintScrollTextView);
                            
                            return true;
                        
                        if (-getScrollY() < partitionNode)    // 如果小于临界值,则返回起始坐标
                            // XY都从滑动的距离回去,最后一个参数是多少毫秒内执行完这个动作。
                            isScrllerTop = true;
                            mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), mScrollSpeed);
                         else     // 如果大于临界值,则展开
                            isScrllerTop = false;
                            mScroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -(getHeight() - (-getScrollY()) - mPaintIvHeight), mScrollSpeed);
                        
                        invalidate();
                        return true;
                
                return false;
            
        );
    


    /**
     * 滑动处理
     */
    @Override
    public void computeScroll() 
        if (mScroller.computeScrollOffset())   // 计算新位置,并判断上一个滚动是否完成。
            // 请求处理点击事件,防止父控件滑动
            requestDisallowInterceptTouchEvent(true);
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            // 重新设置显示的控件高度
            if (0 < Math.abs(mScroller.getCurrY())) 
                if (!isScrllerTop) 
                    lpp.height = mPaintStartHeight + Math.abs(mScroller.getCurrY()) + mPaintIvHeight / 2;
                 else 
                    lpp.height = mPaintStartHeight + Math.abs(mScroller.getCurrY()) - mPaintIvHeight / 2;
                
             else 
                lpp.height = mPaintStartHeight;
            
            mPaintView.setLayoutParams(lpp);
            invalidate();
         else 
            // 重新设置画图高度,防止高度异常
            if (mPaintView.getHeight() > mPaintStartHeight + Math.abs(mScroller.getCurrY()) && !isScrllerTop && mScroller.getStartY() > 0) 
                lpp.height = mPaintStartHeight + Math.abs(mScroller.getCurrY());
                mPaintView.setLayoutParams(lpp);
            
        
        // 防止多次调用
        if (lastScrollY != mScroller.getCurrY()) 
            // 收缩完成
            if (mScroller.getCurrY() >= 0 && !isClick) 
                if (null != listener) 
                    listener.onScrollTop(mPaintScrollTextView);
                
            
            // 展开完成
            if (-mScroller.getCurrY() >= getHeight() - mPaintIvHeight && !isClick) 
                if (null != listener) 
                    listener.onScrollBottom(mPaintScrollTextView);
                
            
            lastScrollY = mScroller.getCurrY();
        
    

    /**
     * 重置滚动
     */
    public void replaceScroll() 
        // 重置信息
        scrollTo(0, 0);
        mScroller.setFinalY(0);
        lastScrollY = 0;
        lpp.height = mPaintStartHeight;
        mPaintView.setLayoutParams(lpp);
        if (null != listener) 
            listener.onScrollTop(mPaintScrollTextView);
        
    

    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitamp(Drawable drawable) 
        if (null == drawable) 
            return null;
        
        if (drawable instanceof BitmapDrawable) 
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    


    /**
     * 按照屏幕宽高缩放图片
     *
     * @param bp
     * @return
     */
    private Bitmap scaleBitmal(Bitmap bp) 
        // 宽度比例
        float scaleW = (float) screenWidth / (float) bp.getWidth();
        // 高度比例
        float scaleH = (float) screenHeight / (float) bp.getHeight();
        // 矩阵,用于缩放图片
        Matrix matrix = new Matrix();
        matrix.postScale(scaleW, scaleH);
        // 缩放后的图片
        Bitmap scaleBp = Bitmap.createBitmap(bp, 0, 0, bp.getWidth(), bp.getHeight(), matrix, true);
        return scaleBp;
    

    /**
     * 设置默认的滚轴
     *
     * @return
     */
    private Bitmap makeDefaultScroll() 
        Bitmap defaultBp = Bitmap.createBitmap(screenWidth, mPaintIvHeight,
                Bitmap.Config.ARGB_8888);
        //填充颜色
        defaultBp.eraseColor(Color.parseColor("#FF0000"));
        return defaultBp;

    


    /**
     * 将px值转换为sp值,保证文字大小不变
     */
    public int px2sp(float pxValue) 
        final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    


activity_main:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#eeeeee"
        tools:context="com.example.junweiliu.scrollpaintdemo.MainActivity">
    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <RelativeLayout android:layout_width="match_parent"
                        android:layout_height="match_parent">
            <!--头部图片部分-->
            <ImageView
                    android:id="@+id/iv_banner"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:scaleType="fitXY"
                    android:src="@mipmap/show_banner"/>
            以上是关于Android仿京东首页画轴效果的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法动态改变每帧的情节动画轴比例?

Android仿京东首页轮播文字(又名垂直跑马灯)

Android仿京东首页轮播文字(又名垂直跑马灯)

高仿京东 2020 版首页布局及刷新效果

高仿京东 2020 版首页布局及刷新效果

高仿京东 2020 版首页布局及刷新效果