最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android性能優(yōu)化之RecyclerView分頁加載組件功能詳解

 更新時(shí)間:2022年09月05日 10:07:02   作者:Ghelper  
這篇文章主要為大家介紹了Android性能優(yōu)化之RecyclerView分頁加載組件功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

在Android應(yīng)用中,列表有著舉足輕重的地位,幾乎所有的應(yīng)用都有列表的身影,但是對于列表的交互體驗(yàn)一直是一個(gè)大問題。在性能比較好的設(shè)備上,列表滑動幾乎看不出任何卡頓,但是放在低端機(jī)上,卡頓會比較明顯,而且列表中經(jīng)常會伴隨圖片的加載,卡頓會更加嚴(yán)重,因此本章從手寫分頁加載組件入手,并對列表卡頓做出對應(yīng)的優(yōu)化

1 分頁加載組件

為什么要分頁加載,通常列表數(shù)據(jù)存儲在服務(wù)端會超過100條,甚至上千條,如果服務(wù)端一次性返回,我們一次性接受直接加載,如果其中有圖片加載,肯定直接報(bào)OOM,應(yīng)用崩潰,因此我們通常會跟服務(wù)端約定分頁的規(guī)則,服務(wù)端會按照頁碼從0開始給數(shù)據(jù),或者在數(shù)據(jù)中返回下一頁對應(yīng)的索引,當(dāng)出發(fā)分頁加載時(shí),就會拿到下一頁的頁碼請求新一頁的數(shù)據(jù)。

目前在JetPack組件中,Paging是使用比較多的一個(gè)分頁加載組件,但是Paging使用的場景有限,因?yàn)榱鞯南拗?,?dǎo)致只能是單一數(shù)據(jù)源,而且數(shù)據(jù)不能斷,只能全部加載進(jìn)來,因此決定手寫一個(gè)分頁加載組件,適用多種場景。

1.1 功能定制

如果想要自己寫一個(gè)分頁加載庫,首先需要明白,分頁加載組件需要做什么事?

對于RecyclerView來說,它的主要功能就是創(chuàng)建視圖并綁定數(shù)據(jù),因此我們先定義分頁列表的基礎(chǔ)能力,綁定視圖和數(shù)據(jù)

interface IPagingList<T> {
    fun bindView(context: Context,lifecycleOwner: LifecycleOwner, recyclerView: RecyclerView,adapter: PagingAdapter<T>,mode: ListMode) {}
    fun bindData(model: List<BasePagingModel<T>>) {}
}

bindData:

bindData就不多說了,就是綁定數(shù)據(jù),首先我們拿到的數(shù)據(jù)一定是一個(gè)列表數(shù)據(jù),因?yàn)椴⒉恢罉I(yè)務(wù)方需要展示的數(shù)據(jù)類型是啥樣的,因此需要泛型修飾,那么BasePagingModel是干什么的呢?

open class BasePagingModel<T>(
    var pageCount: String = "", //頁碼
    var type: Int = 1, //分頁類型 1 帶日期 2 普通列表
    var time: String = "", //如果是帶日期的model,那么需要傳入此值
    var itemData: T? = null
)

首先BasePagingModel是分頁列表中數(shù)據(jù)的基類,其中存儲的元素包括pageCount,代表傳進(jìn)來的數(shù)據(jù)列表是哪一頁,type用來區(qū)分列表數(shù)據(jù)類型,time可以代表當(dāng)前數(shù)據(jù)在服務(wù)端的時(shí)間(主要場景就是列表中數(shù)據(jù)展示需要帶時(shí)間,并根據(jù)某一天進(jìn)行數(shù)據(jù)聚合),itemData代表業(yè)務(wù)層需要處理的數(shù)據(jù)。

bindView:

對于RecyclerView來說,創(chuàng)建視圖、展示數(shù)據(jù)需要適配器,因此這里傳入了RecyclerView還有通用的適配器PagingAdapter

