Android基础控件——ImageView的自定义,再次利用Matrix完美模仿小红书长图自动滚动效果

前言

当我们阅读了ImageView源码后,发现Matrix的使用真的是很强大,几乎可以实现我们很多该有的功能,当我第一次看到这个效果的时候,第一想法就是ImageView的Matrix。通过比对了网上很多方案后,网上的方案还是比较复杂,如果我们巧用Matrix去做效果时,会发现其实代码也就100行左右就完美实现了效果,而且性能方面很不错,由于Gif图效果不佳,建议用代码跑一遍

效果展示

在这里插入图片描述

实现思路

我们的思想思路就是将两面相同的长图拼接成一张长图,通过平移播放这张长图,当我们的长图滑动到第二面图片的顶部时候,此时屏幕的顶部刚好和第二面图顶部相连接,这时候,我们马上将图片移到第一面,通过无缝的快速移动,骗过眼睛,实现我们自动滚动的效果,此时又重新从第一面开始滚动,如此循环就形成了自动滚动效果

准备工作

  • 准备好一张长图
  • 将两张同样的长图通过工具合成一张长图
  • 开始撸代码

合成前的长图

在这里插入图片描述

合成后的长图

在这里插入图片描述

实现步骤

1、快速使用

在xml直接配置的形式即可

<com.remo.mobile.framework.widget.ScrollerImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:autoScroller="true"
    app:scrollerImage="@drawable/login_bg_scroller_picture" />

2、自定义属性

<resources>
    <declare-styleable name="ScrollerImageView">
        <attr name="scrollerImage" format="reference" />
        <attr name="autoScroller" format="boolean" />
    </declare-styleable>
</resources>

3、定义属性

public class ScrollerImageView extends AppCompatImageView {

    private Bitmap mBitmap; //当前滚动的图片
    private Boolean autoScroller; //是否自动滚动
    
    private int speed = 10; //当前滚动的速度
    private float scale = 1f; //当前图片需要放大的比例
    private double scrollerY = 0f; //当前滚动的Y坐标
    private double onePictureHeight = 0; //一张图片的高度
    
    public ScrollerImageView(Context context) {
        this(context, null);
    }
    
    public ScrollerImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public ScrollerImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initTypeArray(context, attrs);
    }
}

4、获取自定义属性

private void initTypeArray(Context context, AttributeSet attrs) {
    final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScrollerImageView);
    int resId = typedArray.getResourceId(R.styleable.ScrollerImageView_scrollerImage, -1);
    autoScroller = typedArray.getBoolean(R.styleable.ScrollerImageView_autoScroller, false);
    mBitmap = BitmapFactory.decodeResource(context.getResources(), resId);
    typedArray.recycle();
}

5、更新尺寸和自动播放

onSizeChanged回调中,去更新我们的界面和开始滚动播放,由于我们的图片不一定是宽度充满整个屏幕,所以我们需要先缩放整个图片,通过updateCropMatrix缩放,接着通过runnable一直平移

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    updateCropMatrix();
    if (autoScroller) {
        startScroller();
    }
}

private void updateCropMatrix() {
    if (mBitmap == null) return;
    setImageBitmap(mBitmap);

    setScaleType(ScaleType.MATRIX);
    Matrix mMatrix = new Matrix();

    final int vHeight = getHeight() - getPaddingLeft() - getPaddingRight();//获取真实高度
    final int vWidth = getWidth() - getPaddingTop() - getPaddingBottom();//获取真实宽度
    final int dWidth = mBitmap.getWidth();
    final int dHeight = mBitmap.getHeight();

    scale = (float) vWidth / (float) dWidth;
    onePictureHeight = (mBitmap.getHeight() * scale) / 2; //获取一面图片的高度

    mMatrix.setScale(scale, scale); //先将图片宽缩放到View的同等大小
    setImageMatrix(mMatrix);
}

private Runnable scrollerRunnable = new Runnable() {
    @Override
    public void run() {
        Matrix matrix = getImageMatrix();
        if (scrollerY >= onePictureHeight) { //当前刚好到第二面顶部,里面平移回去到第一面
            matrix.postTranslate(0, (float) onePictureHeight);
            scrollerY = 0;
        } else {
            matrix.postTranslate(0, -1);
            scrollerY++;
        }
        setImageMatrix(matrix);
        invalidate();
        getHandler().postDelayed(this, speed);
    }
};

private void startScroller() {
    removeCallbacks(scrollerRunnable);
    postDelayed(scrollerRunnable, speed);
}

6、释放内存

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    stopScroller();
    mBitmap.recycle();
    mBitmap = null;
}

7、源码

public class ScrollerImageView extends AppCompatImageView {

