1、引言
我把我的小说软件中的阅读界面解析规则和相关内容进行了更新,目前已经支持上一章和下一章的切换,并且能通过目录直接跳转到指定章节,当前被展示的章节目录中对应的章节名会有明显的变色效果,目前这个加载的数据只能识别来自于搜索界面的传递的内容,感兴趣的朋友可以参考我上一篇关于小说解析的文章。
2、代码实现
2.1、主界面布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".ui.ReadActivity">
<WebView
android:id="@+id/wv_read"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:id="@+id/ll_wv_src"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone"
android:background="#44404040"/>
<LinearLayout
android:id="@+id/ll_lv_back"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginRight="100dp"
android:visibility="gone"
android:background="#f7e7c5">
<ListView
android:id="@+id/lv_list"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="10dp"
android:divider="#00000000"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_shelf"
android:gravity="center"
android:layout_alignParentRight="true"
android:layout_marginTop="100dp"
android:visibility="gone"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_read_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加入书架"
android:textSize="15sp"
android:textColor="#ffffff"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:visibility="visible"/>
</LinearLayout>
<!--用于加载网络数据-->
<WebView
android:id="@+id/wv_load"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
2.2、目录界面布局
<?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">
<TextView
android:id="@+id/tv_read_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_marginLeft="10dp"
android:textColor="#cabea4"
android:layout_marginVertical="10dp"
android:text="书名"/>
</LinearLayout>
2.3、真正阅读界面布局
真正的阅读界面我是使用一个本地网页来展示的。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阅读页面</title>
<script type="text/javascript">
//被java代码调用显示小说内容
function showContent(data,title) {
var div=document.getElementById("div_read");
div.innerHTML=data;
document.getElementById("span_last").innerHTML="上一章";
document.getElementById("span_list").innerHTML="目录";
document.getElementById("span_next").innerHTML="下一章";
document.getElementById("h1_title").innerText=title;
}
function backStart() {
scrollTo(0,0);//跳转到距离页面顶端0的位置
}
</script>
<style type="text/css">
#body{
background-color: #d7c7a5;
}
div{
font-size: 2ch;
margin-left: 5%;
margin-right: 5%;
}
.span_bottom{
width: 30%;
font-size: 2ch;
position: center;
color: #000000;
}
#span_list{
margin-left: 20%;
margin-right: 20%;
}
#div_read{
color: rgb(85,85,85);
}
</style>
</head>
<body id="body">
<br/>
<center>
<h2 id="h1_title"></h2>
</center>
<div id="div_read"></div>
<br/>
<br/>
<center>
<span id="span_last" class="span_bottom" onclick="window.Android.lastChapter()"></span>
<span id="span_list" class="span_bottom" onclick="window.Android.showList()"></span>
<span id="span_next" class="span_bottom" onclick="window.Android.nextChapter()"></span></center>
<br/>
<br/>
</body>
</html>
2.4、实现代码
目前实现代码中包含的添加历史阅读记录和添加书架数据的部分功能暂时没有修改,因此这一部分内容请忽略,待我全部修改完成后会将完整的代码提交到我的网站提供下载。
package xyz.dritrtj.read.ui;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
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.view.View;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.BaseAdapter;
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.fragment.BookShelfFragment;
import xyz.dritrtj.read.interfaces.Init;
import xyz.dritrtj.read.utils.DBHelper;
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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* 本界面不需要适配,后面为目录添加一个水平进出的动画效果更好,然后进一步添加水平方向翻页的效果
*/
public class ReadActivity extends AppCompatActivity implements View.OnClickListener, Init {
private LinearLayout ll_lv_back;//目录的布局
private ListView lv_list;
private WebView wv_read;
private String path;//路径
private String originPath;//初始路径路径
private String data;//数据
private int page;//章节索引
private LinearLayout ll_wv_src;//webView的遮挡布局
private String name;//书名
private String author;//作者
private int code;//表示跳转来源
private Intent intent;//用于判断跳转来源
private LinearLayout ll_add;
private String imagePath;//图片路径
private TextView tv_read_add;
private WebView wv_load;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_read);
initView();
setViewSize();
setData();
}
private boolean isShow=true;
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.ll_wv_src:
ll_lv_back.setVisibility(View.GONE);
ll_wv_src.setVisibility(View.GONE);
isShow=true;
break;
case R.id.ll_add:
add();
break;
}
}
@SuppressLint("SetJavaScriptEnabled")
@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();
wv_read = findViewById(R.id.wv_read);
lv_list = findViewById(R.id.lv_list);
ll_lv_back = findViewById(R.id.ll_lv_back);
ll_wv_src = findViewById(R.id.ll_wv_src);
ll_wv_src.setOnClickListener(this);
lv_list.setOnItemClickListener((parent, view, position, id) -> {
if (page!=position){
page=position;
ll_lv_back.setVisibility(View.GONE);
ll_wv_src.setVisibility(View.GONE);
isShow=true;
handler.sendEmptyMessage(3);
}
});
ll_add = findViewById(R.id.ll_add);
ll_add.setOnClickListener(this);
tv_read_add = findViewById(R.id.tv_read_add);
intent = getIntent();
path=intent.getStringExtra("url");
originPath=path;
name=intent.getStringExtra("name");
author=intent.getStringExtra("author");
imagePath=intent.getStringExtra("imagePath");
code=Integer.parseInt(intent.getStringExtra("code"));
WebSettings webSettings = wv_read.getSettings();
//设置支持javaScript脚步语言
webSettings.setJavaScriptEnabled(true);
//设置js调用java支持
wv_read.addJavascriptInterface(new JavaAndJs(),"Android");
//设置客户端-不跳转到默认浏览器中
wv_read.setWebViewClient(new WebViewClient());
wv_read.loadUrl("file:///android_asset/read.html");
wv_load = findViewById(R.id.wv_load);
WebSettings settings = wv_load.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_load.requestFocusFromTouch();
settings.setJavaScriptCanOpenWindowsAutomatically(true);//允许js弹出窗口
wv_load.addJavascriptInterface(new AndroidAndJs(), "Android");
wv_load.setWebViewClient(new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (url.startsWith("http")){
if (flag==0){//加载目录
jsFunction="javascript:function getDirectory() {\n" +
" var list=document.getElementById('list');\n" +
" var dds=list.getElementsByTagName('dd');\n" +
" if(dds.length>12){\n" +
" var dd;\n" +
" var url;\n" +
" var now_name;\n" +
" var as;\n" +
" var a;\n" +
" for (var i = 12; i < dds.length; i++) {\n" +
" dd=dds[i];\n" +
" as=dd.getElementsByTagName('a');\n" +
" a=as[0];\n" +
" url=a.href;\n" +
" now_name=a.innerHTML;\n" +
" window.Android.setDirectory(url,now_name);\n" +
" }\n" +
" window.Android.setContent();\n" +
" }\n" +
" }";
view.loadUrl(jsFunction);
//调用 js函数
view.loadUrl("javascript:getDirectory();");
}
if (flag==1){
jsFunction="javascript:function getData() {\n" +
" var div=document.getElementById('content');\n" +
" var data=div.innerHTML;\n" +
" window.Android.setData(data);\n" +
" }";
wv_load.loadUrl(jsFunction);
//调用 js函数
wv_load.loadUrl("javascript:getData();");
}
}
}
});
wv_load.loadUrl(path);//加载章节目录
}
private int flag;//操作标志0代表读取目录
private String jsFunction;//js函数
@Override
public void setViewSize() {
SetUiSize.setMarginRightRelative(ll_lv_back,100);
SetUiSize.setMarginTopLinear(lv_list,20);
SetUiSize.setMarginRightLinear(lv_list,10);
SetUiSize.setMarginTopRelative(ll_add,100);
int size= (int) (20/SetUiSize.displayWidthDp*SetUiSize.displayWidth);
GradientDrawable drawable = (GradientDrawable) ll_add.getBackground();
drawable.setCornerRadii(new float[]{size,size,0,0,0,0,size,size});
SetUiSize.setMarginTopLinear(tv_read_add,15);
SetUiSize.setMarginHorizontalLinear(tv_read_add,20);
SetUiSize.setMarginVerticalLinear(tv_read_add,10);
}
@Override
public void setData() {
// loadContent();
}
class JavaAndJs{
@JavascriptInterface
public void showList(){
if (isShow){
handler.sendEmptyMessage(2);
}
}
//上一章
@JavascriptInterface
public void lastChapter(){
if ((page-1)>=0){
page--;
handler.sendEmptyMessage(3);
}
}
//下一章
@JavascriptInterface
public void nextChapter(){
if ((page+1)<=list.size()-1){
page++;
handler.sendEmptyMessage(3);
}
}
}
private List<Title> list=new ArrayList<>();
private ListAdapter adapter=new ListAdapter();
private class ListAdapter 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(ReadActivity.this,
R.layout.item_read,null);
holder=new Holder();
holder.tv_read_title=convertView.
findViewById(R.id.tv_read_title);
SetUiSize.setTextViewSize(holder.tv_read_title,20);
SetUiSize.setMarginLeftLinear(holder.tv_read_title,10);
SetUiSize.setMarginVerticalLinear(holder.tv_read_title,10);
convertView.setTag(holder);
}else {
holder= (Holder) convertView.getTag();
}
Title title = list.get(position);
holder.tv_read_title.setText(title.chapter);
if (page==position){
holder.tv_read_title.setTextColor(Color.parseColor("#000000"));
}else {
holder.tv_read_title.setTextColor(Color.parseColor("#cabea4"));
}
return convertView;
}
private class Holder{
public TextView tv_read_title;
}
}
/**
* 加载主体数据
* @param path url
*/
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
wv_read.loadUrl("javascript:backStart()");//将页面滑动到开始位置
wv_read.loadUrl("javascript:showContent("+"'"+data+"'"+","+"'"+list.get(page).chapter+"'"+")");
// update();
break;
case 1://加载目录
{
lv_list.setAdapter(adapter);
handler.sendEmptyMessage(3);
}
break;
case 2:
ll_lv_back.setVisibility(View.VISIBLE);
ll_wv_src.setVisibility(View.VISIBLE);
isShow=false;
break;
case 3://加载章节内容
flag=1;
wv_load.loadUrl(list.get(page).path);
break;
}
}
};
private void loadContent(String path){
Document document = null;
try {
document = Jsoup.connect(path).get();
Element content = document.getElementById("content");
String html = content.html();
data = html.substring(0, html.indexOf("<p>")-5);
handler.sendEmptyMessage(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String tmpStr="";
/**
* 进入主界面时加载目录和页面内容数据
*/
private void loadContent() {
new Thread(){
@Override
public void run() {
//4.解析主体内容
Document document;
try {
//1.抓取目录和对应的章节相对路径,后面需要合成绝对路径
document = Jsoup.connect(path).get();
Elements dl = document.getElementsByTag("dl");
Elements a = dl.first().getElementsByTag("a");
Title title;
int index=0;
for (Element element:a){
title=new Title();
title.chapter=new String(element.text());//目录
title.path=new String(element.attr("href"));//相对路径
list.add(title);
if (index==0){
tmpStr="https://www.ibiquge.la"+title.path;
index=-1;
}
}
if (index==-1){
handler.sendEmptyMessageDelayed(1,50);
//2.解析主体内容,判断显示是否加入书架的按钮
if (code==1){//如果是来源于书架,那么重新定位阅读位置
page= Integer.parseInt(intent.getStringExtra("page"));
tmpStr="https://www.ibiquge.la"+list.get(page).path;
}if (code==2){//如果是来源于阅读记录,那么重新定位阅读位置
page= Integer.parseInt(intent.getStringExtra("page"));
tmpStr="https://www.ibiquge.la"+list.get(page).path;
if (select()){
code=1;
}
}else {
select();
}
loadContent(tmpStr);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}.start();
}
/**
* 添加
*/
private void add(){
DBHelper dbHelper=new DBHelper(ReadActivity.this,1);
SQLiteDatabase database = dbHelper.getReadableDatabase();
String sql="CREATE TABLE IF NOT EXISTS shelf(\n" +
"\tbook VARCHAR(20) NOT NULL,\n" +
"\tauthor VARCHAR(30) NOT NULL,\n" +
"\tintroduce VARCHAR(200),\n" +
"\tpage VARCHAR(100),\n" +
"\torigin VARCHAR(100) NOT NULL,\n" +
"\tprogress INTEGER NOT NULL\n" +
");";
database.execSQL(sql);
ContentValues value=new ContentValues();
value.put("book",name);//书名
value.put("author",author);//作者
value.put("origin",originPath);//书源初始路径
value.put("progress",page);//阅读进度
String imageName;
if (code==2){
imageName=intent.getStringExtra("imageName");
}else{
imageName=imagePath.substring(imagePath.lastIndexOf("/") + 1);
}
value.put("page",imageName);//封面名字
database.insert("shelf",null,value);
database.close();
ll_add.setVisibility(View.GONE);
new Thread(){
@Override
public void run() {
Bitmap bitmap = null;
try {
//得到连接
URL url = new URL(imagePath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
//连接
connection.connect();
//发请求读取返回的数据并封装为bitmap
int responseCode = connection.getResponseCode();
if(responseCode==200) {
InputStream is = connection.getInputStream();//图片文件流
//将is封装为bitmap
bitmap = BitmapFactory.decodeStream(is);
is.close();
if(bitmap!=null) {
//缓存到本地
// /storage/sdcard/Android/data/packageName/files/
String filesPath = getExternalFilesDir(null).getAbsolutePath();
String fileName = imagePath.substring(imagePath.lastIndexOf("/")+1);//xxx.jpg
String filePath = filesPath+"/"+fileName;
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
}
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
BookShelfFragment.fragment.handler.sendEmptyMessageDelayed(0,50);
}
/**
* 更新书架
*/
private void update(){
DBHelper dbHelper=new DBHelper(ReadActivity.this,1);
SQLiteDatabase database = dbHelper.getReadableDatabase();
String sql="CREATE TABLE IF NOT EXISTS shelf(\n" +
"\tbook VARCHAR(20) NOT NULL,\n" +
"\tauthor VARCHAR(30) NOT NULL,\n" +
"\tintroduce VARCHAR(200),\n" +
"\tpage VARCHAR(100),\n" +
"\torigin VARCHAR(100) NOT NULL,\n" +
"\tprogress INTEGER NOT NULL\n" +
");";
database.execSQL(sql);
ContentValues value=new ContentValues();
value.put("progress",page);//阅读进度
database.update("shelf",value,"book = ? AND author = ?",
new String[]{name,author});
database.close();
updateHistory();
BookShelfFragment.fragment.handler.sendEmptyMessage(0);
}
/**
* 更新历史记录
*/
@SuppressLint("Range")
private void updateHistory(){
DBHelper dbHelper=new DBHelper(ReadActivity.this,1);
SQLiteDatabase database = dbHelper.getReadableDatabase();
String sql="CREATE TABLE IF NOT EXISTS history(\n" +
"\tbook VARCHAR(20) NOT NULL,\n" +
"\tauthor VARCHAR(30) NOT NULL,\n" +
"\tintroduce VARCHAR(200),\n" +
"\tpage VARCHAR(100),\n" +
"\torigin VARCHAR(100) NOT NULL,\n" +
"\tprogress INTEGER NOT NULL\n" +
");";
database.execSQL(sql);
Cursor shelf = database.query("history", null, "book = ? AND author = ?",
new String[]{name, author}, null, null, null);
if (!shelf.moveToNext()){
ContentValues value=new ContentValues();
value.put("book",name);//书名
value.put("author",author);//作者
value.put("origin",originPath);//书源初始路径
value.put("progress",page);//阅读进度
String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
value.put("page",imageName);//封面名字
database.insert("history",null,value);
new Thread(){
@Override
public void run() {
Bitmap bitmap = null;
try {
//得到连接
URL url = new URL(imagePath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
//连接
connection.connect();
//发请求读取返回的数据并封装为bitmap
int responseCode = connection.getResponseCode();
if(responseCode==200) {
InputStream is = connection.getInputStream();//图片文件流
//将is封装为bitmap
bitmap = BitmapFactory.decodeStream(is);
is.close();
if(bitmap!=null) {
//缓存到本地
// /storage/sdcard/Android/data/packageName/files/
String filesPath = getExternalFilesDir(null).getAbsolutePath();
String fileName = imagePath.substring(imagePath.lastIndexOf("/")+1);//xxx.jpg
String filePath = filesPath+"/"+fileName;
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
}
}
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
ContentValues value=new ContentValues();
value.put("progress",page);//阅读进度
database.update("history",value,"book = ? AND author = ?",
new String[]{name,author});
database.close();
}
/**
* 查询本书是否已经加入书架
*/
@SuppressLint("Range")
private boolean select(){
boolean isAdd;
DBHelper dbHelper=new DBHelper(ReadActivity.this,1);
SQLiteDatabase database = dbHelper.getReadableDatabase();
String sql="CREATE TABLE IF NOT EXISTS shelf(\n" +
"\tbook VARCHAR(20) NOT NULL,\n" +
"\tauthor VARCHAR(30) NOT NULL,\n" +
"\tintroduce VARCHAR(200),\n" +
"\tpage VARCHAR(100),\n" +
"\torigin VARCHAR(100) NOT NULL,\n" +
"\tprogress INTEGER NOT NULL\n" +
");";
database.execSQL(sql);
Cursor shelf = database.query("shelf", null, "book = ? AND author = ?",
new String[]{name, author}, null, null, null);
if (shelf.moveToNext()){
page=shelf.getInt(shelf.getColumnIndex("progress"));
tmpStr=list.get(page).path;
isAdd=true;
}else {
runOnUiThread(() -> ll_add.setVisibility(View.VISIBLE));
isAdd=false;
}
database.close();
return isAdd;
}
private Title title;
private class AndroidAndJs{
/**
* 加载目录数据
* @param url 章节url
* @param name 章节名
*/
@JavascriptInterface
public void setDirectory(String url,String name) {
title=new Title();
title.chapter=name;//章节名
title.path=url;//章节路径
list.add(title);
}
/**
* 目录加载完成,加载章节内容
*/
@JavascriptInterface
public void setContent() {
handler.sendEmptyMessage(1);//加载显示目录
}
/**
* 显示章节内容
*/
@JavascriptInterface
public void setData(String now_data) {
data=now_data;
handler.sendEmptyMessage(0);
}
}
}
3、效果展示
3.1、阅读界面展示
从阅读界面中可以看出,还有部分来自于网站的广告没有去掉,这个后期再修改。目前阅读界面是使用竖屏的方式进行展示小说内容的,暂时不支持横屏切换阅读,这个功能后期再添加,每加载一个新章节后会将阅读界面滑动到开头的位置。
3.2、底部章节切换按钮展示
这里我的上下章节的切换按钮和目录展示按钮是放在阅读界面底部的,虽然是竖屏展示内容,但目前并不支持将多个章节内容连接在一起自动切换展示,该该功能后期酌情添加。
3.3、目录展示效果
目前目录是从左边弹出的,点击外部区域目录会被隐藏,我使用相对布局的方式在目录背后填充了一层布局,用于判断是否点击目录中的内容,同时也是为了防止对其它部分进行误操作。