Просмотр исходного кода

Feature: PDF search in left toolbar

cooperku_kdanmobile 5 лет назад
Родитель
Сommit
1185bffc14

+ 15 - 2
reader/src/main/java/com/kdanmobile/reader/ReaderActivity.kt

@@ -16,6 +16,7 @@ import android.view.View
 import com.kdanmobile.kmpdfkit.pdfcommon.KMPDFReaderView
 import com.kdanmobile.reader.Utils.applyConstraintSet
 import com.kdanmobile.reader.screen.view.OutlineView
+import com.kdanmobile.reader.screen.view.SearchView
 import com.kdanmobile.reader.screen.view.ThumbnailView
 import com.kdanmobile.reader.utils.AnimationUtil
 import com.kdanmobile.reader.utils.DensityUtil
@@ -180,6 +181,7 @@ open class ReaderActivity : AppCompatActivity() {
             AnimationUtil.hideViewFromTopToBottom(viewGroup_readerActivity_bottomToolbar, UI_ANIMATION_DURATION)
             AnimationUtil.hideViewFromRightToLeft(viewGroup_readerActivity_leftToolbar, UI_ANIMATION_DURATION)
             AnimationUtil.hideViewFromLeftToRight(viewGroup_readerActivity_rightToolbar, UI_ANIMATION_DURATION)
+            hideSearchViewSoftKeyboard()
         } else {
             constrainLayout_readerActivity_root.applyConstraintSet(this, R.layout.activity_reader_hide_all, UI_ANIMATION_DURATION)
         }
@@ -190,11 +192,21 @@ open class ReaderActivity : AppCompatActivity() {
             AnimationUtil.hideViewFromBottomToTop(toolbar_readerActivity, UI_ANIMATION_DURATION)
             AnimationUtil.hideViewFromTopToBottom(viewGroup_readerActivity_bottomToolbar, UI_ANIMATION_DURATION)
             AnimationUtil.hideViewFromRightToLeft(viewGroup_readerActivity_leftToolbar, UI_ANIMATION_DURATION)
+            hideSearchViewSoftKeyboard()
         } else {
             constrainLayout_readerActivity_root.applyConstraintSet(this, R.layout.activity_reader_hide_top_left_bottom, UI_ANIMATION_DURATION)
         }
     }
 