abstract class PagingAdapter<T> : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private var datas: List<BasePagingModel<T>>? = null
    private var maps: MutableMap<String, MutableList<BasePagingModel<T>>>? = null
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return buildBusinessHolder(parent, viewType)
    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (datas != null) {
            bindBusinessData(holder, position, datas)
        } else if (maps != null) {
            bindBusinessMapData(holder, position, maps)
        }
    }
    abstract fun getHolderWidth(context: Context):Int
    override fun getItemCount(): Int {
        return if (datas != null) datas!!.size else 0
    }
    open fun bindBusinessMapData(
        holder: RecyclerView.ViewHolder,
        position: Int,
        maps: MutableMap<String, MutableList<BasePagingModel<T>>>?
    ) {
    }
    open fun bindBusinessData(
        holder: RecyclerView.ViewHolder,
        position: Int,
        datas: List<BasePagingModel<T>>?
    ) {
    }
    abstract fun buildBusinessHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
    fun setPagingData(datas: List<BasePagingModel<T>>) {
        this.datas = datas
        notifyDataSetChanged()
    }
    fun setPagingMapData(maps: MutableMap<String, MutableList<BasePagingModel<T>>>) {
        this.maps = maps
        notifyDataSetChanged()
    }
}

這一章,我們先介紹使用場景比較多的單數(shù)據(jù)列表

PagingAdapter是一個(gè)抽象類,攜帶的數(shù)據(jù)同樣是業(yè)務(wù)方需要處理的數(shù)據(jù),是一個(gè)泛型,創(chuàng)建視圖方法buildBusinessHolder交給業(yè)務(wù)方實(shí)現(xiàn),這里我們關(guān)注兩個(gè)數(shù)據(jù)相關(guān)的方法 bindBusinessData和setPagingData,當(dāng)調(diào)用setPagingData方法時(shí),將處理好的數(shù)據(jù)列表發(fā)進(jìn)來,然后調(diào)用notifyDataSetChanged方法刷新列表,這個(gè)時(shí)候會調(diào)用bindBusinessData將列表中的數(shù)據(jù)綁定并展示出來。

這里我們還需要關(guān)注一個(gè)方法,這個(gè)方法業(yè)務(wù)方必須要實(shí)現(xiàn),這個(gè)方法有什么作用呢?

abstract fun getHolderWidth(context: Context):Int

這個(gè)方法用于返回列表中每個(gè)ItemView的尺寸寬度,因?yàn)樵诜猪摻M件中會判斷當(dāng)前列表可見的ItemView有多少個(gè)。這里大家可能會有疑問,RecyclerView的LayoutManager不是有對應(yīng)的api嗎,像

findFirstVisibleItemPosition()
findLastVisibleItemPosition()
findFirstCompletelyVisibleItemPosition()
findLastCompletelyVisibleItemPosition()

為什么不用呢?因?yàn)槲覀兊姆猪摻M件是要兼容多種視圖形式的,雖然我們今天講到的普通列表用這個(gè)是沒有問題的,但是有些視圖類型是不能兼容這個(gè)api的,后續(xù)會介紹。

1.2 手寫分頁列表

先把第一版的代碼貼出來,有個(gè)完整的體系

