自定义控件之大图的加载
来源:互联网 发布:java观察者模式例子 编辑:程序博客网 时间:2024/05/16 18:54
要求:加载一张大图片到APP中,用户手机仅显示图片的一部分,根据用户的交互,用户手机显示图片不同的部分.(使用分块的模式,加载一张大图片)
实现步骤:
1.创建资源目录,assets文件夹,把超大图片放入其中
2.创建一个完全的自定义控件,实现加载显示图片一部分的功能
(1).继承View,覆写其3个构造方法
public class BigViewextendsView {
publicBigView(Context context) {super(context);}publicBigView(Context context, AttributeSet attrs) {super(context, attrs);}publicBigView(Context context, AttributeSet attrs,intdefStyleAttr) {super(context, attrs, defStyleAttr);}.....
}
(2).创建设置图片参数对象,使用静态代码块,给图片设置颜色质量,降低图片中的颜色数量,来减少存储图片所需的内存,改变之后的图片和原图,人眼看不出来
//创建设置图片参数对象
private staticBitmapFactory.Optionsops=newBitmapFactory.Options();static{
//图片的颜色质量的参考网址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1023/1825.html//设置图片的颜色质量,使其不要太占内存ops.inPreferredConfig= Bitmap.Config.RGB_565;
}
(3).创建一个方法,从外界接收图片的字节流
/*** @param inputStream代表着超大图片文件*/public voidsetInput(InputStream inputStream) {
//通过图片参数对象设置不会把这张图片加载到内存中,避免OOM的问题ops.inJustDecodeBounds=true;//只是单纯的将流和图片的参数设置对象进行关联 参数:1.流 2.为null 3.对图片的设置对象BitmapFactory.decodeStream(inputStream,null,ops);//通过图片的参数设置对象获取到图片的宽和高imageWidth=ops.outWidth;imageHeight=ops.outHeight;try {
//BitmapRegionDecoder用于解码图像,把图片字节流其中一部分以矩形区域展示并换成Bitmap对象 参数:1.流资源 2.false会对加载的图片进行复制decoder= BitmapRegionDecoder.newInstance(inputStream,false);
} catch(IOException e) {e.printStackTrace();}
}
(4).通过手机屏幕和图片的宽与高进行运算,绘制一个在图片有具体位置的矩形(提示:一般测量等耗时操作不要在onDraw里执行,放到onMeasure)
@Overrideprotected voidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取手机屏幕的宽和高measuredWidth= getMeasuredWidth();measuredHeight= getMeasuredHeight();//是控件加载大图片一部分,显示的是图片中心位置,所以设置左右和上下底部的点int top = imageHeight/2-measuredHeight/2;int bottom = imageHeight/2+measuredHeight/2;int left = imageWidth/2-measuredWidth/2;int right = imageWidth/2+measuredWidth/2;//创建一个矩形,设置其最左边的点,最上面的点,最右边的点,最下面的点currentRect=newRect(left, top, right, bottom);
}
(5).画出自定义控件的最终样子.此逻辑在onDraw方法里进行
@Overrideprotected voidonDraw(Canvas canvas) {
super.onDraw(canvas);//创建一个指定区域的矩形Bitmap。 参数1.矩阵(就是手机显示图片一部分的大小,以屏幕的宽和高为基准) 2.图片参数对象Bitmap bitmap = decoder.decodeRegion(currentRect,ops);//重新画一张图片图片 ,参数1:Bitmap 2,3设置为0 4画笔设置为nullcanvas.drawBitmap(bitmap,0,0,null);
}
(6).处理用户的触摸事件,使用户滑动屏幕时,加载的部分图片也跟着用户的滑动出现变化
@Overridepublic booleanonTouchEvent(MotionEvent event) {
switch(event.getAction()) {
//用户按下的回调case MotionEvent.ACTION_DOWN:
//获取当前的x轴和Y轴(也就是起点)downX= (int) event.getX();downY= (int) event.getY();
break;//用户移动的回调case MotionEvent.ACTION_MOVE:
//获取移动后的x轴和y轴(也就是终点)int moveX = (int) event.getX();int moveY = (int) event.getY();//起点-终点,得到距离(之所以是起点减,是因为终点是移动值不固定)int diffX = downX- moveX;int diffY = downY- moveY;System.out.println("按下的x:"+downX+"移动后的x:"+ moveX);//把终点变回起点downX= moveX;downY= moveY;//加载区域根据距离进行移动refreshRect(diffX, diffY);
break;
}//请求重绘View,也就是再次调用Ondraw方法.invalidate();//返回值必须是true,代码才起效果.return true;
}
(7).限制用户移动范围,不能让用户一直拉,直到超出图片的范围.
private voidrefreshRect(intdiffX,intdiffY) {//矩形改变值的方法currentRect.offset(diffX, diffY);//限制用户移动范围,不能让用户一直拉,直到超出图片的范围.//不让用户超出左边界,左边界是0if (currentRect.left<=0) {//当到了左边界,把最左边的参数设置为0currentRect.left=0;//最右边的参数设置为屏幕的宽即可currentRect.right=measuredWidth;}//不让用户超出右边界,右边界图片的宽.else if(currentRect.right>=imageWidth) {//当到了右边界,把最左边的参数设置图片宽-屏幕宽currentRect.left=imageWidth-measuredWidth;//最右边的参数设置为图片的宽即可currentRect.right=imageWidth;}//不让用户超出顶部,顶部边界是0.if (currentRect.top<=0){//当到了顶部,把最上边的参数设置为0currentRect.top=0;//最下面的参数为屏幕的高currentRect.bottom=measuredHeight;}//不让用户超出底部,顶部边界是图片的高.else if(currentRect.bottom>=imageHeight){//当到了顶部,用图片的高-屏幕的高currentRect.top=imageHeight-measuredHeight;//最下面的参数为图片的高currentRect.bottom=imageHeight;}}
3.在XML布局文件中使用自己自定义的控件.
<!--A.使用自己的自定义控件,加载大图片的一部分--><com.example.siyan.imagedemo.BigViewandroid:id="@+id/img"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
4.java代码中初始化控件,拿到图片资源,使用自己的自定义控件进行图片加载
//初始化控件BigView bigView = (BigView) findViewById(R.id.img);//从资产目录把图片转换成字节流InputStream inputStream = getAssets().open("world.jpg");//使用自定义控件,调用方法传入图片的字节流,进行图片的展示bigView.setInput(inputStream);
使用开源框架实现上面的逻辑:开源自定义控件实现的代码思路和我们的是一样的,只不过还可以实现两个手指进行对图片放到缩小的功能,对细节的处理也更周全.
1.从github下载WorldMap-master开源自定义控件(网址:https://github.com/johnnylambada/WorldMap)
2.找到library文件,从src代码中找到这三个类(ImageSurfaceView,InputStreamScene,Scene)拷贝到我们的项目中
3.在XML布局文件中使用开源的自定义控件.
<com.example.siyan.imagedemo.custom.ImageSurfaceView
android:id="@+id/img"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
4.java代码中初始化控件,拿到图片资源,使用开源的自定义控件进行图片加载
ImageSurfaceView img = (ImageSurfaceView) findViewById(R.id.img);InputStream inputStream = getAssets().open("world.jpg");img.setInputStream(inputStream);
具体代码:
public class BigView extends View{ private static BitmapFactory.Options ops = new BitmapFactory.Options(); static {//图片的颜色质量的参考网址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1023/1825.html //设置图片的颜色质量,使其不要太占内存 ops.inPreferredConfig = Bitmap.Config.RGB_565; } private int imageWidth; private int imageHeight; private BitmapRegionDecoder decoder; private int measuredWidth; private int measuredHeight; private Rect currentRect; private int downX; private int downY; public BigView(Context context) { super(context); } public BigView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * @param inputStream 代表着超大图片文件 */ public void setInput(InputStream inputStream) {//通过图片参数对象设置不会把这张图片加载到内存中,避免OOM的问题 ops.inJustDecodeBounds = true;//只是单纯的将流和图片的参数设置对象进行关联 参数:1.流 2.为null 3.对图片的设置对象 BitmapFactory.decodeStream(inputStream, null, ops);//通过图片的参数设置对象获取到图片的宽和高 imageWidth = ops.outWidth; imageHeight = ops.outHeight; try {//BitmapRegionDecoder用于解码图像,把图片字节流其中一部分以矩形区域展示并换成Bitmap对象 参数:1.流资源 2.false会对加载的图片进行复制 decoder = BitmapRegionDecoder.newInstance(inputStream, false); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取手机屏幕的宽和高 measuredWidth = getMeasuredWidth(); measuredHeight = getMeasuredHeight();//是控件加载大图片一部分,显示的是图片中心位置,所以设置左右和上下底部的点 int top = imageHeight / 2 - measuredHeight / 2; int bottom = imageHeight / 2 + measuredHeight / 2; int left = imageWidth / 2 - measuredWidth / 2; int right = imageWidth / 2 + measuredWidth / 2;//创建一个矩形,设置其最左边的点,最上面的点,最右边的点,最下面的点 currentRect = new Rect(left, top, right, bottom); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas);//创建一个指定区域的矩形Bitmap。 参数1.矩阵(就是手机显示图片一部分的大小,以屏幕的宽和高为基准) 2.图片参数对象 Bitmap bitmap = decoder.decodeRegion(currentRect, ops);//重新画一张图片图片 ,参数1:Bitmap 2,3设置为0 4画笔设置为null canvas.drawBitmap(bitmap, 0, 0, null); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {//用户按下的回调 case MotionEvent.ACTION_DOWN://获取当前的x轴和Y轴(也就是起点) downX = (int) event.getX(); downY = (int) event.getY(); break;//用户移动的回调 case MotionEvent.ACTION_MOVE://获取移动后的x轴和y轴(也就是终点) int moveX = (int) event.getX(); int moveY = (int) event.getY();//起点-终点,得到距离(之所以是起点减,是因为终点是移动值不固定) int diffX = downX - moveX; int diffY = downY - moveY; System.out.println("按下的x:" + downX + " 移动后的x:" + moveX);//把终点变回起点 downX = moveX; downY = moveY;//加载区域根据距离进行移动 refreshRect(diffX, diffY); break; }//请求重绘View,也就是再次调用Ondraw方法. invalidate();//返回值必须是true,代码才起效果. return true; } private void refreshRect(int diffX, int diffY) {//矩形改变值的方法 currentRect.offset(diffX, diffY);//限制用户移动范围,不能让用户一直拉,直到超出图片的范围. //不让用户超出左边界,左边界是0 if (currentRect.left <= 0) {//当到了左边界,把最左边的参数设置为0 currentRect.left = 0;//最右边的参数设置为屏幕的宽即可currentRect.right = measuredWidth; }//不让用户超出右边界,右边界图片的宽. else if (currentRect.right >= imageWidth) {//当到了右边界,把最左边的参数设置图片宽-屏幕宽 currentRect.left=imageWidth-measuredWidth;//最右边的参数设置为图片的宽即可 currentRect.right = imageWidth; }//不让用户超出顶部,顶部边界是0. if (currentRect.top<=0){//当到了顶部,把最上边的参数设置为0 currentRect.top=0;//最下面的参数为屏幕的高 currentRect.bottom=measuredHeight; }//不让用户超出底部,顶部边界是图片的高. else if(currentRect.bottom>=imageHeight){//当到了顶部,用图片的高-屏幕的高 currentRect.top=imageHeight-measuredHeight;//最下面的参数为图片的高 currentRect.bottom=imageHeight; } }}
/////////////////////////////////////////////////////////////////////////////////
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//初始化控件// ImageSurfaceView img = (ImageSurfaceView) findViewById(R.id.img);//// InputStream inputStream = null;// try {// inputStream = getAssets().open("world.jpg");// img.setInputStream(inputStream);// } catch (IOException e) {// e.printStackTrace();// }//初始化控件 BigView bigView = (BigView) findViewById(R.id.img);//从资产目录把图片转换成字节流 InputStream inputStream = null; try { inputStream = getAssets().open("world.jpg"); } catch (IOException e) { e.printStackTrace(); }//使用自定义控件,调用方法传入图片的字节流,进行图片的展示 bigView.setInput(inputStream); } }////////////////////////////////////////////////////////////////////////////////////////////////
<test.bwie.com.datu.BigView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <!--<!––><test.bwie.com.datu.ImageSurfaceView–>--> <!--<!–android:id="@+id/img"–>--> <!--<!–android:layout_width="wrap_content"–>--> <!--<!–android:layout_height="wrap_content" />--></RelativeLayout>
阅读全文
0 0
- 自定义控件之大图的加载
- Android自定义控件之大图轮播
- 自定义UitableViewcell 加载大图片的内存优化问题
- Android自定义控件之加载进度条的实现
- 加载大图的优化
- Android加载大图之关于inSampleSize选项的思考
- 安卓大图加载-自定义view
- 有效的加载大图片
- Winform----自定义控件之背景半透明遮罩加载控件
- android 自定义控件---简单的加载View
- Android之Bitmap大图加载处理
- Android自助餐之大图片加载
- 高效加载大图片之Lrucache使用
- Android基础之加载大图片
- Android自定义控件之ListView的下拉刷新与上拉加载
- 动态加载自定义控件
- 自定义控件进度条加载
- 加载大图
- .NET漫游指南-007-WPF中多线程调用界面控件
- 软件工程需求分析模板(简单)
- Linux Bash 提示符的一些骚操作
- 数字图像处理成长之路6:滤波(中值 平均值 高斯 双边)
- 春雨医生问答实例的爬取
- 自定义控件之大图的加载
- 获取Android webview的点击元素
- 黑马程序员-java笔记之equals和hascode方法总结。
- makefile的选项CFLAGS和LDFLAGS
- 第三周项目2建设“顺序表”算法库
- 自举
- Glide图片加载
- java 连接MySQL的一般步骤
- LeetCode-76-Minimum Window Substring 尺取法+字典