+    private fun hideSearchViewSoftKeyboard() {
+        if (leftToolbarType == LeftToolbarType.SEARCH) {
+            val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
+            if (view is SearchView) {
+                view.hideSoftKeyboard()
+            }
+        }
+    }
+
     private fun setupLeftToolbar() {
         viewGroup_readerActivity_leftToolbar.setScroll(false)
         setLeftToolbarWidth(w_240)
@@ -303,12 +315,13 @@ open class ReaderActivity : AppCompatActivity() {
         if (isLeftToolbarOpen) {
             if (leftToolbarType != type) {
                 linearLayout_readerActivity_leftToolbarContainer.removeAllViews()
-                val child = layoutInflater.inflate(R.layout.view_thumbnail, null)
-                linearLayout_readerActivity_leftToolbarContainer.addView(child)
+                val outlineView = SearchView(this, viewModel)
+                linearLayout_readerActivity_leftToolbarContainer.addView(outlineView)
             }
             iv_readerActivity_search.setImageResource(R.drawable.ic_pagelist_search_h)
         } else {
             iv_readerActivity_search.setImageResource(R.drawable.ic_pagelist_search_nor)
+            hideSearchViewSoftKeyboard()
         }
         leftToolbarType = type
     }

+ 34 - 2
reader/src/main/java/com/kdanmobile/reader/ReaderViewModel.kt

@@ -4,6 +4,7 @@ import android.arch.lifecycle.MutableLiveData
 import android.arch.lifecycle.ViewModel
 import android.content.Context
 import android.graphics.Bitmap
+import android.graphics.RectF
 import android.net.Uri
 import com.kdanmobile.kmpdfkit.annotation.bean.*
 import com.kdanmobile.kmpdfkit.globaldata.Config
@@ -11,11 +12,13 @@ import com.kdanmobile.kmpdfkit.manager.KMPDFFactory
 import com.kdanmobile.kmpdfkit.manager.controller.KMPDFDocumentController
 import com.kdanmobile.kmpdfkit.pdfcommon.KMPDFReaderView
 import com.kdanmobile.kmpdfkit.pdfcommon.OutlineItem
+import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
 import com.kdanmobile.reader.screen.handler.OutlineHandler
 import com.kdanmobile.reader.screen.handler.PdfInfoHandler
+import com.kdanmobile.reader.screen.handler.SearchHandler
 import com.kdanmobile.reader.screen.handler.ThumbnailHandler
 
-class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMsg: String) : ViewModel(), PdfInfoHandler, ThumbnailHandler, OutlineHandler {
+class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMsg: String) : ViewModel(), PdfInfoHandler, ThumbnailHandler, OutlineHandler, SearchHandler {
     enum class ViewDirection(val mode: Config.PDFViewMode) {
         VerticalSinglePageContinues(Config.PDFViewMode.VERTICAL_SINGLE_PAGE_CONTINUES),
         VerticalSinglePage(Config.PDFViewMode.VERTICAL_SINGLE_PAGE),
@@ -144,7 +147,7 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
     }
 
     fun setReaderView(readerView: KMPDFReaderView) {
-        kmpdfFactory?.setReaderView(readerView)
+        kmpdfFactory?.readerView = readerView
         kmpdfDocumentController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.DOCUMENT) as KMPDFDocumentController
         updateViewDirection()
     }
@@ -186,6 +189,10 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
         return kmpdfDocumentController?.gotoPage(page)
     }
 
+    override fun textLines(page: Int): Array<Array<TextWord>>? {
+        return kmpdfDocumentController?.textLines(page)
+    }
+
     //  ====================================================================
     //  implement ThumbnailHandler
     //  ====================================================================
@@ -201,4 +208,29 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
     override fun getOutline(): Array<OutlineItem>? {
         return kmpdfDocumentController?.outline
     }
+
+    //  ====================================================================
+    //  implement SearchHandler
+    //  ====================================================================
+
+    override fun searchPage(page: Int, keyword: String): Array<RectF>? {
+        return kmpdfDocumentController?.searchPage(page, keyword)
+    }
+
+    override fun cleanPreviousSearchResults(keyword: String): Boolean? {
+        return kmpdfDocumentController?.cleanPreviousSearchResults(keyword)
+    }
+
+    override fun cleanPreviousSearchResults(page: Int, keyword: String, rectArray: Array<RectF>): Boolean? {
+        return kmpdfDocumentController?.setSearchResult(keyword, page, rectArray)
+    }
+
+    override fun stopSearchKeyWord(): Boolean? {
+        return kmpdfDocumentController?.stopSearchKeyWord()
+    }
+
+    override fun updateSearchBoxes() {
+        val pageView = kmpdfFactory?.readerView?.displayedView
+
+    }
 }

+ 88 - 0
reader/src/main/java/com/kdanmobile/reader/screen/adapter/SearchAdapter.kt

