Android 仿蘑菇街列表弹出和瀑布流 (ScrollView+RelativeLayout实现)

30 篇文章 0 订阅
4 篇文章 0 订阅



之前看到用线性布局写的瀑布流,觉得不大好,自己想了另外一种方案,

(最近发现用 网页实现瀑布流 再用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 转载请注明出处

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值