class PagingList<T> : IPagingList<T>, IModelProcess<T>, LifecycleEventObserver {
    private var mTotalScroll = 0
    private var mCallback: IPagingCallback? = null
    private var currentPageIndex = ""
    //模式
    private var mode: ListMode = ListMode.DATE
    private var adapter: PagingAdapter<T>? = null
    //支持的類型 普通列表
    private val dateMap: MutableMap<String, MutableList<BasePagingModel<T>>> by lazy {
        mutableMapOf()
    }
    private val simpleList: MutableList<BasePagingModel<T>> by lazy {
        mutableListOf()
    }
    override fun bindView(
        context: Context,
        lifecycleOwner: LifecycleOwner,
        recyclerView: RecyclerView,
        adapter: PagingAdapter<T>,
        mode: ListMode
    ) {
        this.mode = mode
        this.adapter = adapter
        recyclerView.adapter = adapter
        recyclerView.layoutManager =
            LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        addRecyclerListener(recyclerView)
        lifecycleOwner.lifecycle.addObserver(this)
    }
    private fun addRecyclerListener(recyclerView: RecyclerView) {
        recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    if (!recyclerView.canScrollHorizontally(1) && currentPageIndex == "-1" && mTotalScroll > 0) {
                        //滑動到底部
                        mCallback?.scrollEnd()
                    }
                    //獲取可見item的個(gè)數(shù)
                    val visibleCount = getVisibleItemCount(recyclerView.context, recyclerView)
                    if (recyclerView.childCount > 0 && visibleCount >= (getListCount(mode) ?: 0)) {
                        if (currentPageIndex != "-1") {
                            //請求下一頁數(shù)據(jù)
                            mCallback?.scrollRefresh()
                        }
                    }
                } else {
                    //暫停刷新
                    mCallback?.scrolling()
                }
            }
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (!recyclerView.canScrollHorizontally(1) && currentPageIndex == "-1" && mTotalScroll > 0) {
                    //滑動到底部
                    mCallback?.scrollEnd()
                }
                mTotalScroll += dx
                //滑動超出2屏
//                binding.ivBackFirst.visibility =
//                    if (mTotalScroll > ScreenUtils.getScreenWidth(requireContext()) * 2) View.VISIBLE else View.GONE
            }
        })
    }
    override fun bindData(model: List<BasePagingModel<T>>) {
        //處理數(shù)據(jù)
        dealPagingModel(model)
        //adapter刷新數(shù)據(jù)
        if (mode == ListMode.DATE) {
            adapter?.setPagingMapData(dateMap)
        } else {
            adapter?.setPagingData(simpleList)
        }
    }
    fun setScrollListener(callback: IPagingCallback) {
        this.mCallback = callback
    }
    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (event == Lifecycle.Event.ON_RESUME) {
            //TODO 加載圖片
//            Glide.with(requireContext()).resumeRequests()
        } else if (event == Lifecycle.Event.ON_PAUSE) {
            //TODO 停止加載圖片
        } else if (event == Lifecycle.Event.ON_DESTROY) {
            //TODO 頁面銷毀不會加載圖片
        }
    }
    /**
     * 獲取可見的item個(gè)數(shù)
     */
    private fun getVisibleItemCount(context: Context, recyclerView: RecyclerView): Int {
        var totalCount = 0
        //首屏假設(shè)全部占滿
        totalCount +=
            ScreenUtils.getScreenWidth(recyclerView.context) / adapter?.getHolderWidth(context)!!
        totalCount += mTotalScroll / adapter?.getHolderWidth(context)!!
        return (totalCount + 1)
    }
    override fun getTotalCount(): Int? {
        return getListCount(mode)
    }
    override fun dealPagingModel(data: List<BasePagingModel<T>>) {
        this.currentPageIndex = updateCurrentPageIndex(data)
        if (mode == ListMode.DATE) {
            data.forEach { model ->
                val time = DateFormatterUtils.check(model.time)
                if (dateMap.containsKey(time)) {
                    model.itemData?.let {
                        dateMap[time]?.add(model)
                    }
                } else {
                    val list = mutableListOf<BasePagingModel<T>>()
                    list.add(model)
                    dateMap[time] = list
                }
            }
        } else {
            simpleList.addAll(data)
        }
    }
    private fun updateCurrentPageIndex(data: List<BasePagingModel<T>>): String {
        if (data.isNotEmpty()) {
            return data[0].pageCount
        }
        return "-1"
    }
    private fun getListCount(mode: ListMode): Int? {
        var count = 0
        if (mode == ListMode.DATE) {
            dateMap.keys.forEach { key ->
                //獲取key下的元素個(gè)數(shù)
                count += dateMap[key]?.size ?: 0
            }
        } else {
            count = simpleList.size
        }
        return count
    }
}

