效果图
工程目录
依赖库
Android Studio 项目:http://pan.baidu.com/s/1hr8KZTq
MainActivity.java
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends Activity {
private final int COLNUM = 2; //列数
private RecyclerView recyclerView;
private ArrayList<String> imgList = new ArrayList<>(); //图片的url
private ArrayList<Integer> heightList = new ArrayList<>(); //item高度
private MyImageLoader imageLoader;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//为ImageView设置下载好的图片
if (MyImageLoader.WHAT == msg.what){
MyImageLoader.ImageBean bean = (MyImageLoader.ImageBean) msg.obj;
if (null != bean){
imageLoader.addBitmapToCache(bean.url, bean.bitmap);
//防止图片错位
if (bean.url.equals(bean.imageView.getTag())) {
bean.imageView.setImageBitmap(bean.bitmap);
}
}
}
//图片路径抓取完成,设置适配器
else if (1 == msg.what){
recyclerView.setAdapter(new MyAdapter(imgList));
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
imageLoader = new MyImageLoader(mHandler){
@Override
public Bitmap loadBitmap(ImageBean bean) {
Bitmap bitmap = null;
try {
//从URL流读取图片
URL url = new URL(bean.url);
bitmap = BitmapFactory.decodeStream(url.openStream());
}catch (Exception e){
e.printStackTrace();
}
return bitmap;
}
};
//抓取图片
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gbk&word=%C3%C0%C5%AE&ala=1&fr=ala&alatpl=cover&pos=0#z=0&pn=&ic=0&st=-1&face=0&s=0&lm=-1")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String html = response.body().string();
//用正则表达式匹配网页中图片的地址
Pattern pattern = Pattern.compile("hoverURL\":\"[^\"]+");
Matcher matcher = pattern.matcher(html);
while (matcher.find()) {
String s = matcher.group();
imgList.add(s.substring(s.indexOf("http"), s.length()));
}
//匹配图片宽度
pattern = Pattern.compile("\"width\":[0-9]+");
matcher = pattern.matcher(html);
String matcherStr = "\"width\":";
int[] widths = new int[imgList.size()];
int i = 0;
while (matcher.find()){
String s = matcher.group();
try {
int w = Integer.valueOf(s.substring(matcherStr.length(), s.length()));
widths[i++] = w;
}catch (Exception e){
e.printStackTrace();
}
}
//匹配图片高度
pattern = Pattern.compile("\"height\":[0-9]+");
matcher = pattern.matcher(html);
matcherStr = "\"height\":";
int[] heights = new int[imgList.size()];
i = 0;
while (matcher.find()){
String s = matcher.group();
try {
int h = Integer.valueOf(s.substring(matcherStr.length(), s.length()));
heights[i++] = h;
}catch (Exception e){
e.printStackTrace();
}
}
//计算缩放item的实际高度
int size = widths.length;
double screenWidth = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getWidth();
double itemWidth = screenWidth / COLNUM;
for (i = 0; i < size; i++){
//计算宽高比例
double scale = heights[i] / (double) widths[i];
heightList.add((int) (scale * itemWidth));
}
mHandler.sendEmptyMessage(1);
}
});
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(COLNUM, StaggeredGridLayoutManager.VERTICAL));
}
private class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
private ArrayList<String> dataList;
public MyAdapter(ArrayList<String> dataList){
this.dataList = dataList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
//设置图片大小
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
params.height = heightList.get(position);
holder.itemView.setLayoutParams(params);
holder.iv.setImageBitmap(null);
//防止图片错位
holder.iv.setTag(dataList.get(position));
imageLoader.add(new MyImageLoader.ImageBean(holder.iv, dataList.get(position)));
}
@Override
public int getItemCount() {
return dataList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
public ImageView iv;
public MyViewHolder(View itemView) {
super(itemView);
iv = (ImageView) itemView.findViewById(R.id.iv);
}
}
}
}
页面的抓取用了okhttp库
图片的链接和对应的宽高都是从百度图片根据“美女”关键字搜索得出的页面中抓取的
在onBindViewHolder方法中根据heightList来设置itemView的高度来实现瀑布流效果
MyImageLoader.java
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.widget.ImageView;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 异步图片加载
*/
public abstract class MyImageLoader{
public static final int WHAT = 98791981; //msg.what
private final int CACHE_PART = 8; //把可用内存的 1/CACHE_PART 用于图片缓存
private final Deque<ImageBean> deque = new ArrayDeque<>(); //等待队列
private Handler handler; //UI线程的Handler
private Type loadType = Type.LIFO; //加载方式,默认为后进先出
private LruCache<String, Bitmap> imgCache; //图片缓存
private final Thread mThread = new Thread(){ //加载线程
@Override
public void run() {
super.run();
while (true){
while(!deque.isEmpty()){
ImageBean bean;
synchronized (deque) {
//先进先出,取出队列的尾部
if (Type.FIFO == loadType){
bean = deque.removeLast();
}
//后进先出,取出队列的首部
else {
bean = deque.removeFirst();
}
}
//加载Bitmap
Bitmap bitmap = loadBitmap(bean);
//通知UI线程更新图片
if (null != bitmap) {
Message msg = new Message();
msg.what = WHAT;
bean.bitmap = bitmap;
msg.obj = bean;
handler.sendMessage(msg);
}
}
//没有任务则让线程休眠
synchronized (this){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
/**
* @param handler UI线程的Handler
*/
public MyImageLoader(Handler handler){
this.handler = handler;
//设置图片缓存
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / CACHE_PART;
imgCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount() / 1024;
}
};
//开始加载线程
mThread.start();
}
/**
* 添加任务
*/
public void add(ImageBean bean){
Bitmap bitmap = getBitmapFromCache(bean.url);
if (null != bitmap){
Message msg = new Message();
msg.what = WHAT;
bean.bitmap = bitmap;
msg.obj = bean;
handler.sendMessage(msg);
}
synchronized (deque){
deque.addFirst(bean);
}
if (Thread.State.WAITING == mThread.getState()){
synchronized (mThread) {
mThread.notify();
}
}
}
/**
* 设置加载方式
* @param type
*/
public void setLoadType(Type type){
if (null != type) {
this.loadType = type;
}
}
/**
* 添加到缓存
*/
public void addBitmapToCache(String key, Bitmap bitmap){
if (null != key && null != bitmap){
imgCache.put(key, bitmap);
}
}
/**
* 从缓存中取出
*/
public Bitmap getBitmapFromCache(String key){
return imgCache.get(key);
}
/**
* 加载Bitmap的方法
*/
public abstract Bitmap loadBitmap(ImageBean bean);
public static class ImageBean{
public ImageView imageView;
public String url;
public Bitmap bitmap;
public ImageBean(ImageView imageView, String url) {
this.imageView = imageView;
this.url = url;
}
}
public enum Type{
FIFO, //先进先出
LIFO //后进先出
}
}
用Thread+Handler实现图片的异步加载,并且使用了LruCache来缓存图片
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="1px"
android:background="#8A8A8A"
android:gravity="center">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>