package com.cpm.demo.meilishuo.app.views.PhotoViewGroup; import java.io.InputStream; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.cpm.demo.meilishuo.R; import com.cpm.demo.meilishuo.SimulateData; import com.cpm.demo.meilishuo.app.views.PhotoScrollView; import com.cpm.demo.meilishuo.app.views.eClass; import com.cpm.demo.meilishuo.app.views.gClass; import com.cpm.demo.meilishuo.app.views.iClass; import com.cpm.demo.meilishuo.app.views.q_Interface; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; /** * @作者:陈培敏 * @创建时间:2012-8-28 下午11:56:20 (1)四个方法中用到:Rect.intersects */ public class PhotoViewGroup extends ViewGroup {//com.meilishuo.app.views.b private static boolean isShowLog=false; String TAG = PhotoViewGroup.class.getName(); String TAG2 = PhotoViewGroup.class.getName()+"2"; String TAG3 = PhotoViewGroup.class.getName()+"3"; private Context context;// a private int imageWidth;// 固定的图片宽度,b private ExecutorService executorService;// d private List<ImageInfo> list = new ArrayList();// e,要显示的ImageInfo列表,跟map的区别是,(1)list中数据可能有重复;(2)list直接放入内存中;(3)map采用软引用 private Map<Integer, SoftReference> map = new HashMap();// f, private ReferenceQueue refQueue = new ReferenceQueue(); /** 三张图片top位置 **/ private int[] tops = new int[3];// h /** 三张图片left位置 **/ private int[] lefts = new int[3];// i private PhotoScrollView photoScriollView;// j private Paint paint;// 作用是什么呢?k private final int imageSpace = 10;// 图片之间的间隔 // private int l; // private final int m = 0; // private final int n = 1; private int o_datatype; // private boolean p = false; // private View.OnClickListener q; /**点击的效果**/ private View.OnClickListener r = new ImageDataViewClickListener(this); /*** **/ public PhotoViewGroup(Context context) { super(context); this.context = context; if(isShowLog)if(isShowLog) Log.d(TAG, "PhotoShowView=>宽度" + getWidth()); } /*** 测试数据 **/ public void test_SimulateData() { this.setBackgroundColor(Color.GREEN); ImageInfo pi = new ImageInfo(); pi.rect = new Rect(10, 10, 100, 120); pi.width = 20; pi.height = 20; // ImageDataView imageData = new ImageDataView(context); imageData.setBitmap(SimulateData.getImageRefBitmap(context)); imageData.setTag(pi); pi.imageData = imageData; /** 以下如果没有图片,讲通过http获取,所以才需要post线程获取 **/ post(new ImageAddRunnable(this, pi)); } /*** **/ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if(isShowLog) if(isShowLog) Log.d(TAG, "=>onLayout=>getChildCount():" + getChildCount()); for (int i = 0; i < getChildCount(); ++i) { View childView = getChildAt(i); Rect rect = ((ImageInfo) childView.getTag()).rect; childView.layout(rect.left, rect.top, rect.right, rect.bottom); } } /*** **/ @Override protected final void onMeasure(int paramInt1, int paramInt2) { if(isShowLog) if(isShowLog) Log.d(TAG,"onMeasure=>传递过参数:" + paramInt1 + "-" + paramInt2+ "=>getChildCount():" + getChildCount() + ";屏幕宽度"+ this.getWidth()); super.onMeasure(paramInt1, paramInt2); for (int i2 = 0; i2 < getChildCount(); ++i2) { View localView = getChildAt(i2); ImageInfo localba = (ImageInfo) localView.getTag(); localView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.EXACTLY), View.MeasureSpec .makeMeasureSpec(0, View.MeasureSpec.EXACTLY)); System.out.println("onMeasure=>childView参数:" + localba.width + "-" + localba.height); } int i4 = 0;//获取top最大值 for (int i = 0; i < this.tops.length; ++i) { if (i4 < this.tops[i]) { i4 = this.tops[i]; } else { } } System.out.println("onMeasure=>setMeasuredDimension参数:" + View.MeasureSpec.getSize(paramInt1) + "-" + i4); // 指定该控件在屏幕上的大小 super.setMeasuredDimension(View.MeasureSpec.getSize(paramInt1), i4); } /**初始化PhotoScriollView、list以及各个变量值<br/> * (1)在MainHotActivity的a()调用到<br/> * (2)在加入数据前,一定要执行此方法*/ public final void intiDefaultData() {// public final void a() if(isShowLog) if(isShowLog) Log.d(TAG, "================================获取不到宽度,我手动添加!测试长高=>width:" + this.getWidth() + ",height:"+ this.getHeight()); q_Interface localq = this.photoScriollView.a(); this.photoScriollView.a(null); this.photoScriollView.scrollTo(0, 0); //this.list.clear(); //if(isShowLog) if(isShowLog) Log.d(TAG3, "this.list.clear()"); //initVariable(getWidth());//为什么获取不到width initVariable(540); if (this.executorService != null) { this.executorService.shutdownNow(); this.executorService = Executors.newFixedThreadPool(5); } removeAllViews(); // this.p = false; this.photoScriollView.a(localq); } private void initVariable(int screenWidth) {// private void b(int paramInt) this.imageWidth = ((screenWidth - imageSpace * 4) / 3); for (int i = 0; i < this.tops.length; ++i) this.tops[i] = 0;// 初始化,不固定的 for (int i = 0; i < this.lefts.length; ++i) this.lefts[i] = (imageSpace * (i + 1) + i * this.imageWidth);// 初始化left位置,固定的 if (true) { this.paint = new Paint(); this.paint.setColor(-2236963); this.paint.setStyle(Paint.Style.STROKE); this.paint.setStrokeWidth(1.0F); return; } } /*** * (1)在滚动时候,要加载数据,会报错:java.util.ConcurrentModificationException * 如果方法加入synchronized,就不会报错,但view空出一个区域出来(滚动速度稍微快点)。 * (2) * @param imageInfos */ public final void loadList(List<ImageInfo> imageInfos) {// public final void a(List paramList) if(isShowLog) if(isShowLog) Log.d(TAG, "loadList,start=>图片列表大小:" + imageInfos.size() + "=>getChildCount:"+ this.getChildCount() + "=>屏幕宽度:" + this.getWidth()); this.o_datatype = 30020; // this.o = 30020; Iterator localIterator = imageInfos.iterator(); ImageInfo imageInfo = null; while (localIterator.hasNext()) { imageInfo = (ImageInfo) localIterator.next(); Rect localRect = new Rect(); // 找出top最小值和位置 int minTopValue = this.tops[0]; int minPos = 0; for (int i3 = 1; i3 < this.tops.length; i3++) { if(isShowLog) if(isShowLog) Log.d(TAG, "loadList,比较tops:"+i3+"=>minTopValue:"+minTopValue+"=>tops["+i3+"]:"+this.tops[i3]); if (minTopValue <= this.tops[i3]) continue; else { minTopValue = this.tops[i3]; minPos = i3; } } if(isShowLog) if(isShowLog) Log.d(TAG, "loadList,最低位置:"+minPos+"=>对于的值:"+tops[minPos]); { // 自己添加的缩放比例;或放在加载时间时候判断;甚至也放在服务器上。 imageInfo.height = this.imageWidth * imageInfo.height / imageInfo.width; imageInfo.width = this.imageWidth; } this.tops[minPos] = (imageSpace + this.tops[minPos]);// localRect.left = this.lefts[minPos]; localRect.top = this.tops[minPos]; localRect.right = (imageInfo.width + this.lefts[minPos]); localRect.bottom = (this.tops[minPos] + imageInfo.height); // 赋值布局区域 imageInfo.rect = localRect; // 因为添加一张图片,所以tops[minPos]变高了 this.tops[minPos] += imageInfo.height; // 加入列表 this.list.add(imageInfo); if(isShowLog) if(isShowLog) Log.d(TAG3, "this.list.add(imageInfo);"); if(isShowLog) if(isShowLog) Log.d(TAG, "loadList=>返回imageInfo.rect值:" + localRect.left + "-" + localRect.top + "-" + localRect.right + "-" + localRect.bottom); // 判断两个rect是否相交,有的话就显示处理 // if // ((Rect.intersects(getCanLookRect(this.photoScriollView.getScrollY()), // imageInfo.rect))){ // // 注意下反编译代码 // loadImage(imageInfo, true); // //return; or continue; // }else{ // if(isShowLog) if(isShowLog) Log.d(TAG, "loadList=>区域是没有交叉,未执行加载图片"); // continue; // } loadImage(imageInfo, true); } } /** * 加载数据 * @param paramList * @param paramInt */ public final void loadList(List paramList, int paramInt){//public final void a(List paramList, int paramInt) this.o_datatype = paramInt; loadList(paramList); } /*** * 在屏幕上能看到的区域<br/> * 终于搞懂了!!必须要知道: * (1)getTop的含义(相对父View) * (2)getScrollY()的含义 * (3)PhotoViewGroup放在PhotoScrollView里面,所以产生滚动条是PhotoScrollView。 * 滚动条在最下面的时候,会往PhotoViewGroup自动加入数据,所以PhotoViewGroup会变得原来越长了, * 而PhotoViewGroup看不到的数据,在PhotoScrollView最上面,那哪些数据能看到呢?此方法就是计算能看到的区域 * (4)通过允许,就可以看到区域left肯定0,right就是宽度,top通过_top判断的,bottom通过top和height判断的 * @param _top 意思是PhotoViewGroup头部距离PhotoScrollView头部的距离 * @return */ private Rect getCanLookRect(int _top) {// private Rect c(int paramInt) // if(isShowLog) Log.d(TAG, // "了解getTop与getScrollY的=>getTop:"+this.getTop()+"--getScrollY:"+this.getScrollY()); int height = this.photoScriollView.getHeight();// 注意这个是photoScriollView高度,而不是调用this.getHeight() int i3 = 0; ; if (_top < getTop()) {// 这时候PhotoShowView头部都可以在屏幕上看到,出现这样子原因是photoScriollView还有其他view在PhotoShowView上面 int i4 = height - (getTop() - _top); if(isShowLog) Log.d(TAG2, "getCanLookRect=>返回Rect值1:" + getLeft() + "-" + i3 + "-"+ getRight() + "-" + i4 + "=>宽度:" + this.getWidth() + "=>高度:" + this.getHeight()+"=>"+_top); return new Rect(getLeft(), i3, getRight(), i4);// getLeft()、getRight()固定,所以只关心上和下 } else {// 这时候PhotoShowView头部都在屏幕上看不到 i3 = _top - getTop(); int i4 = i3 + height;// 放在这边,还是上面那句呢? if(isShowLog) Log.d(TAG2, "getCanLookRect=>返回Rect值2:" + getLeft() + "-" + i3 + "-"+ getRight() + "-" + i4 + "=>宽度:" + this.getWidth() + "=>高度:" + this.getHeight()+"=>"+_top); return new Rect(getLeft(), i3, getRight(), i4); } } private void loadImage(ImageInfo imageInfo, boolean isGetFromHttp) {// private void a(ba paramba, boolean paramBoolean) if(isShowLog) Log.d(TAG, "执行方法loadImage----------------开始------------"); if (imageInfo.imageData == null) { // 首次运行的时候,每一张图片信息的imageData都为空的。则要实例下(没有图片数据呢,先占用图片空间,然后图片再从http下载或从SD中加载) if(isShowLog) Log.d(TAG, "执行方法loadImage,构建空的图片"); ImageDataView imageData = new ImageDataView(this.context); imageData.setTag(imageInfo); imageInfo.imageData = imageData; imageData.setOnClickListener(this.r);// 点击的操作,暂时不设置吧 post(new ImageAddRunnable(this, imageInfo)); } if(!isGetFromHttp)return; imageInfo.hasLoaded=true; if (true) { Bitmap localBitmap1 = null; if (this.map.get(Integer.valueOf(imageInfo.id)) != null) { localBitmap1 = (Bitmap) ((SoftReference) this.map.get(Integer.valueOf(imageInfo.id))).get(); if(isShowLog) Log.d(TAG, "执行方法loadImage,成功从map获取数据,这次没通过SD卡或HTTP获取数据"); } if (localBitmap1 == null) { /** * 根据反编译信息,可以看出原理(设计太多类,不用了): (1)通过imageInfo.url获取MD5唯一数据标志 * =>e.java (2)定义图片的字节数组imgByte * (3)从缓存(其实是从SD卡获取之前保持的数据)中获取数据赋值给imgByte =>i.java * (4)获取没有缓存,则只能从http获取了 => 本类a(imageInfo)方法 **/ String md5Url = null;// 获取MD5唯一数据标志 byte[] arrayOfByte = null; // 从SD卡获取图片数据=>什么时候存储SD数据呢,答案是在下载的时候,存储SD卡中 // if (iClass.a(md5Url)) {//如果文件存在 // arrayOfByte = iClass.b(md5Url);//从SD卡获取之前保持的数据 // if ((arrayOfByte == null) || (arrayOfByte.length == // 0))//如果获取的数据位空 // iClass.c(str);//删除该文件 // } if ((arrayOfByte == null) || (arrayOfByte.length == 0)) { downImageByHttp(imageInfo);// 图片为空启动下载,第一次加载都会首先执行这个 } else {// 没从SD卡获取,所以这里都还没执行 Bitmap localBitmap2 = createScaledBitmapByByte(arrayOfByte, this.imageWidth);// 根据图片字节 伸缩图片 if (localBitmap2 == null) { // i.c(new String(e.a(paramba.e.getBytes()))); // //删除SD中的文件。paramba.e是ImageInfo.url downImageByHttp(imageInfo);// 找不到图片,则启动下载 } if(isShowLog) Log.d(TAG, "loadImage=>addMap=>" + localBitmap2); addMap(imageInfo.id, localBitmap2); imageInfo.imageData.setBitmap(localBitmap2); } } else { imageInfo.imageData.setBitmap(localBitmap1); } } } /** 通过字节转换Bitmap,且固定width高度自动伸缩。注意这个方法是static **/ private static Bitmap createScaledBitmapByByte(byte[] paramArrayOfByte, int width) {// private static Bitmap b(byte[] paramArrayOfByte, int // paramInt) Bitmap localBitmap1 = BitmapFactory.decodeByteArray(paramArrayOfByte, 0, paramArrayOfByte.length); if(isShowLog) Log.d("com.cpm.demo.meilishuo.myview.PhotoShowView", "createBitmapByByte1=>要显示的width:" + width + "=>图片高度:" + localBitmap1.getHeight() + "=>图片宽度:" + localBitmap1.getWidth() + "=>" + localBitmap1); Bitmap localBitmap2 = null; if (localBitmap1 != null) { localBitmap2 = Bitmap.createScaledBitmap(localBitmap1, width, width * localBitmap1.getHeight() / localBitmap1.getWidth(), true); localBitmap1.recycle(); } if(isShowLog) Log.d("com.cpm.demo.meilishuo.myview.PhotoShowView", "createBitmapByByte2=>要显示的width:" + width + "=>图片高度:" + localBitmap2.getHeight() + "=>图片宽度:" + localBitmap2.getWidth() + "=>" + localBitmap2); return localBitmap2; } /** 往map加入图片源,map格式是SoftReference **/ protected void addMap(int id, Bitmap bitmap) {// private void a(int // paramInt, Bitmap // paramBitmap) synchronized (this.map) { if (this.map.containsKey(Integer.valueOf(id))) { return; } this.map.put(Integer.valueOf(id), new SoftReference(bitmap, this.refQueue)); checkMap(); } } /** 通过http下载图片 **/ private void downImageByHttp(ImageInfo paramba) {// private void a(ba // paramba) if(isShowLog) Log.d(TAG, "执行方法downImageByHttp"); // 反编译认真看下 if (paramba.httpCilent == null) { ImageDownloadRunnable localay = new ImageDownloadRunnable(this, paramba); if (this.executorService == null) this.executorService = Executors.newFixedThreadPool(5); this.executorService.execute(localay); } } /* 作用? */ private void checkMap() {// b(); /** * SoftReference localSoftReference = (SoftReference) * this.refQueue.poll(); Map.Entry localEntry = null; if * (localSoftReference != null) { Iterator iterator = * this.map.entrySet().iterator(); while(iterator.hasNext()){ localEntry * = (Map.Entry) iterator.next(); if (localEntry!=null ) { int value = * ((Integer) localEntry.getKey()).intValue(); if (value == -1) * this.map.remove(Integer.valueOf(value)); return; } } } **/ label0: { if(isShowLog) Log.d(TAG, "checkMap()=>作用待确定"); SoftReference localSoftReference = (SoftReference) this.refQueue .poll(); Map.Entry localEntry = null; if (localSoftReference != null) { Iterator localIterator = this.map.entrySet().iterator(); do { if (!(localIterator.hasNext())) break; localEntry = (Map.Entry) localIterator.next(); } while (localEntry.getValue() != localSoftReference); } if (localEntry != null) for (int i1 = ((Integer) localEntry.getKey()).intValue();; i1 = -1) { if (i1 != -1) ; this.map.remove(Integer.valueOf(i1)); break label0; } } } private Rect getRect(int paramInt) { return new Rect(getLeft(), 0, getRight(), 100); } public final void setPhotoScrollView(PhotoScrollView paramPhotoScrollView) { this.photoScriollView = paramPhotoScrollView; } /** * 滚动条变化的时候,判断list * (1)滚动条是在PhotoScrollView产生的,所以这个方法专门给PhotoScrollView的onScrollChanged方法调用 */ public final void a_optimizeListData(int paramInt) {// bClass=> public final void a(int paramInt) if(isShowLog) Log.d(TAG2, "方法a(int)=>list大小:"+this.list.size()+"=>宽度:"+this.getWidth()+"=>参数:"+paramInt); Rect localRect = this.getCanLookRect(paramInt); Iterator localIterator = this.list.iterator(); if(isShowLog) Log.d(TAG3, "this.list.iterator()"); while (localIterator.hasNext()) { ImageInfo localba = (ImageInfo) localIterator.next();//会报错: java.util.ConcurrentModificationException,为什么呢?原因用iterator遍历集合时要注意的地方:不可以对iterator相关的地方做添加或删除操作。 但是我在哪里操作了呢?滚动条滚动时候,此方法未执行完,再执行一次了? if(isShowLog) Log.d(TAG2, "方法a(int)=>id:"+localba.id+"=>是否交叉:"+Rect.intersects(localRect, localba.rect)+"=>localba.是否已加载:"+localba.hasLoaded+"---------->"+(Rect.intersects(localRect, localba.rect)?"":"-----------------------有看不见了")); if (localba.hasLoaded && !Rect.intersects(localRect, localba.rect)) {//已加载,且没有交叉,图片设空,节约内存吧? localba.hasLoaded = false;//从list获取出来对象,再对对象修改属性,这个时候list对象也会修改的。 if (localba.httpCilent != null) { ImageDownloadRunnable.HttpCilent(localba.httpCilent);//作用? localba.httpCilent = null; } localba.imageData.setBitmap(null); } // if ((localba.isGetFromHttp) || (!(Rect.intersects(localRect, localba.rect)))) // continue; // this.loadImage(localba, true); //改成下面: if (!localba.hasLoaded && Rect.intersects(localRect, localba.rect) ) this.loadImage(localba, true); } } /**给每一张图片,画边框。其实这里可以放在ImageDataView了,没必要在这里设置,因为ImageDataView已经有实现此功能=》红边**/ protected final boolean drawChild(Canvas paramCanvas, View paramView, long paramLong) { Rect localRect = new Rect(); paramView.getHitRect(localRect); int i1 = Math.max(0, 1); localRect.top -= i1; localRect.left -= i1; paramCanvas.drawRect(localRect, this.paint); return super.drawChild(paramCanvas, paramView, paramLong); } // protected final void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) // { // super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4); // if (paramInt1 == paramInt3) // return; // b(paramInt1); // } }