首先,PagingList實(shí)現(xiàn)了IPagingList接口,我們先看實(shí)現(xiàn),在bindView方法中,其實(shí)就是給RecyclerView設(shè)置了適配器,然后注冊了RecyclerView的滑動監(jiān)聽,我們看下監(jiān)聽器中的主要實(shí)現(xiàn)。

onScrollStateChanged方法主要用于監(jiān)聽列表是否在滑動,當(dāng)列表的狀態(tài)為SCROLL_STATE_IDLE時(shí),代表列表停止了滑動,這里做了兩件事:

(1)首先判斷列表是否滑動到了底部

if (!recyclerView.canScrollHorizontally(1) && currentPageIndex == "-1" && mTotalScroll > 0) {
    //滑動到底部
    mCallback?.scrollEnd()
}

這里需要滿足三個(gè)條件:recyclerView.canScrollHorizontally(1)如果返回了false,那么代表列表不能繼續(xù)滑動;還有就是會判斷currentPageIndex是否是最后一頁,如果等于-1那么就是最后一頁,同樣需要判斷滑動的距離,綜合來說就是【如果列表滑動到了最后一頁而且不能再繼續(xù)滑動了,那么就是到底了】,這里可以展示尾部的到底UI。

(2)判斷是否能夠觸發(fā)分頁加載

/**
 * 獲取可見的item個(gè)數(shù)
 */
private fun getVisibleItemCount(context: Context, recyclerView: RecyclerView): Int {
    var totalCount = 0
    //首屏假設(shè)全部占滿
    totalCount +=
        ScreenUtils.getScreenWidth(recyclerView.context) / adapter?.getHolderWidth(context)!!
    totalCount += mTotalScroll / adapter?.getHolderWidth(context)!!
    return (totalCount + 1)
}

首先這里會判斷展示了多少ItemView,之前提到的適配器中的getHolderWidth這里就用到了,首先我們會假設(shè)首屏全部占滿了ItemView,然后根據(jù)列表滑動的距離,判斷后續(xù)有多少ItemView展示出來,最終返回結(jié)果。

我們先不看下面的邏輯,因?yàn)榉猪摷虞d涉及到了數(shù)據(jù)的處理,因此我們先看下bindData的實(shí)現(xiàn)

override fun bindData(model: List<BasePagingModel<T>>) {
    //處理數(shù)據(jù)
    dealPagingModel(model)
    //adapter刷新數(shù)據(jù)
    if (mode == ListMode.DATE) {
        adapter?.setPagingMapData(dateMap)
    } else {
        adapter?.setPagingData(simpleList)
    }
}

在調(diào)用bindData時(shí)會傳入一頁的數(shù)據(jù),dealPagingModel方法用于處理數(shù)據(jù),首先獲取當(dāng)前數(shù)據(jù)的頁碼,用于判斷是否需要繼續(xù)分頁加載。

override fun dealPagingModel(data: List<BasePagingModel<T>>) {
    this.currentPageIndex = updateCurrentPageIndex(data)
    if (mode == ListMode.DATE) {
        data.forEach { model ->
            val time = DateFormatterUtils.check(model.time)
            if (dateMap.containsKey(time)) {
                model.itemData?.let {
                    dateMap[time]?.add(model)
                }
            } else {
                val list = mutableListOf<BasePagingModel<T>>()
                list.add(model)
                dateMap[time] = list
            }
        }
    } else {
        simpleList.addAll(data)
    }
}

剩下的工作用于組裝數(shù)據(jù),simpleList用于存儲全部的列表數(shù)據(jù),每次傳入一頁數(shù)據(jù),都會存在這個(gè)集合中。處理完數(shù)據(jù)之后,將數(shù)據(jù)塞進(jìn)adapter,用于刷新數(shù)據(jù)。

