前言
當我們閱讀了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();
}
}
}