1. 程式人生 > >android 使用epublib開源框架解析epub檔案(章節內容、書籍選單)

android 使用epublib開源框架解析epub檔案(章節內容、書籍選單)

前期準備

jsoup(可以可把html標籤,解析為物件):

進入正題

如果你是吧 .equb 格式的檔案放到 assets 檔案下,你可以這樣獲取book物件。

MainActivity.java

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app
.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView
; import com.example.epubdaemon.utils.StringUtils; import com.example.epubdaemon.utils.TimeUtils; 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.text.SimpleDateFormat
; import java.util.ArrayList; import java.util.Date; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import nl.siegmann.epublib.domain.Book; import nl.siegmann.epublib.domain.Metadata; import nl.siegmann.epublib.domain.Resource; import nl.siegmann.epublib.domain.Spine; import nl.siegmann.epublib.domain.SpineReference; import nl.siegmann.epublib.epub.EpubReader; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @BindView(R.id.image) ImageView mImage; @BindView(R.id.tv_text) TextView mTvText; @BindView(R.id.recycler) RecyclerView mRecycler; private Book book; private MyAdatper mAdatper; private List<EpubBean> indexTitleList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); initView(); } private void initView() { mRecycler.setLayoutManager(new LinearLayoutManager(this)); mAdatper = new MyAdatper(indexTitleList, this); mRecycler.setAdapter(mAdatper); try { EpubReader reader = new EpubReader(); InputStream in = getAssets().open("176116.epub"); book = reader.readEpub(in); //獲取封面圖方法一: /* Bitmap coverImage = BitmapFactory.decodeStream(book.getCoverImage().getInputStream()); if (coverImage!=null) { mImageView.setImageBitmap(coverImage); }else { Log.i(TAG, "onCreate: mImageView is null"); }*/ // 獲取封面圖方法二: /*nl.siegmann.epublib.domain.Resources resources = book.getResources(); Resource res = resources.getById("cover"); byte[] data = res.getData(); Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); mImage.setImageBitmap(bitmap);*/ Metadata metadata = book.getMetadata(); StringBuffer buffer = new StringBuffer(); for (String s : metadata.getDescriptions()) { buffer.append(s + " "); } /*String bookInfo = "作者:" + metadata.getAuthors() + "\n出版社:" + metadata.getPublishers() + "\n出版時間:" + metadata.getDates() + "\n書名:" + metadata.getTitles() + "\n簡介:" + metadata.getDescriptions() + "\n語言:" + metadata.getLanguage() + "\n\n封面圖:";*/ String bookInfo = "作者:" + metadata.getAuthors().get(0) + "\n出版社:" + metadata.getPublishers().get(0) + "\n出版時間:" + TimeUtils.getStringData(metadata.getDates().get(0).getValue()) + "\n書名:" + metadata.getTitles().get(0) + "\n簡介:" + metadata.getDescriptions().get(0) + "\n語言:" + metadata.getLanguage() + "\n\n封面圖:"; mTvText.setText(bookInfo); // Log.i(TAG, "onCreate: bookInfo=" + bookInfo); // 書籍的閱讀順序,是一個線性的順序。通過Spine可以知道應該按照怎樣的章節,順序去閱讀, // 並且通過Spine可以找到對應章節的內容。 Spine spine = book.getSpine(); List<SpineReference> spineReferences = spine.getSpineReferences(); if (spineReferences != null && spineReferences.size() > 0) { Resource resource = spineReferences.get(1).getResource();//獲取帶章節資訊的那個html頁面 Log.i(TAG, "initView: ddd=" + resource.getId() + " " + resource.getTitle() + " " + resource.getSize() + " "); byte[] data = resource.getData();//和 resource.getInputStream() 返回的都是html格式的文章內容,只不過讀取方式不一樣 String strHtml = StringUtils.bytes2Hex(data); Log.i(TAG, "initView: strHtml= " + strHtml); parseHtmlData(strHtml); /* InputStream inputStream = resource.getInputStream(); String strHtml = StringUtils.convertStreamToString(inputStream); Log.i(TAG, "initView: strHtml=" + strHtml);*/ } else { Log.i(TAG, "initView: spineReferences is null"); } // 獲取所有章節內容。測試發現和 spine.getSpineReferences() 效果差不多 /* List<Resource> contents = book.getContents(); if (contents != null && contents.size() > 0) { try { Resource resource = contents.get(1); //byte[] data = resource.getData(); InputStream inputStream = resource.getInputStream(); String dddd = StringUtils.convertStreamToString(inputStream); Log.i(TAG, "onCreate: dddd=" + dddd); //mTextView.setText(Html.fromHtml(dddd)); // mWebView.loadDataWithBaseURL(null, dddd, "text/html", "utf-8", null); } catch (IOException e) { e.printStackTrace(); } } else { Log.i(TAG, "onCreate: contents is null"); }*/ } catch (Exception e) { e.printStackTrace(); } } /** * 解析html */ private void parseHtmlData(String strHtml) throws IOException { Document doc = Jsoup.parse(strHtml); Log.i(TAG, "parseHtmlData: doc.title();=" + doc.title()); Elements eles = doc.getElementsByTag("a"); // a標籤 // 遍歷Elements的每個Element EpubBean epubBean; for (Element link : eles) { String linkHref = link.attr("href"); // a標籤的href屬性 String text = link.text(); epubBean = new EpubBean(); epubBean.href = linkHref; epubBean.tilte = text; indexTitleList.add(epubBean); //Log.i(TAG, "parseHtmlData: linkHref=" + linkHref + " text=" + text); } } private class MyAdatper extends RecyclerView.Adapter<MyAdatper.ViewHolder> { private final LayoutInflater mInflater; private List<EpubBean> mStrings; public MyAdatper(List<EpubBean> mStrings, Context context) { this.mStrings = mStrings; mInflater = LayoutInflater.from(context); } public class ViewHolder extends RecyclerView.ViewHolder { TextView mTextView; public ViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(R.id.title); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.item, null); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { holder.mTextView.setText(mStrings.get(position).tilte); holder.mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //通過href獲取 String href = mStrings.get(position).href; Intent intent = new Intent(MainActivity.this, ChapterDetailActivity.class); intent.putExtra("href", href); startActivity(intent); } }); } @Override public int getItemCount() { return mStrings.size(); } } }

佈局: R.layout.activity_main :

<?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="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:src="@mipmap/ic_launcher"/>

        <TextView
            android:id="@+id/tv_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="內容描述dddd"/>

    </LinearLayout>


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

adapter的item佈局:

<?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:background="#fff"
    android:orientation="vertical">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#fff"
        android:gravity="center_vertical"
        android:paddingLeft="10dp"
        android:text="ddd"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#eee"/>

</LinearLayout>

EpubBean.java

public class EpubBean {
    public String tilte;
    public String href;

}

ChapterDetailActivity.java

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.webkit.WebView;

import com.example.epubdaemon.utils.StringUtils;

import java.io.IOException;
import java.io.InputStream;

import butterknife.BindView;
import butterknife.ButterKnife;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Resource;
import nl.siegmann.epublib.epub.EpubReader;

public class ChapterDetailActivity extends AppCompatActivity {
    private static final String TAG = "ChapterDetailActivity";
    @BindView(R.id.webView)
    WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chapter_detail);
        ButterKnife.bind(this);

        String href = getIntent().getStringExtra("href");
        Log.i(TAG, "onCreate: href=" + href);
        try {
            EpubReader reader = new EpubReader();
            InputStream in = getAssets().open("176116.epub");
            Book book = reader.readEpub(in);
            Resource byHref = book.getResources().getByHref(href);

            byte[] data = byHref.getData();   //和 resource.getInputStream() 返回的都是html格式的文章內容,只不過讀取方式不一樣
            String strHtml1 = StringUtils.bytes2Hex(data);
            //Log.i(TAG, "initView: strHtml1111= " + strHtml1);


            mWebView.getSettings().setJavaScriptEnabled(true);
            mWebView.loadDataWithBaseURL(null, strHtml1, "text/html", "utf-8", null);


        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

佈局:R.layout.activity_chapter_detail

<?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">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></WebView>

</LinearLayout>

StringUtils.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

/**
 * Created by yuanpk on 2018/4/26  8:43
 * <p>
 * Description:TODO
 */

public class StringUtils {

    public static String convertStreamToString(InputStream is) {
        /*
          * To convert the InputStream to String we use the BufferedReader.readLine()
          * method. We iterate until the BufferedReader return null which means
          * there's no more data to read. Each line will appended to a StringBuilder
          * and returned as String.
          */
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return sb.toString();
    }


    public static String bytes2Hex(byte[] bs) {
        if (bs == null || bs.length <= 0) {
            return null;
        }
        Charset charset = Charset.defaultCharset();
        ByteBuffer buf = ByteBuffer.wrap(bs);
        CharBuffer cBuf = charset.decode(buf);

        return cBuf.toString();


    }
}

最後,176116.epub 的這個檔案和相應工具類就不在列出了,可以到下面的 “原始碼下載” 中去下載。

原始碼下載

參考部落格: