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();
        }
    }
}
發佈了207 篇原創文章 · 獲贊 936 · 訪問量 96萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章