1、引言
这两天打算把以前弄的一个小说阅读软件的爬虫规则更新一下,然后略微修改后准备上线使用,目前只改了一下搜索界面的规则,勉强能用,加载数据方面还需要优化一下,把这一部分发出来给大家看看,感兴趣的可以直接复制去使用。
2、主要代码
2.1、主界面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.SearchActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f7e7c5"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_exit"
android:layout_width="9dp"
android:layout_height="16dp"
android:layout_marginHorizontal="25dp"
android:layout_marginVertical="10dp"
android:background="@drawable/back"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_search_top"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_search"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_circle"
android:layout_width="19dp"
android:layout_height="20dp"
android:layout_marginHorizontal="10dp"
android:background="@drawable/search_edit"/>
<EditText
android:id="@+id/et_content"
android:layout_width="match_parent"
android:layout_height="34dp"
android:singleLine="true"
android:textSize="20sp"
android:imeOptions="actionSearch"
android:background="#00000000"
android:hint="请输入书名或者作者"/>
</LinearLayout>
<TextView
android:id="@+id/tv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#303030"
android:layout_marginHorizontal="10dp"
android:text="搜索"/>
</LinearLayout>
</LinearLayout>
<ListView
android:id="@+id/lv_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#00000000"/>
<WebView
android:id="@+id/wv_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</LinearLayout>
2.2、ListView布局
<?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:orientation="horizontal">
<ImageView
android:id="@+id/iv_search_name"
android:layout_width="95dp"
android:layout_height="136dp"
android:layout_marginTop="20dp"
android:visibility="invisible"
android:layout_marginHorizontal="20dp"
android:background="#a88d8a"/>
<LinearLayout
android:id="@+id/ll_item_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_search_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="20sp"
android:layout_marginTop="5dp"
android:textColor="#303030"
android:textStyle="bold"
android:text="书名"/>
<TextView
android:id="@+id/tv_search_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:textSize="15sp"
android:textColor="#a5a5a5"
android:text="作者:"/>
<TextView
android:id="@+id/tv_search_introduction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textSize="15sp"
android:layout_marginTop="10dp"
android:maxLines="3"
android:visibility="invisible"
android:ellipsize="end"
android:text="简介:"/>
</LinearLayout>
</LinearLayout>
2.3、activity界面代码
package xyz.dritrtj.read.ui;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import xyz.dritrtj.read.R;
import xyz.dritrtj.read.data.Title;
import xyz.dritrtj.read.interfaces.Init;
import xyz.dritrtj.read.utils.SetUiSize;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* java中重复使用的变量使用后一定要赋值为空,否则数据可能会出错
*/
public class SearchActivity extends AppCompatActivity implements View.OnClickListener, Init {
private LinearLayout ll_search;
private LinearLayout ll_exit;
private ImageView iv_exit;
private ImageView iv_circle;
private EditText et_content;
private TextView tv_search;
private ListView lv_search;
private LinearLayout ll_search_top;
private WebView wv_search;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
initView();
setViewSize();
setData();
}
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.tv_search:
handler.sendEmptyMessage(3);//通知搜索
break;
case R.id.ll_exit:
handler.removeMessages(0);
finish();
break;
}
}
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
if (list.size()>0){
lv_search.setAdapter(adapter);
}
break;
case 1://通知更新数据,不要用线程更新
adapter.notifyDataSetChanged();
break;
case 2://页面加载完成通知可以开始搜索
{
String name=et_content.getText().toString();
et_content.setText("");
if (!name.equals("")){
jsFunction="javascript:function search(data) {\n" +
" var search=document.getElementById('bdcsMain');\n" +
" search.value=''+data;\n" +
" var cls=document.getElementsByClassName('searchBtn');\n" +
" if (cls.length>0){\n" +
" var now_search=cls[0];\n" +
" now_search.click();\n" +
" }\n" +
" }";
wv_search.loadUrl(jsFunction);
//调用 js函数
wv_search.loadUrl("javascript:search("+"'"+name+"'"+");");
}
}
break;
case 3://重新刷新页面,准备搜索
{
flag=0;
isStart=false;
isSearch=true;
wv_search.clearCache(true);//清除缓存
wv_search.loadUrl("http://www.ibiquzw.info/");//重新加载页面,因为后面会替换掉url
}
break;
case 4://返回搜索数据
{
jsFunction="javascript:function getTitle() {\n" +
" var uls=document.getElementsByTagName('ul');\n" +
" if(uls.length>0){\n" +
" ul=uls[1];\n" +
" var lis=ul.getElementsByTagName('li');\n" +
" if(lis.length>0){\n" +
" var now_li;\n" +
" var as;\n" +
" var a;\n" +
" var url;\n" +
" var name;\n" +
" var author;\n" +
" for (var i = 1; i < lis.length && i<21; i++) {\n" +
" now_li=lis[i];\n" +
" as=now_li.getElementsByTagName('a');\n" +
" if(as.length>0){\n" +
" a=as[0];\n" +
" url=a.href;\n" +
" name=a.innerHTML;\n" +
" a=as[1];\n" +
" author=a.innerHTML;\n" +
" window.Android.setTitle(url,name,author);\n" +
" }\n" +
" }\n" +
" window.Android.notice();\n" +
" }\n" +
" }\n" +
" }";
wv_search.loadUrl(jsFunction);
//调用 js函数
wv_search.loadUrl("javascript:getTitle();");
}
break;
case 5: //加载简介和封面url页面
{
flag=1;
if (list.size()>now_position){
wv_search.loadUrl(list.get(now_position).path);
}
}
break;
case 6: //加载简介和封面url
{
if (list.size()>now_position){
jsFunction="javascript:function getContent() {\n" +
" var div=document.getElementById('intro');\n" +
" var introduce=div.innerText;\n" +
" window.Android.setContent(introduce);\n" +
" var img_div=document.getElementById('fmimg');\n" +
" var imgs=img_div.getElementsByTagName('img');\n" +
" if (imgs.length>0){\n" +
" var img=imgs[0];\n" +
" var url=img.src;\n" +
" window.Android.getImg(url);\n" +
" }\n" +
" }";
wv_search.loadUrl(jsFunction);
//调用 js函数
wv_search.loadUrl("javascript:getContent();");
}
}
break;
}
}
};
private Content adapter=new Content();
private List<Title> list=new ArrayList();
private String jsFunction;//js函数
private boolean isStart;//是否初始化
private boolean isSearch;//是否搜索
@Override
public void initView() {
View decorView=getWindow().getDecorView();//获取当前界面的DecorView
int option=View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;//更改文字颜色为深黑色
decorView.setSystemUiVisibility(option);//设置系统UI元素的可见性
getWindow().setNavigationBarColor(Color.TRANSPARENT);
getWindow().setStatusBarColor(Color.parseColor("#ffffff"));//将状态栏设置为白色
getSupportActionBar().hide();
ll_exit = findViewById(R.id.ll_exit);
ll_exit.setOnClickListener(this);
ll_search = findViewById(R.id.ll_search);
iv_exit = findViewById(R.id.iv_exit);
iv_circle = findViewById(R.id.iv_circle);
et_content = findViewById(R.id.et_content);
tv_search = findViewById(R.id.tv_search);
tv_search.setOnClickListener(this);
ll_search_top = findViewById(R.id.ll_search_top);
lv_search = findViewById(R.id.lv_search);
wv_search = findViewById(R.id.wv_search);
WebSettings settings = wv_search.getSettings();
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);//设置回退
settings.setSupportZoom(true);//支持缩放
settings.setBuiltInZoomControls(true);//出现缩放工具
settings.setJavaScriptEnabled(true);// 表示webview可以执行服务器端的js代码
settings.setUserAgentString("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36");
//自适应屏幕
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
//自动缩放
settings.setBuiltInZoomControls(true);
settings.setSupportZoom(true);
//支持获取手势焦点
wv_search.requestFocusFromTouch();
settings.setJavaScriptCanOpenWindowsAutomatically(true);//允许js弹出窗口
wv_search.addJavascriptInterface(new AndroidAndJs(), "Android");
wv_search.setWebViewClient(new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (url.startsWith("http")){
if (!isStart&&isSearch){
isStart=true;//阻止加载同时切换状态
handler.sendEmptyMessage(2);//开始搜索
}
if (isStart&&url.startsWith("http://www.ibiquzw.info/search.html?name=")){
isStart=false;
handler.sendEmptyMessage(4);//返回搜索数据
}
if (flag==1){//加载简介内容和封面url
handler.sendEmptyMessage(6);//设置简介
}
}
}
});
}
private int now_position;//当前位置
private int flag;//当前位置
@Override
public void setViewSize() {
SetUiSize.setMarginTopLinear(ll_search,10);
SetUiSize.setMarginBottomLinear(ll_search,10);
SetUiSize.setWidthLinear(iv_exit,9);
SetUiSize.setHeightLinear(iv_exit,16);
SetUiSize.setMarginHorizontalLinear(iv_exit,25);
SetUiSize.setMarginVerticalLinear(iv_exit,10);
SetUiSize.setWidthLinear(iv_circle,19);
SetUiSize.setHeightLinear(iv_circle,19);
SetUiSize.setHeightLinear(et_content,34);
SetUiSize.setTextViewSize(et_content,20);
SetUiSize.setTextViewSize(tv_search,20);
SetUiSize.setMarginHorizontalLinear(tv_search,10);
GradientDrawable drawable = (GradientDrawable) ll_search_top.getBackground();
int size= (int) (20/SetUiSize.displayWidthDp*SetUiSize.displayWidth);
//两个参数表示一个角,按照左上角,右上角,右下角,左下角的顺序用两个相同的数据表示
drawable.setCornerRadii(new float[]{size,size,size,size,size,size,size,size});
}
@Override
public void setData() {
et_content.setOnEditorActionListener((v, actionId, event) -> {
if ((actionId == EditorInfo.IME_ACTION_UNSPECIFIED ||
actionId == EditorInfo.IME_ACTION_SEARCH) && event != null){
list.clear();
handler.sendEmptyMessage(3);//发消息搜索
}
return false;
});
lv_search.setOnItemClickListener((parent, view, position, id) -> {
// Intent intent=new Intent(SearchActivity.this,ReadActivity.class);
// intent.putExtra("url",list.get(position).path);//章节页面
// intent.putExtra("name",list.get(position).name);//书名
// intent.putExtra("author",list.get(position).author);//作者
// intent.putExtra("code","0");//表示是搜索来源
// intent.putExtra("imagePath",list.get(position).imagePath);//表示图片路径
// startActivity(intent);
});
wv_search.clearCache(true);
wv_search.loadUrl("http://www.ibiquzw.info/");
}
private class Content extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
if (convertView==null){
convertView=View.inflate(SearchActivity.this,
R.layout.item_search,null);
holder=new Holder();
holder.iv_search_name= convertView.
findViewById(R.id.iv_search_name);
SetUiSize.setWidthLinear(holder.iv_search_name,95);
SetUiSize.setHeightLinear(holder.iv_search_name,136);
SetUiSize.setMarginTopLinear(holder.iv_search_name,20);
SetUiSize.setMarginHorizontalLinear(holder.iv_search_name,20);
holder.ll_item_search=convertView.
findViewById(R.id.ll_item_search);
SetUiSize.setMarginTopLinear(holder.ll_item_search,20);
holder.tv_search_title=convertView.
findViewById(R.id.tv_search_title);
SetUiSize.setTextViewSize(holder.tv_search_title,20);
SetUiSize.setMarginTopLinear(holder.tv_search_title,5);
holder.tv_search_author=convertView.
findViewById(R.id.tv_search_author);
SetUiSize.setTextViewSize(holder.tv_search_author,15);
SetUiSize.setMarginTopLinear(holder.tv_search_author,5);
holder.tv_search_introduction=convertView.
findViewById(R.id.tv_search_introduction);
SetUiSize.setTextViewSize(holder.tv_search_introduction,15);
SetUiSize.setMarginTopLinear(holder.tv_search_introduction,10);
convertView.setTag(holder);
}else {
holder= (Holder) convertView.getTag();
}
Title title = list.get(position);
holder.tv_search_title.setText(title.name);//书名
holder.tv_search_author.setText("作者:"+title.author);//作者
if (title.refresh){
if (title.introduction.equals("")){
holder.tv_search_introduction.setText("暂无简介");//简介
}else {
holder.tv_search_introduction.setText(title.introduction);//简介
}
holder.tv_search_introduction.setVisibility(View.VISIBLE);//显示简介
}else {
holder.tv_search_introduction.setVisibility(View.INVISIBLE);//隐藏简介
}
if (title.isLoad){
holder.iv_search_name.setImageBitmap(title.bitmap);//图片
holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
}else {
holder.iv_search_name.setVisibility(View.INVISIBLE);//隐藏图片
if (holder.iv_search_name.getDrawable()==null){
holder.iv_search_name.setBackgroundResource(R.drawable.empty);
holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
}
}
if (title.isEmpty){
holder.iv_search_name.setBackgroundResource(R.drawable.empty);
holder.iv_search_name.setVisibility(View.VISIBLE);//显示图片
}
return convertView;
}
}
private class Holder{
public TextView tv_search_title;
public TextView tv_search_author;
public TextView tv_search_introduction;
public ImageView iv_search_name;
public LinearLayout ll_item_search;
}
private ExecutorService service_constriction = Executors.newFixedThreadPool(4);
private ExecutorService serviceImg = Executors.newFixedThreadPool(8);
/**
* 加载图片数据
* @param position
* @param img_url
*/
private void load_img(int position,String img_url){
serviceImg.execute(() -> {
Title title=list.get(position);
try {
//加载图片
URL url = new URL(img_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
//发请求读取返回的数据并封装为bitmap
int responseCode = connection.getResponseCode();
if(responseCode==200) {
InputStream is = connection.getInputStream();//图片文件流
//将is封装为bitmap
Bitmap bitmap = BitmapFactory.decodeStream(is);
is.close();
if(bitmap!=null) {
title.bitmap=bitmap;
title.isLoad=true;
}else {
title.isEmpty=true;
}
handler.sendEmptyMessage(1);
}
} catch (Exception e) {
// throw new RuntimeException(e);
}
});
}
private Title title;
private class AndroidAndJs{
/**
* 用于获取百度网盘共享目录数据
* @param url 当前小说url,已经包含前缀
* @param name 书名
* @param author 作者
*/
@JavascriptInterface
public void setTitle(String url,String name,String author) {
title=new Title();
title.name=name;
title.path=url;
title.author=""+author;
list.add(title);
if (list.size()==1){
handler.sendEmptyMessage(0);//直接设置数据
}
handler.sendEmptyMessage(1);//更新数据
}
/**
* 接收加载完成的通知,收到该通知就开始加载图片和简介
*/
@JavascriptInterface
public void notice() {
handler.sendEmptyMessage(5);//通知加载简介和封面图片url
}
/**
* 加载简介内容
*/
@JavascriptInterface
public void setContent(String introduce) {
if (list.size()>now_position){
list.get(now_position).refresh=true;
list.get(now_position).introduction=""+introduce;
}
}
/**
* 加载图片url
*/
@JavascriptInterface
public void getImg(String url) {
if (list.size()>now_position){
list.get(now_position).imagePath=""+url;
load_img(now_position,url);
now_position++;
handler.sendEmptyMessage(1);
handler.sendEmptyMessage(5);
}
}
}
}