@@ -0,0 +1,88 @@
+package com.kdanmobile.reader.screen.adapter
+
+import android.text.Html
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import android.widget.TextView
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.model.SearchResultInfo
+import java.util.ArrayList
+
+class SearchAdapter(private var width: Int): BaseAdapter() {
+    private var list: MutableList<SearchResultInfo>
+    private var selected = -1
+
+    init {
+        this.width = width
+        list = ArrayList()
+    }
+
+    fun clear() {
+        list.clear()
+    }
+
+    fun add(list: List<SearchResultInfo>) {
+        this.list.addAll(list)
+    }
+
+    override fun getCount(): Int {
+        return list.size
+    }
+
+    override fun getItem(position: Int): Any {
+        return list[position]
+    }
+
+    override fun getItemId(position: Int): Long {
+        return position.toLong()
+    }
+
+    fun setSelected(position: Int) {
+        this.selected = position
+        notifyDataSetChanged()
+    }
+
+    override fun getView(position: Int, view: View?, parent: ViewGroup): View {
+        var view = view
+        val item = list[position]
+        val hv: HolderView
+        if (view == null) {
+            view = LayoutInflater.from(parent.context).inflate(R.layout.item_search, null)
+            hv = HolderView()
+            hv.tv_result = view!!.findViewById<View>(R.id.tv_fragmentPdfReaderSearchLvItem_result) as TextView
+            hv.tv_page = view.findViewById<View>(R.id.tv_fragmentPdfReaderSearchLvItem_page) as TextView
+            hv.view = view.findViewById(R.id.view_fragmentPdfReaderSearchLvItem_selected)
+            hv.rl = view .findViewById<View>(R.id.rl_fragmentPdfReaderSearchLvItem_) as RelativeLayout
+            val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+            params.leftMargin = 21 * width / 800
+            params.rightMargin = 21 * width / 800
+            params.topMargin = 8 * width / 800
+            hv.rl!!.layoutParams = params
+            view.tag = hv
+        } else {
+            hv = view.tag as HolderView
+        }
+
+        if (selected == position) {
+            hv.view!!.setBackgroundResource(R.drawable.rectangle_6633b5e5)
+        } else {
+            hv.view!!.setBackgroundResource(R.drawable.rectangle_0000)
+        }
+
+        hv.tv_result!!.text = Html.fromHtml(item.result)
+        hv.tv_page!!.text = "" + (item.page + 1)
+
+        return view
+    }
+
+    private inner class HolderView {
+        var rl: RelativeLayout? = null
+        var tv_result: TextView? = null
+        var tv_page: TextView? = null
+        var view: View? = null
+    }
+}

+ 4 - 0
reader/src/main/java/com/kdanmobile/reader/screen/handler/PdfInfoHandler.kt

@@ -1,5 +1,7 @@
 package com.kdanmobile.reader.screen.handler
 
