之前看到用线性布局写的瀑布流,觉得不大好,自己想了另外一种方案,
(最近发现用 网页实现瀑布流 再用WebView加载才能完美实现效果)
原理使用RelativeLayout任意定位位置 核心方法
private void addViewByMargins(RelativeLayout layout, View view, int x,
int y, int width, int height) {
RelativeLayout.LayoutParams layout_params = null;
layout_params = new RelativeLayout.LayoutParams(width, height);
// padding是控件的内容相对控件的边缘的边距.
// margin是控件边缘相对父控件,或者其他控件的边距.
layout_params.setMargins(x, y, 0, 0);
view.setLayoutParams(layout_params);
layout.addView(view);
}
和二分区间算法searchVisibleMethod 将非可视区域的View移除
时间关系使用的是粗陋的缓存 但不能完美的解决内存溢出的存在。
仿蘑菇街列表滑出代码
package lxz.utils.android.template.view;
import com.cn.lxz.R;
import lxz.utils.android.anim.EasingType;
import lxz.utils.android.anim.ElasticInterpolator;
import lxz.utils.android.resource.AndroidUtils;
import android.content.Context;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation.AnimationListener;
import android.widget.RelativeLayout;
public class LikeMogujie extends RelativeLayout {
public static enum State {
// 关闭
close,
// 关闭中
closeing,
// 打开
open,
// 打开中
opening;
}
// 当前状态
private State state;
private int width;
private int height;
private int move;
// 边界阴影宽度
private int bound_width;
GestureDetector mGestureDetector;
// 动画时间
private int animTime = 600;
// 手势距离
private int gd_distance = 250;
// 右边边距阴影图片
private View bound;
// 抽屉 主内容
private View panel;
// 菜单分类
private View menu;
public LikeMogujie(Context context, AttributeSet attrs) {
super(context, attrs);
state = state.close;
this.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
panel = findViewById(R.id.panel);
bound = findViewById(R.id.bound);
menu = findViewById(R.id.menu);
height = getMeasuredHeight();
width = getMeasuredWidth();
bound_width = bound.getMeasuredWidth();
move = (int) (width / 2.05);
mGestureDetector = new GestureDetector(getContext(),
mOnGestureListener);
mGestureDetector.setIsLongpressEnabled(false);
// 对滑动栏 内容进行监听手势
panel.setOnTouchListener(mOnTouchListener);
// 将bound隐藏
TranslateAnimation animation_bound;
animation_bound = new TranslateAnimation(0, bound_width, 0, 0);
animation_bound.setDuration(0);
animation_bound.setFillAfter(true);
bound.startAnimation(animation_bound);
// 将分类列表隐藏
menu.setVisibility(View.GONE);
}
});
}
private void change(State will) {
RelativeLayout.LayoutParams layout_params = null;
TranslateAnimation animation_bound = null;
TranslateAnimation animation = null;
switch (will) {
case open:
state = state.opening;
menu.setVisibility(View.VISIBLE);
layout_params = new RelativeLayout.LayoutParams(width, height);
layout_params.setMargins(-move, 0, 0, 0);
panel.setLayoutParams(layout_params);
removeView(panel);
addView(panel);
animation_bound = new TranslateAnimation(bound_width, -move
+ bound_width, 0, 0);
animation = new TranslateAnimation(move, 0, 0, 0);
break;
case close:
state = state.closeing;
layout_params = new RelativeLayout.LayoutParams(width, height);
layout_params.setMargins(0, 0, 0, 0);
panel.setLayoutParams(layout_params);
removeView(panel);
addView(panel);
animation_bound = new TranslateAnimation(-move + bound_width,
bound_width, 0, 0);
animation = new TranslateAnimation(-move, 0, 0, 0);
break;
default:
break;
}
// 中断事件信号
SystemClock.sleep(0);
// 刷新视图
invalidate();
animation_bound.setDuration(animTime);
animation_bound.setFillAfter(true);
animation.setDuration(animTime);
panel.startAnimation(animation);
bound.startAnimation(animation_bound);
animation.setAnimationListener(mAnimationListener);
}
private OnTouchListener mOnTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(final View v, MotionEvent event) {
// TODO Auto-generated method stub
return mGestureDetector.onTouchEvent(event) || true;
}
};
private AnimationListener mAnimationListener = new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
if (state == state.closeing) {
state = State.close;
menu.setVisibility(View.GONE);
} else if (state == state.opening) {
state = State.open;
}
}
};
private OnGestureListener mOnGestureListener = new OnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (state == State.open) {
change(state.close);
}
return false;
}
@Override
public void onShowPress(MotionEvent e) {
if (state == State.open) {
change(state.close);
}
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
System.out.println("onLongPress" + e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
AndroidUtils.showToast(getContext(), "" + velocityX);
if ((velocityX > 0 ? velocityX : velocityX * -1) < gd_distance) {
return false;
}else
{
if (state == state.close && velocityX < 0) {
change(state.open);
}
}
if (state == State.open) {
change(state.close);
}
return false;
}
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
};
//打开或者关闭
public void OpenOrClose() {
if (state == state.close) {
change(state.open);
return;
}
if (state == State.open) {
change(state.close);
return;
}
}
瀑布流代码
package com.cn.lxz.pubo2;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.WeakHashMap;
import com.cn.lxz.R;
import lxz.utils.android.layout.LayoutUtils;
import lxz.utils.android.layout.Margins;
import lxz.utils.android.network.HttpResourcesTask;
import lxz.utils.android.network.HttpResourcesTask.CacheType;
import lxz.utils.android.network.Task;
import lxz.utils.android.network.Task.OnFinishListen;
import lxz.utils.android.network.TaskGroupAsyn;
import lxz.utils.android.network.HttpResourcesTask.HttpType;
import lxz.utils.android.network.TaskGroup.OnGroupFinsh;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
public class MyScrollView extends ScrollView {
public interface OnScrollListener {
void onBottom(int scrollY);
void onTop(int scrollY);
void onScroll(int scrollY);
void onAutoScroll(int scrollY);
void onUpdata(int scrollY);
// 上滑
void onScrollUP(int scrollY);
// 下滑
void onScrollDOWN(int scrollY);
}
private OnScrollListener onScrollListener;
private AssetManager asset_manager = null;
private List<String> image_filenames = null;
private final String image_path = "images";
private RelativeLayout relatLayout;
private ProgressBar loadingbar;
private Button flag1;
private Button flag2;
// 是否运行加载数据
private boolean canLoading = true;
// WeakHashMap<String, SoftReference<Bitmap>> map = new WeakHashMap<String,
// SoftReference<Bitmap>>();
// WeakHashMap<String, SoftReference<Bitmap>> map = new WeakHashMap<String,
// SoftReference<Bitmap>>();
// 三列
int lineSize = 3;
// 边距系数 采用系数可以控制不同屏幕的尺寸问题
float disCoeffic = 0.015f;
float distanX;
// 计算边距
int scalewidth;
// 获得可视区域最大值
int maxVisibleHeight;
// 获得自定义布局的高度
int layoutHeight;
// 获得自定义布局的宽度
int layoutWidth;
// 获得最高的列的索引
int maxRowIndex;
// 获得最高的列的高度
int maxRowHeight;
// 判断向上滑true还是向下滑false
boolean orientation;
// 当前滑动的坐标Y索引
int scrolledIndex = 0;
// 快速区间搜索
int[][] quickSearch = new int[lineSize][2];
LinkedList<ViewInfo>[] lists = new LinkedList[lineSize];
// 计算高度坐标值
int[] lineHeight = new int[lineSize];
// 计算X坐标值
int[] lineX = new int[lineSize];
{
for (int i = 0; i < lineSize; i++) {
lineHeight[i] = 0;
lists[i] = new LinkedList<MyScrollView.ViewInfo>();
lineX[i] = 103 * i;
}
}
Integer tasksOver = 0;
int tasksLength;
// 加载网络图片
public void addUrlata(final List<String> list) {
canLoading = false;
this.post(new Runnable() {
@Override
public void run() {
// Task类是未开发完全的框架 ,这里用来加载图片,如果必要你可以重写该方法
removeLoadingBar();
tasksLength = list.size();
tasksOver = 0;
for (String s : list) {
new HttpResourcesTask(getContext(), HttpType.Img,
CacheType.saveInSDcard).setParameter(s)
.setOnFinishListen(new OnFinishListen() {
@Override
public void OnFinish(Task t, Object data) {
// TODO Auto-generated method stub
if (data != null
&& !(data instanceof Exception)) {
addSingleView(t.getParameter()
.toString(), (Drawable) t
.getResult());
}
synchronized (tasksOver) {
tasksOver++;
if (tasksOver == tasksLength) {
canLoading = true;
}
}
}
}).start();
}
}
});
}
// 移除加载进度条
private void removeLoadingBar() {
if (loadingbar != null) {
relatLayout.removeView(loadingbar);
loadingbar = null;
}
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
// 初始化界面
init();
}
private void init() {
// 隐藏滚动条
setVerticalScrollBarEnabled(false);
relatLayout = new RelativeLayout(getContext());
ScrollView.LayoutParams layout_params = null;
layout_params = new ScrollView.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT);
this.addView(relatLayout);
// layout_params = new ScrollView.LayoutParams(LayoutParams.FILL_PARENT,
// 1000);
// padding是控件的内容相对控件的边缘的边距.
// margin是控件边缘相对父控件,或者其他控件的边距.
relatLayout.setLayoutParams(layout_params);
// view标记主要是防止回收图片时 relatLayout高度减少
flag1 = new Button(getContext());
addViewByMargins(relatLayout, flag1, 0, 0, 1, 1);
flag2 = new Button(getContext());
this.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
maxVisibleHeight = MyScrollView.this.getMeasuredHeight();
// System.out.println("高度" +
// MyScrollView.this.getMeasuredHeight());
}
});
// 初始化分配空间的参数
relatLayout.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
layoutHeight = relatLayout.getMeasuredHeight();
layoutWidth = relatLayout.getMeasuredWidth();
distanX = layoutWidth * disCoeffic;
scalewidth = (int) (1.0f * (layoutWidth - (distanX * (lineSize + 1))) / lineSize);
for (int i = 0; i < lineSize; i++) {
lineX[i] = (int) (distanX + (distanX + scalewidth) * i);
}
}
});
}
private void addViewByMargins(RelativeLayout layout, View view, int x,
int y, int width, int height) {
RelativeLayout.LayoutParams layout_params = null;
layout_params = new RelativeLayout.LayoutParams(width, height);
// padding是控件的内容相对控件的边缘的边距.
// margin是控件边缘相对父控件,或者其他控件的边距.
layout_params.setMargins(x, y, 0, 0);
view.setLayoutParams(layout_params);
layout.addView(view);
}
// // 获得插入的列的索引
// int maxRowIndex;
private int getRowIndex() {
int lineIndex = 0;
for (int i = 1; i < lineSize; i++) {
if (lineHeight[lineIndex] > lineHeight[i]) {
lineIndex = i;
}
}
maxRowIndex = lineIndex;
return lineIndex;
}
private void addSingleView(String url, Drawable d) {
try {
// 计算获得哪个列的高度
int lineIndex = getRowIndex();
int sWidth = d.getIntrinsicWidth();
;
float scale = 1.0f * scalewidth / sWidth;
int height = (int) (d.getIntrinsicHeight() * scale);
ImageView imageView = new ImageView(getContext());
addViewByMargins(relatLayout, imageView, lineX[lineIndex],
lineHeight[lineIndex], scalewidth, height);
imageView.setImageDrawable(d);
ViewInfo viewInfo = new ViewInfo(lineX[lineIndex],
lineHeight[lineIndex], lineHeight[lineIndex] + height,
scalewidth, height, imageView, url);
// 加入到队列信息中
lists[lineIndex].add(viewInfo);
imageView.setTag(viewInfo);
imageView.setOnTouchListener(childOnTouListen);
// 高度=图片高度+边距
lineHeight[lineIndex] += height + distanX;
// 最底部插入一个flag2 view防止容器被回收
if (lineHeight[lineIndex] > maxRowHeight) {
maxRowHeight = lineHeight[lineIndex];
insertFlag2(lineX[lineIndex], lineHeight[lineIndex]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void insertFlag2(int x, int y) {
// TODO Auto-generated method stub
try {
try {
relatLayout.removeView(flag2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag2.setText("测试");
addViewByMargins(relatLayout, flag2, x, y, 1, 1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public boolean arrowScroll(int direction) {
// TODO Auto-generated method stub
return super.arrowScroll(direction);
}
@Override
public boolean fullScroll(int direction) {
// TODO Auto-generated method stub
return super.fullScroll(direction);
}
protected void onOverScrolled(int scrollX, final int scrollY,
boolean clampedX, boolean clampedY) {
// TODO Auto-generated method stub
orientation = scrollY - scrolledIndex > 0 ? false : true;
scrolledIndex = scrollY;
searchVisibleMethod(orientation, scrollY);
if (onScrollListener != null) {
if (orientation) {
onScrollListener.onScrollUP(scrollY);
} else {
onScrollListener.onScrollDOWN(scrollY);
}
if (scrollY <= 0) {
onScrollListener.onTop(scrollY);
} else if (relatLayout.getMeasuredHeight()
- this.getMeasuredHeight() - scrollY <= 0) {
onScrollListener.onBottom(scrollY);
// Log.e("数据", relatLayout.getMeasuredHeight() + " "
// +this.getMeasuredHeight() + " "+scrollY );
// 滑动到底部时加载 进度到底部
if (loadingbar == null) {
loadingbar = new ProgressBar(getContext());
LayoutUtils.addView(relatLayout,
android.view.ViewGroup.LayoutParams.FILL_PARENT,
android.view.ViewGroup.LayoutParams.FILL_PARENT,
loadingbar,
new Margins(0, relatLayout.getMeasuredHeight(), 0,
0), null);
}
if (canLoading) {
canLoading = false;
new Thread() {
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (onScrollListener != null) {
onScrollListener.onUpdata(scrollY);
}
}
}.start();
}
} else {
onScrollListener.onScroll(scrollY);
}
}
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
public OnScrollListener getOnScrollListener() {
return onScrollListener;
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
// System.out.println("onSizeChanged=" + "W=" + w + " H=" + h +
// " oldw="
// + oldw + " clampedY" + oldh);
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public boolean pageScroll(int direction) {
// TODO Auto-generated method stub
System.out.println(" pageScroll=" + direction);
return super.pageScroll(direction);
}
// 算法 防止Out of Memory 通过方向和区间搜索快速移除非可视区域的view
private void searchVisibleMethod(boolean orientation, int scrollY) {
for (int k = 0; k < lineSize; k++) {
// 向上滑
int length = lists[k].size();
// 初始化区间索引 quickSearch[k][0]
// 标示k列的可视区域的从上向下第一个视图
// quickSearch[k][1]可视区域的从下向上第一个视图
if (quickSearch[k] == null) {
quickSearch[k] = new int[2];
quickSearch[k][0] = 0;
quickSearch[k][1] = length;
}
// 二分搜索区间搜索
if (orientation) {
// 开关从满足条件的地方开始索引
boolean indexonOff = true;
// 正向查询
for (int i = quickSearch[k][1]; i >= 0; i--) {
ViewInfo v = lists[k].get(i);
// 是否在可视范围内部
if (isViewinVisible(v, scrollY)) {
// Log.e("shua",""+ quickSearch[k][1]);
if (indexonOff == true) {
quickSearch[k][1] = i;
indexonOff = false;
}
quickSearch[k][1] = i;
if (v.isIn == false) {
try {
// Log.e("shua", "" + "AAAAA");
v.addView();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.isIn = true;
}
} else {
try {
if (indexonOff == false) {
quickSearch[k][0] = i;
// System.out.println(quickSearch[k][1]);
}
if (v.isIn == true) {
relatLayout.removeView(v.getView());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.isIn = false;
}
}
} else {
// 开关从满足条件的地方开始索引
boolean indexonOff = true;
// 反向查询
for (int i = quickSearch[k][0]; i < length; i++) {
ViewInfo v = lists[k].get(i);
// 是否在可视范围内部
if (isViewinVisible(v, scrollY)) {
if (indexonOff == true) {
quickSearch[k][0] = i;
indexonOff = false;
}
quickSearch[k][0] = i;
if (v.isIn == false) {
try {
v.addView();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.isIn = true;
}
} else {
try {
if (indexonOff == false) {
quickSearch[k][1] = i;
}
if (v.isIn == true) {
relatLayout.removeView(v.getView());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.isIn = false;
}
}
}
}
}
// 是否在可视区域
private boolean isViewinVisible(ViewInfo v, int scrollY) {
return (v.y1 >= scrollY && v.y1 <= scrollY + maxVisibleHeight)
|| (v.y2 >= scrollY && v.y2 <= scrollY + maxVisibleHeight)
|| (v.y1 <= scrollY && v.y2 >= scrollY + maxVisibleHeight);
}
public class ViewInfo {
private int x;
private int y1;
private int y2;
private int width;
private int height;
private SoftReference<ImageView> soft;
private String url;
private boolean isIn = true;
public ViewInfo(int x, int y1, int y2, int width, int height,
ImageView view, String url) {
super();
this.x = x;
this.y1 = y1;
this.y2 = y2;
this.width = width;
this.height = height;
soft = new SoftReference<ImageView>(view);
this.url = url;
}
public String getUrl() {
return url;
}
public View getView() {
if (soft != null && soft.get() != null) {
return soft.get();
}
return null;
}
public View addView() {
ImageView iv = soft.get();
if (iv != null) {
try {
addViewByMargins(relatLayout, iv, x, y1, width, height);
return iv;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
iv = null;
}
}
new HttpResourcesTask(getContext(), HttpType.Img,
CacheType.saveInSDcard).setParameter(url)
.setOnFinishListen(new OnFinishListen() {
@Override
public void OnFinish(Task task, Object data) {
// TODO Auto-generated method stub
Drawable drawable = (Drawable) data;
ImageView imageView = new ImageView(getContext());
addViewByMargins(relatLayout, imageView, x, y1,
width, height);
soft = new SoftReference<ImageView>(imageView);
imageView.setImageDrawable(drawable);
imageView.setTag(ViewInfo.this);
imageView.setOnTouchListener(childOnTouListen);
}
}).start();
return null;
}
}
OnTouchListener childOnTouListen = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Toast.makeText(getContext(), ((ViewInfo) v.getTag()).getUrl(),
1).show();
}
return false;
}
};
}
代码DEMO下载:http://download.csdn.net/detail/b275518834/4931361
原文链接:http://write.blog.csdn.net/postedit/8440670 转载请注明出处