    private Bitmap mBitmap; //当前滚动的图片
    private Boolean autoScroller; //是否自动滚动

    private int speed = 10; //当前滚动的速度
    private float scale = 1f; //当前图片需要放大的比例
    private double scrollerY = 0f; //当前滚动的Y坐标
    private double onePictureHeight = 0; //一张图片的高度

    public ScrollerImageView(Context context) {
        this(context, null);
    }

    public ScrollerImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollerImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initTypeArray(context, attrs);
    }

    private void initTypeArray(Context context, AttributeSet attrs) {
        final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScrollerImageView);
        int resId = typedArray.getResourceId(R.styleable.ScrollerImageView_scrollerImage, -1);
        autoScroller = typedArray.getBoolean(R.styleable.ScrollerImageView_autoScroller, false);
        mBitmap = BitmapFactory.decodeResource(context.getResources(), resId);
        typedArray.recycle();
    }

    private void updateCropMatrix() {
        if (mBitmap == null) return;
        setImageBitmap(mBitmap);

        setScaleType(ScaleType.MATRIX);
        Matrix mMatrix = new Matrix();

        final int vHeight = getHeight() - getPaddingLeft() - getPaddingRight();//获取真实高度
        final int vWidth = getWidth() - getPaddingTop() - getPaddingBottom();//获取真实宽度
        final int dWidth = mBitmap.getWidth();
        final int dHeight = mBitmap.getHeight();

        scale = (float) vWidth / (float) dWidth;
        onePictureHeight = (mBitmap.getHeight() * scale) / 2;

        mMatrix.setScale(scale, scale); //先将图片宽缩放到View的同等大小
        setImageMatrix(mMatrix);
    }

    private Runnable scrollerRunnable = new Runnable() {
        @Override
        public void run() {
            Matrix matrix = getImageMatrix();
            if (scrollerY >= onePictureHeight) {
                matrix.postTranslate(0, (float) onePictureHeight);
                scrollerY = 0;
            } else {
                matrix.postTranslate(0, -1);
                scrollerY++;
            }
            setImageMatrix(matrix);
            invalidate();
            getHandler().postDelayed(this, speed);
        }
    };

    private void startScroller() {
        removeCallbacks(scrollerRunnable);
        postDelayed(scrollerRunnable, speed);
    }

    private void stopScroller() {
        removeCallbacks(scrollerRunnable);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopScroller();
        mBitmap.recycle();
        mBitmap = null;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        updateCropMatrix();
        if (autoScroller) {
            startScroller();
        }
    }
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
要实现这个要求,你可以自定义一个继承自 ImageView 的类,并在其中重写 onMeasure 方法来控制 ImageView 的最大长度和高度。同时,在加载图片时,你可以根据图片的宽高比例来计算缩放比例,然后使用 Matrix 对图片进行缩放操作。 下面是一个示例代码: ```java public class CustomImageView extends ImageView { private int mMaxWidth; // 最大宽度 private int mMaxHeight; // 最大高度 public CustomImageView(Context context) { super(context); init(); } public CustomImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 设置默认的最大宽度和高度 mMaxWidth = Integer.MAX_VALUE; mMaxHeight = Integer.MAX_VALUE; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 获取 ImageView 的测量模式和尺寸 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 计算根据最大宽度和高度得到的宽度和高度 int maxWidth = Math.min(widthSize, mMaxWidth); int maxHeight = Math.min(heightSize, mMaxHeight); int scaledWidth = widthSize; int scaledHeight = heightSize; // 根据图片的宽高比例计算缩放比例 Drawable drawable = getDrawable(); if (drawable != null) { int imageWidth = drawable.getIntrinsicWidth(); int imageHeight = drawable.getIntrinsicHeight(); float scale = Math.min((float) maxWidth / imageWidth, (float) maxHeight / imageHeight); scaledWidth = (int) (imageWidth * scale); scaledHeight = (int) (imageHeight * scale); } // 根据测量模式设置最终的宽度和高度 int finalWidth = (widthMode == MeasureSpec.EXACTLY) ? widthSize : scaledWidth; int finalHeight = (heightMode == MeasureSpec.EXACTLY) ? heightSize : scaledHeight; // 设置最终的宽度和高度 setMeasuredDimension(finalWidth, finalHeight); } public void setMaxSize(int maxWidth, int maxHeight) { mMaxWidth = maxWidth; mMaxHeight = maxHeight; } } ``` 你可以在布局文件中使用这个自定义ImageView,然后通过调用 `setMaxSize()` 方法来设置最大宽度和高度。当加载图片时,ImageView 会按照比例缩放图片来适应最大的宽度和高度限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值