思路
開始的時(shí)候,我想的是將圖片直接加載到ImageView上,然后通過 scrollTo()方法滑動內(nèi)容,從而達(dá)到滑動效果,但是這樣做,循環(huán)效果不好實(shí)現(xiàn)。于是看到這邊文章如何實(shí)現(xiàn)一個(gè)循環(huán)顯示超長圖片的控件,給了我啟發(fā),其實(shí)我們可以通過裁剪來實(shí)現(xiàn)。并在此基礎(chǔ)上進(jìn)行了性能的優(yōu)化!
效果
show.gif
memory.gif
package com.sahadev.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import com.sahadev.cylinderapplication.R;
/**
* Created by shangbin on 2016/6/16.
* Email: sahadev@foxmail.com
*/
public class CylinderImageView extends View {
//用于裁剪的原始圖片資源
private Bitmap mSourceBitmap = null;
// 圖片的高寬
private int mBitmapHeight, mBitmapWidth;
// 移動單位,每次移動多少個(gè)單位
private final int mMoveUnit = 1;
// 圖片整體移動的偏移量
private int xOffset = 0;
// 用于持有兩張拼接圖片的引用,并釋放原先的圖片資源
private Bitmap mPointerA, mPointerB;
/**
* 循環(huán)滾動標(biāo)志位
*/
private boolean mRunningFlag;
public CylinderImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initVideoView();
}
public CylinderImageView(Context context) {
super(context);
initVideoView();
}
public CylinderImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initVideoView();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSourceBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.android_m_hero_1200);
/**
* 獲取圖片的高寬
*/
mBitmapHeight = mSourceBitmap.getHeight();
mBitmapWidth = mSourceBitmap.getWidth();
mRunningFlag = true;
setFocusableInTouchMode(true);
requestFocus();
}
private void initVideoView() {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 簡單設(shè)置一下控件的寬高,這里的高度以圖片的高度為準(zhǔn)
setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mBitmapHeight, MeasureSpec.EXACTLY));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
recycleTmpBitmap();
// 如果一張圖片輪播完,則從頭開始
if (xOffset >= mBitmapWidth) {
xOffset = 0;
}
// 第一張圖的寬帶
int tempWidth = xOffset + getMeasuredWidth() >= mBitmapWidth ? mBitmapWidth - xOffset : getMeasuredWidth();
mPointerA = Bitmap
.createBitmap(mSourceBitmap, xOffset, 0, tempWidth, getMeasuredHeight());
// 繪制這張圖
canvas.drawBitmap(mPointerA, getMatrix(), null);
// 如果最后的圖片已經(jīng)不足以填充整個(gè)屏幕,則截取圖片的頭部以連接上尾部,形成一個(gè)閉環(huán)
if (tempWidth < getMeasuredWidth()) {
Rect dst = new Rect(tempWidth, 0, getMeasuredWidth(), mBitmapHeight);
mPointerB = Bitmap.createBitmap(mSourceBitmap, 0, 0, getMeasuredWidth() - tempWidth,
getMeasuredHeight());
/**
* 將另一張圖片繪制在這張圖片的后半部分
* dst 指在canvas中的位置
*/
canvas.drawBitmap(mPointerB, null, dst, null);
}
// 累計(jì)圖片的偏移量
xOffset += mMoveUnit;
if (mRunningFlag) {
postDelayed(new Runnable() {
@Override
public void run() {
invalidate();
}
}, 30);
}
}
/**
* 回收臨時(shí)圖像
*/
private void recycleTmpBitmap() {
if (mPointerA != null) {
mPointerA.recycle();
mPointerA = null;
}
if (mPointerB != null) {
mPointerB.recycle();
mPointerB = null;
}
}
/**
* 暫停
*/
public void resume() {
mRunningFlag = true;
invalidate();
}
/**
* 恢復(fù)
*/
public void pause() {
mRunningFlag = false;
}
/**
* 回收清理工作
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
pause();
recycleTmpBitmap();
mSourceBitmap.recycle();
}
}
優(yōu)化思路:
- 沒有在onDraw頻繁的去申請對內(nèi)存
- 在onDraw中對之前的mPointerA, mPointerB,兩張Bitmap 回收還是值得借鑒的
- 這邊沒有用Handler來進(jìn)行延遲處理,view本來就有post方法,可以拿過來直接用!
完整代碼請戳 Jack_CycleLongBitmap