+import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
+
 interface PdfInfoHandler {
 
     fun getOpenPdfFilename(): String?
@@ -9,4 +11,6 @@ interface PdfInfoHandler {
     fun getCurrentPage(): Int?
 
     fun setCurrentPage(page: Int): Boolean?
+
+    fun textLines(page: Int): Array<Array<TextWord>>?
 }

+ 16 - 0
reader/src/main/java/com/kdanmobile/reader/screen/handler/SearchHandler.kt

@@ -0,0 +1,16 @@
+package com.kdanmobile.reader.screen.handler
+
+import android.graphics.RectF
+
+interface SearchHandler {
+
+    fun searchPage(page: Int, keyword: String): Array<RectF>?
+
+    fun cleanPreviousSearchResults(keyword: String): Boolean?
+
+    fun cleanPreviousSearchResults(page: Int, keyword: String, rectArray: Array<RectF>): Boolean?
+
+    fun stopSearchKeyWord(): Boolean?
+
+    fun updateSearchBoxes()
+}

+ 56 - 0
reader/src/main/java/com/kdanmobile/reader/screen/model/SearchResultInfo.kt

@@ -0,0 +1,56 @@
+package com.kdanmobile.reader.screen.model
+
+import android.graphics.RectF
+import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
+
+class SearchResultInfo {
+    lateinit var result: String
+    var page: Int = 0
+    lateinit var rf: RectF
+    lateinit var search: String
+    lateinit var line: Array<TextWord>
+
+    constructor() {
+
+    }
+
+    constructor(result: String, page: Int) {
+        this.result = result
+        this.page = page
+    }
+
+    constructor(page: Int, search: String, rf: RectF, line: Array<TextWord>) {
+        this.page = page
+        this.search = search
+        this.rf = rf
+        this.line = line
+        result = ""
+        for (aLine in line) {
+            result = result + aLine.w + " "
+        }
+
+        this.result = replace(result, search)
+    }
+
+    private fun replace(result: String, search: String): String {
+        var result = result
+        val sb = StringBuilder()
+        var src = result.toLowerCase()
+        val seh = search.toLowerCase()
+        var index = src.indexOf(seh)
+        while (index >= 0) {
+            sb.append(src.subSequence(0, index))
+            sb.append("<font color=\"#ff0000\">")
+            result = result.substring(index)
+            sb.append(result.substring(0, search.length))
+            sb.append("</font>")
+            result = result.substring(search.length)
+            src = src.substring(index + search.length)
+            index = src.indexOf(seh)
+        }
+
+        sb.append(result)
+
+        return sb.toString()
+    }
+}

+ 17 - 0
reader/src/main/java/com/kdanmobile/reader/screen/model/SearchTaskResult.kt

@@ -0,0 +1,17 @@
+package com.kdanmobile.reader.screen.model
+
+import android.graphics.RectF
+
+class SearchTaskResult(val txt: String, val pageNumber: Int, val searchBoxes: Array<RectF>) {
+    companion object {
+        private var singleton: SearchTaskResult? = null
+
+        fun get(): SearchTaskResult? {
+            return singleton
+        }
+
+        fun set(result: SearchTaskResult) {
+            singleton = result
+        }
+    }
+}

+ 26 - 0
reader/src/main/java/com/kdanmobile/reader/screen/model/SimpleTextWatcher.kt

@@ -0,0 +1,26 @@
+package com.kdanmobile.reader.screen.model
+
+import android.text.Editable
+import android.text.TextWatcher
+
+/**
+ * @类名:SimpleTextWatcher
+ * @类描述:重写TextWatcher
+ * @作者:luozhipeng
+ * @创建时间:2015年7月7日-下午4:14:58
+ * @修改人:
+ * @修改时间:
+ * @修改备注:
+ * @版本:
+ * @Copyright:(c)-2015kdanmobile
+ */
+open class SimpleTextWatcher : TextWatcher {
+    override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {
+    }
+
+    override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
+    }
+
+    override fun afterTextChanged(editable: Editable) {
+    }
+}

+ 365 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/SearchView.kt

