dependencies {
implementation 'com.squareup.picasso:picasso:2.71828'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.airbnb.android:lottie:3.0.3'
implementation 'com.facebook.fresco:fresco:0.9.0+'
implementation 'org.jsoup:jsoup:1.14.1'
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
implementation 'androidx.sqlite:sqlite:2.2.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'com.alibaba:fastjson:1.2.67'
implementation("com.squareup.okhttp3:okhttp:4.10.0")
}
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:usesCleartextTraffic="true">
如果出现错误:app:checkDebugDuplicateClasses
参考这篇博客尝试解决
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<?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:orientation="vertical"
android:padding="5dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true" />
</LinearLayout>
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private ImageAdapter mImageAdapter;
public String html = "https://2k317b5009.goho.co/images/List?type=breakfast";
private List<ImageModel> mImageUrlsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//使用单线程的线程池来异步执行 callable 对象中的任务,并在任务执行完成后获取结果并显示图片和进行其他操作
//使用 ExecutorService 来执行一个 Callable 对象,并获取它的返回结果
//创建一个单线程的线程池,用于执行任务
ExecutorService executor = Executors.newSingleThreadExecutor();
//将 callable 对象提交给线程池进行执行,并返回一个 Future 对象,该对象可以用于获取任务的执行结果
Future<List<ImageModel>> future = executor.submit(callable);
try {
//通过调用 future.get() 方法来阻塞主线程,直到任务执行完成并返回结果。在这里,我们将返回的结果赋值给 imageUrls 变量
List<ImageModel> imageUrls = future.get();
//将获取到的图片列表传递给 showImages 方法,以显示图片和后续操作
showImages(imageUrls);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
executor.shutdown();
}
}
private void showImages(List<ImageModel> imageUrls) {
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView = findViewById(R.id.recyclerView);
//通过设置RecyclerView的布局管理器为StaggeredGridLayoutManager,并指定每行或每列的单元格数量和布局方向,就可以实现错乱瀑布流的效果
//spanCount:指定每行或每列的单元格数量。在这个例子中,设置为2,表示每行有两个单元格。
//orientation:指定布局的方向,VERTICAL表示垂直
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
//自定义的适配器类 将数据绑定到RecyclerView中的每个列表项上
ImageAdapter adapter = new ImageAdapter(imageUrls, this);
//调用RecyclerView的setAdapter()方法,将适配器与RecyclerView关联
mRecyclerView.setAdapter(adapter);
}
/**
* 使用 java.util.concurrent 包来执行并发任务,可以通过创建 ExecutorService 和 Future 对象来实现
* 在 onCreate() 方法中,我们创建了一个单线程的 ExecutorService,然后通过 submit() 方法将 HtmlParserTask 提交给线程池进行执行。
* 接着,我们使用 future.get() 来获取并发任务的结果,并调用 showImages() 方法来展示图片
*/
Callable<List<ImageModel>> callable = new Callable<List<ImageModel>>() {
@Override
public List<ImageModel> call() throws Exception {
List<ImageModel> imageModels = new ArrayList<>();
// 解析 HTML 获取图片 URL 的逻辑
Document document = Jsoup.connect(html).get();
//使用 Jsoup 连接到给定的 HTML 页面,并使用 body().text() 方法获取页面的纯文本内容
String jsonString = document.body().text();
//转换成object
JSONObject jsonObject = new JSONObject(jsonString);
//从jsonObject里面找一个ImageList的对象
JSONArray jsonArray = jsonObject.getJSONArray("ImageList");
String imageUrl = null;
//开始遍历
for (int i = 0; i < jsonArray.length(); i++) {
//遍历ImageList里面的每个对象
JSONObject item = jsonArray.getJSONObject(i);
// 检查是否存在 "src" 键
if (item.has("src")) {
imageUrl = item.getString("src");
//Log.e(TAG,imageUrl);
imageModels.add(new ImageModel(imageUrl));
}
}
return imageModels;
}
};
}
import static android.content.ContentValues.TAG;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.picasso.Picasso;
import java.util.List;
import java.util.Random;
//适配器类
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
private List<ImageModel> imageList;
private Context context;
//有参构造
public ImageAdapter(List<ImageModel> imageList, Context context) {
Log.e(TAG, "ImageAdapter thread id= "+ Thread.currentThread().getId() + " name="+Thread.currentThread().getName());
this.imageList = imageList;
this.context = context;
}
//onCreateViewHolder() 方法用于创建RecyclerView的每个列表项的视图
@NonNull
@Override
public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_image, parent, false);
return new ImageViewHolder(view);
}
//onBindViewHolder() 方法用于将数据绑定到ViewHolder中的视图
@Override
public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) {
ImageModel imageModel = imageList.get(position);//获取特定位置的图像模型(ImageModel)对象,并从中获取图像URL
String imageUrl = imageModel.getImageUrl();
//使用Picasso库加载图像URL并显示在ViewHolder的ImageView
Picasso.get()
.load(imageUrl)
.fit()
.centerCrop()
.into(holder.imageView);
// 设置图片的高度为随机高度,以实现错乱瀑布流效果
ViewGroup.LayoutParams layoutParams = holder.imageView.getLayoutParams();
layoutParams.height = getRandomHeight();//随机高度
holder.imageView.setLayoutParams(layoutParams);
}
// 获取随机高度
private int getRandomHeight() {
Random random = new Random();
return random.nextInt(400) + 300; // 设置图片高度范围为300-700之间的随机值
}
//返回图像列表的大小,即列表中包含的图像数量
@Override
public int getItemCount() {
return imageList.size();
}
//设置适配器的图像列表
public void setImageList(List<ImageModel> imageList) {
this.imageList = imageList;
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
//持有每个列表项的视图组件,这里是一个ImageView
public ImageViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
}
}
}
//接收图片url
public class ImageModel {
private String imageUrl;
public ImageModel(String imageUrl) {
this.imageUrl = imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public String getImageUrl() {
return imageUrl;
}
}