1、记录下学到的Android加载长图写法以备后用
首先准备一张长图。这里把图片先放到项目的 assets文件夹下:命名为big.png
然后开始自定义显示长图的view :BigView
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import java.io.IOException;
import java.io.InputStream;
public class BigView extends View implements GestureDetector.OnGestureListener,View.OnTouchListener{
private Scroller mScroller;
private Rect mRect;
private BitmapFactory.Options mOptions;
private GestureDetector mGresureDetector;
private int mImageWight,mImageHeight;
private BitmapRegionDecoder mDecoder;
private int mViewWidth,mViewHeight;
private float mScale;
private Bitmap bitmap;
public BigView(Context context) {
this(context,null,0);
}
public BigView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//指定区域
mRect = new Rect();
//需要复用
mOptions = new BitmapFactory.Options();
//手势识别类
mGresureDetector = new GestureDetector(context,this);
//设置onTouchListener
setOnTouchListener(this);
//滑动帮助
mScroller = new Scroller(context);
}
/**
* 由使用者输入一张图片
* @param is
* @return
*/
public void setImage(InputStream is){
//先读取原图片的信息 宽、高
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is,null,mOptions);
mImageWight = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
//开启复用
mOptions.inMutable = true;
//设置格式成RBG_565,因为565 存储像素点占用内存小,一个像素点只需要两个字节
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
//创建一个区域解码器
try {
mDecoder = BitmapRegionDecoder.newInstance(is,false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
/**
* 在测量的时候把我们需要的内存区域获取到 存入到mRect中
* @param
* @return
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取测量的view的大小
mViewWidth = getMeasuredWidth();
mViewHeight = getMeasuredHeight();
//如果解码器拿不到,表示没有设置过要显示的图片
if (null == mDecoder){
return;
}
//确定要加载的图片的区域
mRect.left = 0;
mRect.top = 0;
mRect.right = mImageWight;
//获取一个缩放比例
mScale = mViewWidth / (float)mImageWight;
//高度就根据缩放比进行获取
mRect.bottom = (int)(mViewHeight/mScale);
}
/**
* 画出内容
* @param
* @return
*/
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//如果解码器拿不到,表示没有设置过要显示的图片
if (null == mDecoder){
return;
}
//复用上一张bitmao
mOptions.inBitmap = bitmap;
//解码指定区域
bitmap = mDecoder.decodeRegion(mRect,mOptions);
//把得到的矩阵大小的内存进行缩放
Matrix matrix = new Matrix();
matrix.setScale(mScale,mScale);
//画出来
canvas.drawBitmap(bitmap,matrix,null);
}
/**
* 手按下的回调
* @param e
* @return
*/
@Override
public boolean onDown(MotionEvent e) {
//如果移动还没有停止,强制停止
if (!mScroller.isFinished()){
mScroller.forceFinished(true);
}
//继续接受后续事件
return true;
}
/**
*
* @param e1 手势按下去的事件 开始获取坐标
* @param e2 当前手势事件 获取当前坐标
* @param distanceX x方向移动的距离
* @param distanceY y方向移动的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//上下移动的时候,需要改变显示的区域 改mRect
mRect.offset(0,(int)distanceY);
//处理移动时已经移到了两个顶端的问题
if (mRect.bottom > mImageHeight){
mRect.bottom = mImageHeight;
mRect.top = mImageHeight-(int)(mViewHeight/mScale);
}
if (mRect.top < 0){
mRect.top = 0;
mRect.bottom = (int)(mViewHeight/mScale);
}
invalidate();
return false;
}
/**
* 处理惯性问题
* @param e1
* @param e2
* @param velocityX 每秒移动的x点
* @param velocityY
* @return
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//做计算 -velocityY 正负号问题,相反的
// ( 按下手指不拿开,屏幕跟着手势方向,若松开,方向则向相反方向滑动 ) 故 使用 负值才能正常使用
mScroller.fling(0,mRect.top,0, (int) -velocityY,0,0,0,
mImageHeight-(int)(mViewHeight/mScale));
return false;
}
/**
* 使用上一个接口的计算结果
*/
@Override
public void computeScroll() {
if (mScroller.isFinished()){
return;
}
//true 表示当前滑动还没有结束
if (mScroller.computeScrollOffset()){
mRect.top = mScroller.getCurrY();
mRect.bottom = mRect.top+(int)(mViewHeight/mScale);
invalidate();
}
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//交给手势处理
return mGresureDetector.onTouchEvent(event);
}
}
自定义view写完后,在要显示的布局中引入这个bigview控件,这个就不写了,放到布局就行了
下面在activity中展示:
public class BigViewActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bigview_layout);
BigView bigView = findViewById(R.id.bigview);
InputStream is = null;
try {
//加载图片
is = getAssets().open("big.png");
bigView.setImage(is);
}catch (Exception e){
e.printStackTrace();
}finally {
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
好了这些就行了,运行就可以看到加载长图,手动上下滑动浏览大图了