然后我們回到前面,我們在拿到了可見的ItemView的個(gè)數(shù)之后,首先會判斷recyclerView展示的ItemView個(gè)數(shù),如果等于0,那么就說明沒有數(shù)據(jù),就不需要觸發(fā)分頁加載。

if (recyclerView.childCount > 0 && visibleCount >= (getListCount(mode) ?: 0)) {
    if (currentPageIndex != "-1") {
        //請求下一頁數(shù)據(jù)
        mCallback?.scrollRefresh()
    }
}

假設(shè)每頁展示10條數(shù)據(jù),這個(gè)時(shí)候getListCount方法返回的就是總的數(shù)據(jù)個(gè)數(shù)(10),如果visibleCount超過了List的總個(gè)數(shù),那么就需要觸發(fā)分頁加載,因?yàn)橹拔覀兲岬?,最后一頁的index就是-1,所以這里判斷如果是最后一頁,就不需要分頁加載了。

1.3 生命周期管理

在PagingList中,我們實(shí)現(xiàn)了LifecycleEventObserver接口,這里的作用是什么呢?

就是我們知道,在列表中經(jīng)常會有圖片的加載,那么在圖片加載時(shí)如果滑動列表,那么勢必會產(chǎn)生卡頓,因此我們在滑動的過程中不會去加載圖片,而是在滑動停止時(shí),重新加載,這個(gè)優(yōu)化體驗(yàn)是沒有問題,用戶不會關(guān)注滑動時(shí)的狀態(tài)。

那么這里會存在一個(gè)問題,例如我們在滑動的過程中退出到后臺,這個(gè)時(shí)候列表滑動停止時(shí)加載圖片,可能存在上下文找不到的場景導(dǎo)致應(yīng)用崩潰,因此我們傳入生命周期的目的在于:讓列表具備感知生命周期的能力,當(dāng)列表處在不可見的狀態(tài)時(shí),不能進(jìn)行多余的網(wǎng)絡(luò)請求。

2022-09-04 15:41:43.541 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:43.651 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:43.661 2763-2763/com.lay.paginglist E/MainActivity: scrollRefresh--
2022-09-04 15:41:43.668 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:43.674 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:43.877 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:43.885 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:43.950 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:44.101 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:44.175 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:44.318 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:44.467 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:44.475 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:45.188 2763-2777/com.lay.paginglist I/.lay.paginglis: WaitForGcToComplete blocked RunEmptyCheckpoint on ProfileSaver for 12.247ms
2022-09-04 15:41:47.008 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.099 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.186 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:47.322 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.403 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.404 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:47.514 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:47.606 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.650 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:47.683 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.781 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:47.889 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.950 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:47.963 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:48.156 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:48.182 2763-2763/com.lay.paginglist E/MyAdapter: bindBusinessData --- 
2022-09-04 15:41:48.231 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:48.489 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:48.533 2763-2763/com.lay.paginglist E/MainActivity: scrolling--
2022-09-04 15:41:48.593 2763-2763/com.lay.paginglist E/MainActivity: scrollEnd--

我們可以看下具體的實(shí)現(xiàn)效果就是,當(dāng)觸發(fā)分頁加載時(shí),scrollRefresh會被回調(diào),這里可以進(jìn)行網(wǎng)絡(luò)請求,拿到數(shù)據(jù)之后再次調(diào)用bindData方法,然后繼續(xù)往下滑動,當(dāng)滑動到最后一頁時(shí),scrollEnd被回調(diào),具體的使用,可以在demo中查看。

2 github

之前有小伙伴提到這個(gè)事情,希望在github上放出源碼,所以就做了 github.com/LLLLLaaayyy…

大家可以在v1.0分支查看源碼,在app模塊中有一個(gè)demo大家可以看具體的使用方式,分頁列表的代碼在paging模塊中