@@ -0,0 +1,365 @@
+package com.kdanmobile.reader.screen.view
+
+import android.content.Context
+import android.graphics.RectF
+import android.os.Handler
+import android.os.Message
+import android.text.InputType
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.*
+import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.ReaderViewModel
+import com.kdanmobile.reader.screen.adapter.SearchAdapter
+import com.kdanmobile.reader.screen.model.SearchResultInfo
+import com.kdanmobile.reader.screen.handler.PdfInfoHandler
+import com.kdanmobile.reader.screen.handler.SearchHandler
+import com.kdanmobile.reader.screen.model.SearchTaskResult
+import com.kdanmobile.reader.screen.model.SimpleTextWatcher
+import com.kdanmobile.reader.utils.DensityUtil
+import io.reactivex.Completable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import java.util.ArrayList
+import kotlinx.android.synthetic.main.view_search.view.*
+import java.lang.ref.WeakReference
+
+class SearchView: RelativeLayout, View.OnClickListener {
+    private lateinit var pdfInfoHandler: PdfInfoHandler
+    private lateinit var searchHandler: SearchHandler
+    private lateinit var linearLayout: LinearLayout
+    private lateinit var tv_page: TextView
+    private lateinit var tv_text:TextView
+    private lateinit var et_search: EditText
+    private lateinit var lv: ListView
+    private lateinit var tv_tishi: TextView
+    private lateinit var adapter: SearchAdapter
+    private val list = ArrayList<SearchResultInfo>()
+
+    // 判断搜索的是否是页码 true 是页码
+    private var isPage = false
+
+    private var msgHandler: Handler? = null
+
+    private val MAX_SEARCH_RESULT = 200
+
+    private var isDone = false
+    private var deviceWidth: Int = 0
+
+    private var disposable: Disposable? = null
+
+    constructor(context: Context, viewModel: ReaderViewModel) : super(context) {
+        initView(viewModel, viewModel)
+    }
+
+    constructor(context: Context, attributeSet: AttributeSet, viewModel: ReaderViewModel) : super(context, attributeSet) {
+        initView(viewModel, viewModel)
+    }
+
+    constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, viewModel: ReaderViewModel) : super(context, attributeSet, defStyleAttr) {
+        initView(viewModel, viewModel)
+    }
+
+    private fun initView(pdfInfoHandler: PdfInfoHandler, searchHandler: SearchHandler) {
+        LayoutInflater.from(context).inflate(R.layout.view_search, this)
+
+        this.pdfInfoHandler = pdfInfoHandler
+        this.searchHandler = searchHandler
+
+        initHandler()
+
+        initView()
+
+        setListener()
+
+        postDelayed( { showSoftKeyboard() }, 200)
+    }
+
+    private fun initHandler() {
+
+        class MyHandler(view: SearchView) : Handler() {
+            private val mView: WeakReference<SearchView> = WeakReference(view)
+
+            override fun handleMessage(msg: Message) {
+                val view = mView.get()
+                if (view != null) {
+                    when (msg.what) {
+                        10 -> {
+                            if (msg.obj is List<*>) {
+                                view.lv.visibility = View.VISIBLE
+                                view.tv_tishi.visibility = View.GONE
+                                val item = (msg.obj as List<*>).filterIsInstance<SearchResultInfo>()
+                                view.list.addAll(item)
+                                view.adapter.add(item)
+                                view.adapter.notifyDataSetChanged()
+                            }
+                        }
+                        20 -> search()
+                        30 -> {
+                            val position = msg.arg1
+                            val info = view.list[position]
+                            SearchTaskResult.set(SearchTaskResult(info.search, info.page, arrayOf(info.rf)))
+                            view.pdfInfoHandler.setCurrentPage(view.list[position].page)
+                        }
+                    }
+                }
+            }
+        }
+        msgHandler = MyHandler(this)
+    }
+
+    private fun initView() {
+
+        linearLayout = ll_fragmentPdfReaderSearch
+        tv_page = tv_fragmentPdfReaderSearch_page
+        tv_text = tv_fragmentPdfReaderSearch_text
+        et_search = et_fragmentPdfReaderSearch_
+
+        et_search.setHint(R.string.fragment_search_enter_text)
+        et_search.inputType = InputType.TYPE_CLASS_TEXT
+        et_search.isLongClickable = false
+        lv = lv_fragmentPdfReaderSearch_
+        tv_tishi = tv_fragmentPdfReaderSearch_tishi
+
+        var navigationHeight = 0
+        val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+        if (resourceId > 0) {
+            val rid = resources.getIdentifier("config_showNavigationBar", "bool", "android")
+            if (resources.getBoolean(rid)) {
+                navigationHeight = resources.getDimensionPixelSize(resourceId)
+            }
+        }
+        val screenWidth = DensityUtil.getScreenWidthPx(context)
+        val screenHeight = DensityUtil.getScreenHeightPx(context)
+        val density = DensityUtil.getDensity(context)
+        val xDp = (screenWidth / density).toInt()
+        val yDp = ((screenHeight + navigationHeight) / density).toInt()
+        val isPhone = when (xDp >= 550 && yDp >= 550) {
+            true -> false
+            false -> true
+        }
+        deviceWidth = Math.min(screenWidth, screenHeight)
+
+        linearLayout.layoutParams = LinearLayout.LayoutParams(318 * deviceWidth / 800, LinearLayout.LayoutParams.MATCH_PARENT)
+
+        val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+        params.leftMargin = 21 * deviceWidth / 800
+        params.rightMargin = 21 * deviceWidth / 800
+        params.topMargin = 8 * deviceWidth / 800
+        params.bottomMargin = 8 * deviceWidth / 800
+        et_search.layoutParams = params
+        val pad = (density * 4).toInt()
+        et_search.setPadding(pad, pad, pad, pad)
+
+        if (isPhone) {
+            tv_tishi.textSize = 18f
+        } else {
+            val textSize = deviceWidth.toFloat() / 800f / density
+            tv_tishi.textSize = 24 * textSize
+        }
+
+        adapter = SearchAdapter(deviceWidth)
+        lv.adapter = adapter
+    }
+
+    private fun setListener() {
+
+        tv_page.setOnClickListener(this)
+        tv_text.setOnClickListener(this)
+
+        lv.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
+            adapter.setSelected(position)
+
+            msgHandler?.sendMessageDelayed(msgHandler?.obtainMessage(30, position, 0), 50)
+        }
+
+        et_search.setOnEditorActionListener { v, actionId, event ->
+            // if (actionId == EditorInfo.IME_ACTION_DONE) {
+            // search();
+            // }
+            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+                if (!isPage && et_search.text.toString().length < 3) {
+                    val la = IntArray(2)
+                    et_search.getLocationOnScreen(la)
+                    val y = la[1]
+                    val toast = Toast.makeText(context, R.string.fileManager_thishi_search, Toast.LENGTH_SHORT)
+                    toast.setGravity(Gravity.TOP or Gravity.LEFT, 11 * deviceWidth / 800, y + 30 * deviceWidth / 800)
+                    toast.show()
+                } else {
+                    isDone = true
+                    msgHandler?.sendEmptyMessageDelayed(20, 100)
+                    hideSoftKeyboard()
+                }
+            }
+            return@setOnEditorActionListener false
+        }
+
+        et_search.addTextChangedListener(object : SimpleTextWatcher() {
+            override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
+                super.onTextChanged(sequence, start, before, count)
+                try {
+                    if (isPage && sequence.isNotEmpty()) {
+                        val c = sequence[sequence.length - 1]
+                        val index = sequence[0]
+                        if (c < '0' || c > '9' || index == '0') {
+                            val text = sequence.subSequence(0, sequence.length - 1).toString()
+                            et_search.setText(text)
+                            et_search.setSelection(text.length)
+                        }
+                        val searchSequence = et_search.text.toString()
+                        if (searchSequence.isNotEmpty()) {
+                            val page = Integer.parseInt(searchSequence)
+                            val pageCount = pdfInfoHandler.getPdfPageCount() ?: 0
+                            if (page > pageCount) {
+                                val text = searchSequence.subSequence(0, searchSequence.length - 1).toString()
+                                et_search.setText(text)
+                                et_search.setSelection(text.length)
+                            }
+                        }
+                    }
+                } catch (e: Exception) {
+                    e.printStackTrace()
+                    et_search.setText("")
+                }
+
+            }
+        })
+    }
+
+    override fun onClick(v: View) {
+
+        when (v.id) {
+            R.id.tv_fragmentPdfReaderSearch_page -> {
+                if (!isPage) {
+                    et_search.isLongClickable = false
+                    tv_page.setTextColor(-0xcc4a1b)
+                    tv_text.setTextColor(-0x505051)
+                    et_search.setText("")
+                    et_search.setHint(R.string.fragment_search_enter_page)
+                    et_search.inputType = InputType.TYPE_CLASS_NUMBER
+                    et_search.setSingleLine(true)
+
+                    lv.visibility = View.GONE
+                    tv_tishi.visibility = View.GONE
+
+                    showSoftKeyboard()
+                }
+
+                isPage = true
+            }
+            R.id.tv_fragmentPdfReaderSearch_text -> {
+                if (isPage) {
+                    et_search.isLongClickable = true
+                    tv_page.setTextColor(-0x505051)
+                    tv_text.setTextColor(-0xcc4a1b)
+                    et_search.setText("")
+                    et_search.setHint(R.string.fragment_search_enter_text)
+                    et_search.inputType = InputType.TYPE_CLASS_TEXT
+                    et_search.setSingleLine(true)
+
+                    lv.visibility = View.VISIBLE
+                    tv_tishi.visibility = View.GONE
+
+                    showSoftKeyboard()
+                }
+
+                isPage = false
+            }
+        }
+    }
+
+    private fun search() {
+        val search = et_search.text.toString()
+        if (search.isNotEmpty()) {
+            if (isPage) {
+                val page = Integer.parseInt(search)
+                if (page > 0) {
+                    pdfInfoHandler.setCurrentPage(page - 1)
+                }
+            } else if (search.length >= 3) {
+                if (list.size > 0) {
+                    adapter.setSelected(-1)
+                    adapter.clear()
+                    list.clear()
+                    adapter.notifyDataSetChanged()
+                }
+
+                isDone = false
+                disposable = Completable.create {
+                    val pageCount = pdfInfoHandler.getPdfPageCount()!!
+                    for (i in 0 until pageCount) {
+                        if (!isDone) {
+                            val results = searchHandler.searchPage(i, search)
+                            if (results?.isNotEmpty() != null) {
+                                val texts = pdfInfoHandler.textLines(i)
+                                val l = dealData(texts, results, search, i)
+                                msgHandler?.sendMessage(msgHandler?.obtainMessage(10, l))
+                                if (list.size + l.size >= MAX_SEARCH_RESULT) {
+                                    isDone = true
+                                }
+                            }
+                        }
+                    }
+                    it.onComplete()
+                }
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread())
+                        .subscribe({
+                            if (list.size < 1) {
+                                lv.visibility = View.GONE
+                                tv_tishi.visibility = View.VISIBLE
+                            }
+                        }, {
+                            it.printStackTrace()
+                        })
+            }
+        }
+    }
+
+    private fun dealData(texts: Array<Array<TextWord>>?, results: Array<RectF>?, search: String, page: Int): List<SearchResultInfo> {
+        val list = ArrayList<SearchResultInfo>()
+        if (texts == null || results == null) {
+            return list
+        }
+        for (i in texts.indices) {
+            if (!isDone) {
+                val tw = texts[i]
+                for (j in tw.indices) {
+                    if (!isDone) {
+                        for (k in results.indices) {
+                            if (!isDone) {
+                                if (tw[j].contains(results[k])) {
+                                    list.add(SearchResultInfo(page, search, results[k], tw))
+                                    if (this.list.size + list.size >= MAX_SEARCH_RESULT) {
+                                        return list
+                                    }
+                                }
+                            }
+
+                        }
+                    }
+
+                }
+            }
+
+        }
+        return list
+    }
+
+    private fun showSoftKeyboard() {
+        val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+        et_search.requestFocus()
+        inputMethodManager.showSoftInput(et_search, InputMethodManager.RESULT_UNCHANGED_SHOWN)
+    }
+
+    fun hideSoftKeyboard() {
+        val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+        inputMethodManager.hideSoftInputFromWindow(et_search.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
+    }
+}

+ 8 - 0
reader/src/main/java/com/kdanmobile/reader/utils/DensityUtil.kt

@@ -11,6 +11,14 @@ class DensityUtil private constructor() {
 
     companion object {
 
+        fun getDensityDpi(context: Context): Int {
+            return context.resources.displayMetrics.densityDpi
+        }
+
+        fun getDensity(context: Context): Float {
+            return context.resources.displayMetrics.density
+        }
+
         fun getScreenWidthPx(context: Context): Int {
             return context.resources.displayMetrics.widthPixels
         }