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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="#f7e7c5"android:orientation="horizontal"><LinearLayoutandroid: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"><LinearLayoutandroid:id="@+id/ll_exit"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><ImageViewandroid: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><LinearLayoutandroid: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"><ImageViewandroid:id="@+id/iv_circle"android:layout_width="19dp"android:layout_height="20dp"android:layout_marginHorizontal="10dp"android:background="@drawable/search_edit"/><EditTextandroid: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><TextViewandroid: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><ListViewandroid:id="@+id/lv_search"android:layout_width="match_parent"android:layout_height="match_parent"android:divider="#00000000"/><WebViewandroid: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"><ImageViewandroid: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"/><LinearLayoutandroid:id="@+id/ll_item_search"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:orientation="vertical"><TextViewandroid: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="书名"/><TextViewandroid: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="作者:"/><TextViewandroid: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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_search);initView();setViewSize();setData();}@SuppressLint("NonConstantResourceId")@Overridepublic 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(){@Overridepublic 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;//是否搜索@Overridepublic void initView() {View decorView=getWindow().getDecorView();//获取当前界面的DecorViewint 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(){@Overridepublic 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){//加载简介内容和封面urlhandler.sendEmptyMessage(6);//设置简介}}}});}private int now_position;//当前位置private int flag;//当前位置@Overridepublic 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});}@Overridepublic 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{@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic 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();//发请求读取返回的数据并封装为bitmapint responseCode = connection.getResponseCode();if(responseCode==200) {InputStream is = connection.getInputStream();//图片文件流//将is封装为bitmapBitmap 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 作者*/@JavascriptInterfacepublic 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);//更新数据}/*** 接收加载完成的通知,收到该通知就开始加载图片和简介*/@JavascriptInterfacepublic void notice() {handler.sendEmptyMessage(5);//通知加载简介和封面图片url}/*** 加载简介内容*/@JavascriptInterfacepublic void setContent(String introduce) {if (list.size()>now_position){list.get(now_position).refresh=true;list.get(now_position).introduction=""+introduce;}}/*** 加载图片url*/@JavascriptInterfacepublic 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);}}}
}
3、效果展示
