Kotlin 从入门到实战(二)

Kotlin 从入门到实战(二),第1张

概述上一篇文章的地址是:http://www.voidcn.com/article/p-eadxdfhy-bad.html 这次带来的是一个 当当网 的爬虫app。 先放出GitHub链接: https://github.com/CallMeSp/DangDang 先看一下结构分析图: 功能不复杂,但是有Activity、Adapter、自定义view、MVP结构、SQLite等一个app有的几乎一切

上一篇文章的地址是:http://www.jb51.cc/article/p-eadxdfhy-bad.html

这次带来的是一个 当当网 的爬虫app。
先放出GitHub链接:

https://github.com/CallMeSp/DangDang

先看一下结构分析图:

功能不复杂,但是有Activity、Adapter、自定义view、MVP结构、sqlite等一个app有的几乎一切。
功能实现过程说明:

在搜索框中输入要搜索的书籍名称

点击搜索后,首先判断数据库中是否有缓存

有缓存则默认加载数据库中的缓存

否则通过Jsoup去爬取

爬取的结果会存入新建的一张对应搜索名字的表

上拉加载更多:会将新加载的书籍列表存入对应数据库

下拉刷新:将已有的数据库中的缓存清空,重新爬取,并将数据写入数据库

下面进入正文:
首先这是一个爬虫,要先建立我们的model类

class Bookitem (var map:MutableMap<String,Any?>){    var name: String by map    var detail: String by map    var price: String by map    var author: String by map    var imgurl: String by map    var publisher: String by map    var detailurl:String by map    constructor(name :String,detail:String,price:String,author:String,imgurl:String,publisher:String,detailurl:String):this(HashMap()){        this.name=name        this.detail=detail        this.price=price        this.author=author        this.imgurl=imgurl        this.publisher=publisher        this.detailurl=detailurl    }}

本来单纯建立这样一个类是并不需要这个Map的,但是这里面是为了后面的sqlite *** 作做铺垫,anko框架中数据库存储和读取值得时候的映射就是要通过这个Map来实现的,要注意的是这里面每个变量的名字要和数据库table中对应的列的名字一样。

createtable(tbname,true,"ID" to INTEGER+ PRIMARY_KEY+ autoINCREMENT,"name" to TEXT,"price" to TEXT,"author" to TEXT,"publisher" to TEXT,"detail" to TEXT,"imgurl" to TEXT,"detailurl" to TEXT)

下面就来看一下关键的几个功能实现:
1.根据书名和页码获取列表

fun getBookListBynameAndPage(bookname: String,page: Int,ismore: Boolean) {    Observable.just(bookname)            .observeOn(Schedulers.newThread())            .map { s ->                mainpresenter.showPB()                val bookitemArrayList = ArrayList<Bookitem>()                val doc = Jsoup.connect("http://search.dangdang.com/?key=$s&act=input&page_index=$page").get()                Log.e("..",doc.toString())                val elements = doc.select("ul.bigimg").select("li")                for (element in elements) {                    val name = element.select("a").attr("Title")                    val detailurl = element.select("a").attr("href")                    val imgurl = if (!element.select("a").select("img").attr("src").startsWith("http"))                        element.select("a").select("img").attr("data-original")                    else                        element.select("a").select("img").attr("src")                    val detail = element.select("p.detail").text()                    val price = element.select("p.price").select("span.search_Now_price").text()                    val author = element.select("p.search_book_author").select("span")[0].select("a").attr("Title")                    val publisher = element.select("p.search_book_author").select("span")[2].select("a").text()                    val bookitem = Bookitem(name,detail,price,author,imgurl,publisher,detailurl)                    Log.e(TAG,"detailurl:"+detailurl)                    bookitemArrayList.add(bookitem)                }                bookitemArrayList            }            .subscribe(object : disposableObserver<ArrayList<Bookitem>>() {                overrIDe fun onNext(bookitems: ArrayList<Bookitem>) {                    if (!ismore) {                        mainpresenter.updateUI(bookitems)                    } else {                        mainpresenter.addMoreList(bookitems)                    }                }                overrIDe fun onError(e: Throwable) {                    mainpresenter.updateUI(ArrayList<Bookitem>())                    mainpresenter.hIDePB()                }                overrIDe fun onComplete() {                    mainpresenter.hIDePB()                }            })}