以上就是Android性能優(yōu)化之RecyclerView分頁加載組件功能詳解的詳細(xì)內(nèi)容,更多關(guān)于Android RecyclerView分頁加載的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android中EditText setText方法的踩坑實(shí)戰(zhàn)

    Android中EditText setText方法的踩坑實(shí)戰(zhàn)

    這篇文章主要給大家分享了一些關(guān)于Android中EditText setText方法的踩坑記錄,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Android 中自定義ContentProvider與ContentObserver的使用簡單實(shí)例

    Android 中自定義ContentProvider與ContentObserver的使用簡單實(shí)例

    這篇文章主要介紹了Android 中自定義ContentProvider與ContentObserver的使用簡單實(shí)例的相關(guān)資料,這里提供實(shí)例幫助大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • android實(shí)現(xiàn)接通和掛斷電話

    android實(shí)現(xiàn)接通和掛斷電話

    這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)接通和掛斷電話功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Android讀取串口數(shù)據(jù)的操作指南

    Android讀取串口數(shù)據(jù)的操作指南

    在Android系統(tǒng)上讀取串口數(shù)據(jù)是一個(gè)常見的需求,特別是當(dāng)我們需要與硬件設(shè)備進(jìn)行通信時(shí),本文給大家介紹了Android讀取串口數(shù)據(jù)的操作指南,文中有詳細(xì)的步驟和代碼示例,幫助你更好地理解和實(shí)現(xiàn)串口通信,需要的朋友可以參考下
    2024-05-05
  • Android編程實(shí)現(xiàn)手機(jī)震動功能的方法

    Android編程實(shí)現(xiàn)手機(jī)震動功能的方法

    這篇文章主要介紹了Android編程實(shí)現(xiàn)手機(jī)震動功能的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)手機(jī)震動功能的核心代碼與權(quán)限控制操作技巧,需要的朋友可以參考下
    2017-06-06
  • Android RadioButton 圖片位置與大小實(shí)例詳解

    Android RadioButton 圖片位置與大小實(shí)例詳解

    這篇文章主要介紹了Android RadioButton 圖片位置與大小實(shí)例詳解的相關(guān)資料,這里提供實(shí)例代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下
    2016-11-11
  • Android深入淺出之Binder機(jī)制

    Android深入淺出之Binder機(jī)制

    本文主要介紹 Android深入淺出之Binder機(jī)制,這里整理了詳細(xì)的Binder機(jī)制資料,研究Android源碼的朋友可以看下,以便了解Android 通信知識
    2016-08-08
  • Android中兩個(gè)Activity之間數(shù)據(jù)傳遞及返回問題

    Android中兩個(gè)Activity之間數(shù)據(jù)傳遞及返回問題

    本篇文章主要介紹了Android中兩個(gè)Activity之間數(shù)據(jù)傳遞及返回問題,這里整理了詳細(xì)的代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • 利用OPENCV為android開發(fā)畸變校正的JNI庫方法

    利用OPENCV為android開發(fā)畸變校正的JNI庫方法

    今天小編就為大家分享一篇利用OPENCV為android開發(fā)畸變校正的JNI庫方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Android開發(fā)之SD卡文件操作分析

    Android開發(fā)之SD卡文件操作分析

    這篇文章主要介紹了Android開發(fā)之SD卡文件操作,結(jié)合實(shí)例形式分析了Android權(quán)限控制及針對SD卡文件操作的相關(guān)技巧,需要的朋友可以參考下
    2017-07-07

最新評論

沙田区| 安远县| 行唐县| 渭源县| 读书| 阿荣旗| 镇远县| 翁源县| 文水县| 迁安市| 若羌县| 锦屏县| 津市市| 高陵县| 安溪县| 吴川市| 涞水县| 绥宁县| 根河市| 卓尼县| 武清区| 翼城县| 维西| 屏山县| 墨竹工卡县| 瑞金市| 三都| 五大连池市| 安宁市| 哈尔滨市| 姚安县| 临沧市| 克东县| 嫩江县| 沛县| 聂拉木县| 大姚县| 新龙县| 健康| 凤台县| 阳朔县|