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

时间:2021-11-01 00:37:08


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

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

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

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

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