2.书籍详情界面获取procontent

fun getDetail(url:String){    Observable.just(url)            .observeOn(Schedulers.newThread())            .subscribe(object :disposableObserver<String>(){                overrIDe fun onError(e: Throwable?) {                }                overrIDe fun onComplete() {                }                overrIDe fun onNext(t: String?) {                    val detailurl = url                    Log.e(TAG,"url?:" + t)                    val doc = Jsoup.connect(detailurl).get()                    Log.e("??",doc.toString())                    var elements_content = doc.select("div.t_Box").select("div.t_Box_left")                    var procontent = doc.select("div.t_Box").select("div.t_Box_left").select("div.pro_content").select("ul").select("li")                    var contents = doc.select("div.section")                    var catalog = doc.select("div.section")                    var stb:StringBuffer= StringBuffer()                    //procontent加入换行符                    for (str:Element in procontent){                        stb.append(str.text())                        stb.append("\n")                    }                    detailpresenter.setProContent(stb.toString())                    Log.e("procontent",stb.toString())                }            })}

3.DbHelper

class DbHelper(context:Context):ManagedsqliteOpenHelper(context,DB_name,null,DB_VERSION){    val mcontext=context    @Volatile var TB_name:String=""    companion object {        val DB_name="history.db"        val DB_VERSION=1        val TAG="DbHelper"        //val instance by lazy{ DbHelper()}    }    overrIDe fun onCreate(db: sqliteDatabase?) {        Log.e(TAG,"onCreate")    }    fun insertNewtable(tbname:String,bookList:ArrayList<Bookitem>){        DbHelper(mcontext).use {            /*createtable(tbname,true,"ID" to INTEGER+ PRIMARY_KEY+ autoINCREMENT,"bookname" to TEXT,"price" to TEXT,"author" to TEXT,"publisher" to TEXT,"detail" to TEXT,"imgurl" to TEXT,"detailurl" to TEXT)*/            execsql("create table if not exists "+tbname+"(it INTEGER primary key autoINCREMENT," +                    "name text," +                    "detail text," +                    "price text," +                    "author text,"                    +"imgurl text," +                    "publisher text," +                    "detailurl text)")            Log.e(TAG,"create table:"+tbname)            bookList.forEach { insert(tbname,"name" to it.name,"price" to it.price,"author" to it.author,"publisher" to it.author,"detail" to it.detail,"imgurl" to it.imgurl,"detailurl" to it.detailurl)                Log.e(TAG,"insert into table:"+tbname+": "+it.name)}        }    }    fun addTooldtable(tbname:String,bookList:ArrayList<Bookitem>) {        DbHelper(mcontext).use {            bookList.forEach {                insert(tbname,"insert into table:"+tbname+": "+it.name)            }        }    }    fun getListFormDb(bookname:String)= DbHelper(mcontext).use {        select(bookname).parseList {            Bookitem(HashMap(it))        }    }    fun droptable(bookname: String)=DbHelper(mcontext).use {        clear(bookname)    }    fun IshaveTB(bookname:String):Boolean=DbHelper(mcontext).use {        var result:Boolean=false        var cursor=rawquery("select count(*) as c from sqlite_master where type ='table' and name ="+"\'"+bookname+"\';",null)        if (cursor.movetoNext()){            var count:Int=cursor.getInt(0)            if (count>0){                result=true            }        }        result    }    overrIDe fun onUpgrade(db: sqliteDatabase?,oldVersion: Int,newVersion: Int) {        db!!.droptable(TB_name,true)        onCreate(db)    }    fun <T : Any> SelectqueryBuilder.parseList(parser: (Map<String,Any?>) -> T): List<T> =            parseList(object : MapRowParser<T> {                overrIDe fun parseRow(columns: Map<String,Any?>): T = parser(columns)            })    fun <T : Any> SelectqueryBuilder.parSEOpt(parser: (Map<String,Any?>) -> T): T? =            parSEOpt(object : MapRowParser<T> {                overrIDe fun parseRow(columns: Map<String,Any?>): T = parser(columns)            })    fun sqliteDatabase.clear(tablename: String) {        execsql("delete from $tablename")    }}

4.上拉下拉监听:

class SwipeRefreshVIEw(context: Context,attrs: AttributeSet) : SwipeRefreshLayout(context,attrs) {    private val mScaledtouchSlop: Int    private val mFooterVIEw: VIEw    private var recyclervIEw: RecyclerVIEw? = null    @Volatile private var condition2: Boolean = false    private var MonLoadListener:()->Unit={}    /** * 正在加载状态 */    private var isLoading: Boolean = false    internal var layoutInflater: LayoutInflater    init {        // 填充底部加载布局        mFooterVIEw = VIEw.inflate(context,R.layout.vIEw_footer,null)        // 表示控件移动的最小距离,手移动的距离大于这个距离才能拖动控件        mScaledtouchSlop = VIEwConfiguration.get(context).scaledtouchSlop        layoutInflater = LayoutInflater.from(context)    }    overrIDe fun onLayout(changed: Boolean,left: Int,top: Int,right: Int,bottom: Int) {        super.onLayout(changed,left,top,right,bottom)        // recyclervIEw,设置recyclervIEw的布局位置        if (recyclervIEw == null) {            // 判断容器有多少个孩子            if (childCount > 0) {                // 判断第一个孩子是不是ListVIEw                if (getChildAt(0) is RecyclerVIEw) {                    // 创建ListVIEw对象                    recyclervIEw = getChildAt(0) as RecyclerVIEw                    // 设置ListVIEw的滑动监听                    setRecyclerVIEwOnScroll()                }            }        }    }    /** * 在分发事件的时候处理子控件的触摸事件 * @param ev * * * @return */    private var mDownY: float = 0.tofloat()    private var mUpY: float = 0.tofloat()    overrIDe fun dispatchtouchEvent(ev: MotionEvent): Boolean {        when (ev.action) {            MotionEvent.ACTION_DOWN ->                // 移动的起点                mDownY = ev.y            MotionEvent.ACTION_MOVE ->                // 移动过程中判断时候能下拉加载更多                if (condition2 && canLoadMore()) {                    // 加载数据                    loadData()                    return false                }            MotionEvent.ACTION_UP ->                // 移动的终点                mUpY = y        }        return super.dispatchtouchEvent(ev)    }    /** * 判断是否满足加载更多条件 * @return */    private fun canLoadMore(): Boolean {        // 1. 是上拉状态        val condition1 = mDownY - mUpY >= mScaledtouchSlop        if (condition1) {            println("是上拉状态")        }        // 3. 正在加载状态        val condition3 = !isLoading        if (condition3) {            println("不是正在加载状态")        }        return condition1 && condition3    }    /** * 处理加载数据的逻辑 */    private fun loadData() {        println("加载数据...")        if (MonLoadListener != null) {            // 设置加载状态,让布局显示出来            setLoading(true)            MonLoadListener()        }    }    /** * 设置加载状态,是否加载传入boolean值进行判断 * @param loading */    fun setLoading(loading: Boolean) {        // 修改当前的状态        isLoading = loading     }    /** * 设置RecyclerVIEw的滑动监听 */    private fun setRecyclerVIEwOnScroll() {        recyclervIEw!!.setonScrollchangelistener { v,scrollX,scrollY,oldScrollX,oldScrollY ->            val layoutManager = recyclervIEw!!.layoutManager            val linearManager = layoutManager as linearlayoutmanager            val lastItemposition = linearManager.findLastVisibleItemposition()            if (recyclervIEw!!.adapter.itemCount - 1 == lastItemposition) {                condition2 = true            } else {                condition2 = false            }        }    }    fun setCondition2(isLoading: Boolean) {        condition2 = isLoading    }    /** * 上拉加载的接口回调 */    fun setMOnLoadListener(Listener:()->Unit){        MonLoadListener=Listener    }    companion object {        private val TAG = "SwipeRefreshVIEw"    }}

要注意的是这里的DBhelper中的扩展函数。
如:

fun droptable(bookname: String)=DbHelper(mcontext).use {        clear(bookname)    }
Kotlin的扩展函数功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。

扩展函数是静态解析的,并未对原类添加函数或属性,对类本身没有任何影响。

扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。

总结

以上是内存溢出为你收集整理的Kotlin 从入门实战(二)全部内容,希望文章能够帮你解决Kotlin 从入门到实战(二)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/1163129.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-01
下一篇 2022-06-01

发表评论

登录后才能评论

评论列表(0条)

保存