日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Android仿抖音右滑清屏左滑列表功能的實現代碼

瀏覽:183日期:2022-06-06 18:22:45

概述

​ 項目中要實現仿抖音直播間滑動清屏,側滑列表的功能,在此記錄下實現過程和踩坑記錄希望避免大家走些彎路,也當作自己的一個總結

​ 首先看下Demo中的效果

Android仿抖音右滑清屏左滑列表功能的實現代碼

​ 閱讀文章需要提前熟悉些事件分發的內容,相信大家都已經了解過了,網上也有很多優秀的文章,這里推薦兩篇自己讀過印象較深的文章

https://www.jb51.net/article/124249.htm

https://www.jb51.net/article/124861.htm

關于這方面的知識,在Android中是再重要不過的了,是遲早都要掌握的知識,所以還是希望大家都能提早掌握,最好可以跟著源碼一起分析,理解掌握的更深刻一點

實踐

所以網上基于這部分內容講解已經很詳細了,這里就不再搬磚了,主要分享一下自己項目中結合這部分知識運用過程中產生的一些想法和經驗,解決的一些bug

Android仿抖音右滑清屏左滑列表功能的實現代碼

以上就是功能在實現過程中要解決的問題,下面詳細展開

1. 布局結構

​ 布局結構始終是界面設計時首先要考慮的一個問題,從接到一個需求開始,首先要根據項目中現有的布局結構,考慮如何更優雅的嵌入布局層次。如果一不小心,走上了錯誤的實現道路,那么不好意思,即使功能最后實現了,到了后期,也有千萬種理由迫使你不得不走上重構的道路。

​ 比如實現不合理,導致的布局結構復雜,嵌套冗余層次,比如代碼業務邏輯處理復雜蹩腳,比如資源浪費,內存消耗過多等等。雖然功能好使,使用起來也沒有差別,但是,作為一個有追求的程序員,我們還是要避免這種情況的發生不是嗎

不巧的是,本文就屬于上述踩坑記錄,下面詳細分析

1.1 初步實現

​ 上來以后,思路很直接明了的去想要實現清屏和滑屏的功能是每個房間都有的功能,每個房間又都是一個RecyclerView 的一個Item。所以,很明顯在Item的布局上包一層,實現清屏和側滑列表的功能就可以了,這樣每個房間都可以上下滑,切換房間。切換以后,滑屏的功能是在每個房間里的,互不影響,所以很好理解

我們項目中實現直播間上下滑切換的功能是RecyclerView + 自定義LinearLayoutManager實現的,這部分內容網上demo很多,就不展開了

​ 具體實施,是自定義布局繼承RelativeLayout,解析自定義的布局文件,里面包含,直播間的房間布局,和自己右側滑塊兒布局,然后用自己實現的布局替換之前的房間Item布局位置

Android仿抖音右滑清屏左滑列表功能的實現代碼

由于我們自定義的Container布局是繼承子RelativeLayout實現的,內部三個子View 又全部是占滿父布局的,所以就是三層覆蓋的效果,類似抖音直播間效果 這里我們盡量將覆蓋層/清屏控件,封裝成一個ViewGroup 內部包含了上邊細分的各個子View,例如頭部個人信息,頭像列表等等;中間彈幕,SVGA禮物展示區域;底部聊天評論區域方便管理 還有右側滑塊我們也做成繼承自RelativeLayout形式,解析自定義布局,方便擴展

這樣我們調用封裝的Container將清屏控件,和右側滑塊兒布局View分別添加到內部即可

API提供如下

// 添加需要清屏的view fun addClearViews(vararg views: View?) // 添加需要滑入的view fun addSlideView(view: RightSlideLayout)

這樣我們在視頻播放頁面滑動,就可以在Container內判斷手勢,處理清屏控件或者滑出右側滑塊兒了

右側滑塊再動態加載Fragment,展示列表布局,基本完成功能效果了

1.2 重構

​ 本來以為開開心心的可以上線了,誰知到下邊繼續體驗和對比抖音到過程中還是發現不足:

第一個是,右側滑塊兒(后邊稱RightSlider)包含在房間,這樣上下切換房間(后邊稱Container),RightSlider布局也會隨著Container新建而新建,雖然有RecyclerView的布局緩存,但是至少也會新建Holder幾次,造成資源的浪費。第二個是,RightSlider的新建就會導致里邊的Fragment的新建,所以又會重新請求加載列表數據,再次造成資源浪費,而且,新建后右側列表又會重新頂到頭,之前滑動過的距離就會丟失。這樣就造成,用戶從右側列表點擊切換房間后,再次滑出RightSlider切換房間,發現又要從頭開始往下滑,這樣肯定不符合用戶體驗。觀察抖音列表后發現,每次滑動到固定位置點擊Item切換房間后,再次滑出滑塊兒,發現列表還是之前的位置,好像跟之前滑出的是一個滑塊兒的效果,于是恍然大悟,滑塊兒是跟Activity綁定的,也就是要把RightSlider放在跟Activity布局那一層

​ 其實提出RightSlider到外層的過程中,還是走了不少彎路,因為之前畢竟已經實現好的邏輯,如果改動布局結構,肯定要重寫滑動沖突、事件分發這部分代碼,工作量又不可預計。所以想著能不能不動布局結構的情況下實現仿抖音效果

動態替換Fragment

​ 首先想到的是滑出RightSlider里的列表每次都好像是同一個,那么保證里邊的Fragment是同一個不就好了,滑出的滑塊兒雖然不同,但是里邊裝載的Fragment列表是同一個,這樣就營造出同一個滑塊兒的效果。

​ 但是實現過程中還是出現了問題,由于RecyclerView的預加載功能,導致我們項目中,從第一個房間上滑到下一個房間,過程中會新建兩個Holder,這樣Fragment替換就出了問題,切換房間后Fragment添加不上去,折騰一下午后最終放棄這個方案

固定List高度

​ 然后想的,既然Fragment替換不了了,那么RecyclerView肯定不是同一個了,如果點擊后記錄當前RecyclerView滑動的位置,下次滑出時,代碼固定到當前位置不是也可以偽造出同一個滑塊兒的效果嘛,這部分也去找了一些資料,實現了個小demo。其中用到的主要方法是

/** * 獲取滑動距離 */ fun getScollYDistance(): Int { // 獲取recyclerview 的layoutManagerval layoutManager = recyclerView.layoutManager as LinearLayoutManager// 獲取當前第一個可見View的位置val position = layoutManager.findFirstVisibleItemPosition()// 根據position 獲取當前Viewval firstVisiableChildView = layoutManager.findViewByPosition(position)// 獲取當前View 高度val itemHeight = firstVisiableChildView.height// 滑動距離return position * itemHeight - firstVisiableChildView.top }

滑動距離計算的思想是:根據當前可見View 的position * 每個ItemView 的高度 + 當前View已經滑出去的部分

Android仿抖音右滑清屏左滑列表功能的實現代碼

​ 計算出高度后,每次加載時,調用RecyclerView的API

recyclerView.scrollBy(0,scroll) //scroll 剛才計算的高度

還有其他幾個滑動的方法:

// 帶動畫移動距離public void smoothScrollBy(int dx, int dy)// 帶動畫移動到positionpublic void smoothScrollToPosition(int position)// 移動到adapter position ,由LayoutManager實現public void scrollToPosition(int position)// 空實現,無效public void scrollTo(int x, int y)

原理上可以實現,但是最后綜合比較還是放棄了這種方式,因為總感覺這種方法屬于投機取巧不是正道,還是老老實實將RightSlider 提到外面得了

2. 動畫

​ 動畫也是這個功能中很重要的一個方面,因為動畫效果的流暢直接影響了用戶體驗,所以這方面也是細扣了很久。首先這個功能主要分成三個動畫效果:

2.1 進場出場

​ 包含清屏控件入場、出場:

mClearAnimator = ValueAnimator.ofFloat(0f, 1.0f).setDuration(300)mClearAnimator.addUpdateListener(ValueAnimator.AnimatorUpdateListener { valueAnimator -> val value = valueAnimator.animatedValue as Float translateClearChild((startX + value * (endX - startX)).toInt())})mClearAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) {isCleared = !isCleared }})

這里使用了屬性動畫ValueAnimator,其中 translateClearChild 負責移動View 代碼如下:

/** * 移動清屏控件 */ private fun translateClearChild(translate: Int) {for (i in mClearViews.indices) { mClearViews[i].translationX = translate.toFloat()} }

​ 滑塊兒的入場、出場:

mSlideInAnimator = ValueAnimator.ofFloat(0f, 1.0f).setDuration(500)// 設置減速攔截器mSlideInAnimator.interpolator = DecelerateInterpolator(3f)mSlideInAnimator.addUpdateListener(ValueAnimator.AnimatorUpdateListener { valueAnimator -> val value = valueAnimator.animatedValue as Float translateSlideView((startX + value * (endX - startX)).toInt())})mSlideInAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) {mSlideView!!.visibility = View.VISIBLEmBgColorView.isClickable = true } override fun onAnimationEnd(animation: Animator) {if (!isSlideShow && translateX == 0) { isSlideShow = !isSlideShow} else if (isSlideShow && abs(translateX) == width - mSlideView!!.paddingLeft) { isSlideShow = !isSlideShow}if (!isSlideShow) { parent.requestDisallowInterceptTouchEvent(false) mSlideView!!.visibility = View.GONE removeView(mBgColorView) addView(mBgColorView, childCount - 4)}isSliderGoning = false }})

這里startX,endX 分別代表入場和出場時候,動畫起止位置。由于清屏控件沒有中間位置狀態,直接是從0 到屏幕寬度兩個值之間替換;而滑塊兒中間由于要跟隨手勢移動,所以要記錄中間translateX,標記為startX

2.2 跟隨手勢

​ 跟隨手勢實現主要是攔截移動手勢,根據按下手勢位置坐標和Move移動位置坐標的差值,調用移動SliderView的方法

val x = event.rawX.toInt()// 標記移動距離val offsetX = x - mDownXwhen (event.action) { MotionEvent.ACTION_MOVE -> {if ((isSlideShow) && offsetX > 0 && mSlideInAnimator.isRunning && !isSliderGoning) { // 滑入情況下,向右滑一段松開,再向右滑,清除回彈動畫,跟隨手勢 mSlideInAnimator.cancel() translateSlideView(offsetX)}if ((isSlideShow) && offsetX > 0 && !mSlideInAnimator.isRunning) { // 滑入情況下,向右滑,跟隨手勢 translateSlideView(offsetX)}return true } }

2.3 顏色漸變

​ 跟隨手勢滑動過程中還伴隨的左側空白區域顏色漸變,這部分可以在RightSlider移動過程中的距離值關聯起來,設置起始顏色透明和截止顏色灰色蒙層。再根據距離動態算出當前顏色在區間范圍內取值,主要代碼邏輯如下

/** * 移動滑塊兒 */ private fun translateSlideView(translate: Int) {val percent = (mSlideView!!.width.toFloat() - translate) / mSlideView!!.width// 根據百分比算出色值val color = (MASK_DARK_COLOR * percent).toInt() shl 24// 動態設置背景色漸變mBgColorView.setBackgroundColor(color)translateX = translatemSlideView!!.translationX = translate.toFloat() }

3 事件分發

​ 這部分可以說是本功能實現的核心,也是耗費了相當時間的精力,從最開始的Container包含RightSlider布局處理經典的事件分發順序,到最后重構布局,將RightSlider提到外層變成不是包含關系,而是并列或者說是覆蓋關系,中間對事件傳遞的順序理解又深入了一層

3.1 傳遞順序

​ 重構之前的布局結構是每個Container包含了一個RightSlider,兩個是一個整體使用的,滑動的邏輯都可以在Container層內的onInterceptTouchEvent方法內處理。判斷是否攔截事件即可,然后RightSlider內想要禁止父層Container攔截事件,可以使用parent.requestDisallowInterceptTouchEvent(true)禁止父層攔截;是屬于經典模式的事件分發模型,事件分發的順序在一個U型結構里,比較好處理

​ 然后重構以后布局結構變成了如下圖所示

Android仿抖音右滑清屏左滑列表功能的實現代碼

​ 每個Container 共用一個RightSlider,這樣屬于事件的分發處理不在一個ViewGroup的U型模型里了,這樣的分發順序也是屬于自己的一個大膽嘗試,想著實在不行,還是要把Activity內布局包一層,將Container和RightSlider 放在一個U型結構里去處理。

​ 還好最后不斷踩坑,終于實現了事件從Activity分發,到RightSlider,再分發到Container的過程

​ 這里貼下Demo里的布局實現:

<androidx.constraintlayout.widget.ConstraintLayout 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' android:background='@mipmap/bg' tools:context='.MainActivity'> <com.fxf.slide.SlideContainerLayoutandroid:id='@+id/layout_slider_container'android:layout_width='match_parent'android:layout_height='match_parent'><LinearLayout android:id='@+id/ll12' android:layout_width='match_parent' android:layout_height='200dp' android:layout_gravity='center_horizontal' android:layout_marginTop='100dp' android:background='#00f' android:orientation='vertical'> <TextViewandroid:id='@+id/tv111'android:layout_width='wrap_content'android:layout_height='wrap_content'android:text='111111111'android:textColor='#fff' /> <TextViewandroid:layout_width='wrap_content'android:layout_height='wrap_content'android:text='222222222'android:textColor='#fff' /></LinearLayout> </com.fxf.slide.SlideContainerLayout> <com.fxf.slide.RightSlideLayoutandroid:id='@+id/layout_right_slider'android:layout_width='match_parent'android:layout_height='match_parent'android:paddingLeft='60dp'android:visibility='gone'><RelativeLayout android:layout_width='match_parent' android:layout_height='match_parent' android:background='@drawable/shape_slider_background'> <Viewandroid:id='@+id/live_slide_bar'android:layout_width='4.5dp'android:layout_height='90dp'android:layout_centerVertical='true'android:layout_marginLeft='5dp'android:layout_marginRight='5dp'android:background='@drawable/shape_slider_dark_bar' /> <FrameLayoutandroid:id='@+id/list_fragment'android:layout_width='match_parent'android:layout_height='match_parent'android:layout_toRightOf='@+id/live_slide_bar' /></RelativeLayout> </com.fxf.slide.RightSlideLayout></androidx.constraintlayout.widget.ConstraintLayout>

其中做了部分簡化,主要幫助大家理解布局層次

​ 然后貼下RightSlider核心分發代碼:

override fun dispatchTouchEvent(event: MotionEvent): Boolean {// 獲取坐標,這里用rawX 相對屏幕絕對位置,不然隨手勢移動過程中父布局的移動,導致獲取的坐標左右抖動,會出現移動過程中左右一直抖動現象val x = event.rawX.toInt()val y = event.rawY.toInt()// X方向位移val offsetX = x - mDownXif (!mSlideContainerLayout.isSlideShow){// Container滑塊兒沒滑出來不分發事件 return false}when (event.action) { MotionEvent.ACTION_DOWN -> { // 記錄按下點坐標mDownX = xmDownY = ymSlideContainerLayout.setDownXY(mDownX,mDownY) } MotionEvent.ACTION_MOVE -> if (abs(x - mDownX) < abs(y - mDownY) && paddingLeft < x) { // 上下滑動情況處理if (isSlideHorizontal) { return mSlideContainerLayout.dispatchTouchEvent(event)} } else if ( offsetX < 0 && mSlideContainerLayout.isAlignLeftSide()) { // 向左滑動,滑塊兒已經靠最左邊了,不分發return super.dispatchTouchEvent(event) } else if (abs(x - mDownX) > abs(y - mDownY)){ // 水平方向移動,分發事件isSlideHorizontal = true return mSlideContainerLayout.dispatchTouchEvent(event)// 事件傳遞給Container處理 } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->{ // 抬起時處理if (offsetX < 0 && mSlideContainerLayout.isAlignLeftSide()){ return super.dispatchTouchEvent(event)}if (abs(x - mDownX) > abs(y - mDownY) || isSlideHorizontal){ isSlideHorizontal = false return mSlideContainerLayout.dispatchTouchEvent(event)}isSlideHorizontal = false }}return super.dispatchTouchEvent(event) }

3.2 滑動沖突

因為房間是可以上下滑動的,所以可以判斷如果滑塊兒沒滑粗來時,直接返回分發,不讓RightSlider和Container處理事件

if (!mSlideContainerLayout.isSlideShow){ return false }

然后滑塊兒滑出來以后,因為里邊有列表,所以要消費上下滑動事件,可以處理如下:

MotionEvent.ACTION_MOVE -> if (abs(x - mDownX) < abs(y - mDownY) && paddingLeft < x) {if (isSlideHorizontal) { return mSlideContainerLayout.dispatchTouchEvent(event)} }

其中paddingLeft < x 是因為滑塊左邊有一部分空白區域 paddingLeft ,所以當x坐標在此區域右側時才處理事件

Container動畫執行過程中,說明正在消費事件,此時禁止父層攔截事件

if (mClearAnimator.isRunning || mSlideInAnimator.isRunning || isSlideShow) { // 滑入情況下,禁止上下滑切換直播間 parent.requestDisallowInterceptTouchEvent(true) }

Container處理事件時候和直播間上的進入房間頭像列表沖突,解決方法是判斷mDownY 大于進入頭像列表高度時才處理事件,因為正常人滑入滑塊都是在屏幕中下部操作的,所以太靠上的部分不處理事件也可以接受

MotionEvent.ACTION_MOVE -> {if (!mClearAnimator.isRunning && mDownY > 200 && abs(x - mDownX) > abs(y - mDownY)) { // 清屏不在執行時 && 高度大于200dp(解決進入房間頭像滑動沖突)&& 橫向滑動時攔截事件 if (abs(x - mDownX) > 10) {return true }} }

3.3 滑動優化

​ 這部分有很多細節處理的地方,包括動畫執行到一半情況下,再次左右滑動,先向左后向右,左右滑一半再上下滑等等各種情況具體可以看代碼中SlideContainerLayout中onTouchEvent方法內處理邏輯,都添加了注釋

override fun onTouchEvent(event: MotionEvent): Boolean {mVelocityTracker!!.addMovement(event)val x = event.rawX.toInt()val offsetX = x - mDownXif (mLastOffsetList.size > 2){ mLastOffsetList.removeFirst()}mLastOffsetList.add(offsetX)var slideRight = (offsetX - mLastOffsetList.first) > 0when (event.action) { MotionEvent.ACTION_MOVE -> {if ((isSlideShow) && offsetX > 0 && mSlideInAnimator.isRunning && !isSliderGoning) { // 滑入情況下,向右滑一段松開,再向右滑,清除回彈動畫,跟隨手勢 mSlideInAnimator.cancel() translateSlideView(offsetX)}if ((isSlideShow) && offsetX > 0 && !mSlideInAnimator.isRunning) { // 滑入情況下,向右滑,跟隨手勢 translateSlideView(offsetX)}return true } MotionEvent.ACTION_UP -> {mVelocityTracker!!.computeCurrentVelocity(10)if (isSlideShow && offsetX > 0 && abs(offsetX) > width / 3 && !isSliderGoning && mVelocityTracker!!.xVelocity >= 0) { // 滑入情況下,向右滑距離超過寬度1/3,滑出滑塊 startX = offsetX endX = width - mSlideView!!.paddingLeft isSliderGoning = true mSlideInAnimator.start() return true}if (abs(mVelocityTracker!!.xVelocity) > 1) { if (isCleared && offsetX < 0) {// 清屏情況下,左滑速度超過10個像素時 ===》滑入清屏控件layerShowWithAnim() } else if (!isCleared && offsetX > 0 && !isSlideShow && !mSlideInAnimator.isRunning) {// 未清屏 && 向右速度 > 10 && 沒滑入滑塊 && 滑塊動畫沒執行的時候 ===》清屏layerGoneWithAnim() } else if (isSlideShow && offsetX > 0 && slideRight) {// 滑入情況下 && 向右速度 > 10 ===》滑出滑塊mSlideInAnimator.cancel()isSliderGoning = truestartX = translateXendX = width - mSlideView!!.paddingLeftmSlideInAnimator.start() } else if (isSlideShow && offsetX < 0 && translateX != 0) {// 滑入情況下 && 向左速度 > 10 && 已經向右滑動了一段距離 ===》 滑塊回彈startX = translateXendX = 0mSlideInAnimator.start() } else if (!isSlideShow && offsetX < 0 && !mSlideInAnimator.isRunning) {// 沒滑入情況下 && 向左滑速度 > 10 && 沒右正在滑入情況下 ===》 滑入滑塊sliderShowWithAnim() } else {if (isSlideShow && translateX != 0) { // 滑入情況下 && 已經向右滑動過,速度沒達到松開 ===》回彈 startX = translateX mSlideInAnimator.start()} }}else { if (isSlideShow && translateX != 0) {// 滑入情況下 && 已經向右滑動過,速度沒達到松開 ===》回彈startX = translateXmSlideInAnimator.start() }}return super.onTouchEvent(event) } MotionEvent.ACTION_CANCEL -> {if (isSlideShow) { //取消事件時,滑入情況下回彈 startX = translateX mSlideInAnimator.start()} }}return super.onTouchEvent(event) }

總結

​ 最后通過這次實踐,感觸比較深的是功能實現之前,一定要做好充分的調研,研究好需求的細節,并預先想幾種實現策略,對比哪一種更合理。不要埋頭就寫,結果最后發現不符合需求還要重構

​ 感謝,這里Contanier內的邏輯主要參考了gitHub上[這篇文章](https://github.com/lmxjw3/clearscreen )的處理不過里邊處理滑動沖突的邏輯比較少還是要自己結合項目處理

奉上GitHub 項目地址

項目地址

總結

到此這篇關于Android仿抖音右滑清屏左滑列表功能的實現代碼的文章就介紹到這了,更多相關android 抖音右滑清屏左滑列表內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: 抖音
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产伦精品一区二区三区千人斩| 久久久久黄色| 欧美日韩视频免费观看| 红杏一区二区三区| 五月天av在线| 99成人在线视频| 99视频精品全部免费在线视频| 精品成人免费一区二区在线播放| 国户精品久久久久久久久久久不卡 | 久久99偷拍| 麻豆国产欧美日韩综合精品二区| 成人国产综合| 色一区二区三区| 天堂资源在线亚洲| 亚洲尤物在线| 婷婷亚洲精品| 欧美黄页在线免费观看| 美女久久99| 四虎4545www国产精品| 999在线观看精品免费不卡网站| 蜜桃av一区二区三区电影| 欧美日韩一视频区二区| 高清一区二区三区av| 91精品啪在线观看国产18| 久久不射网站| 国产精品尤物| 久久精品主播| 伊人久久大香伊蕉在人线观看热v| 日韩成人一级| 亚洲一区资源| 日韩精品一二三| 国产欧美日韩在线一区二区| 日本激情一区| 午夜精品一区二区三区国产| 亚洲青青久久| 久久免费视频66| 精品一区三区| 欧美一区不卡| 日韩精品一卡| 亚洲精品看片| 日韩精品第一区| 中文字幕av一区二区三区四区| 国产精品白丝久久av网站| 久久久久蜜桃| 欧美久久一区二区三区| 日韩av福利| 亚洲aa在线| 成人污污视频| 美日韩精品视频| 精品国产91| 一区二区三区四区日韩| 成人在线超碰| 亚洲一区av| 国产成人精品亚洲日本在线观看| 日韩欧美高清一区二区三区| 蜜桃视频在线网站| 91福利精品在线观看| 日韩精品欧美| 国产精品手机在线播放| 伊人影院久久| 国产理论在线| 国产欧美一区二区色老头| 图片区亚洲欧美小说区| 国产亚洲高清在线观看| 99精品视频精品精品视频| 国产乱码精品一区二区三区亚洲人| 激情欧美一区二区三区| 久久精品一本| 日韩中文字幕在线一区| 亚洲成人精品| 精品香蕉视频| 亚洲精品九九| 欧美午夜精彩| 久久精品三级| 日本久久二区| 国产亚洲一区在线| 日韩精品诱惑一区?区三区| 91成人精品在线| 91精品高清| 中文字幕在线免费观看视频| 欧美日本不卡| 国产一级一区二区| 久久婷婷久久| 国产+成+人+亚洲欧洲在线| 日韩成人精品一区二区三区 | 图片区亚洲欧美小说区| 精品一区av| 欧美亚洲三区| 亚洲综合福利| 日韩午夜免费| 久久精品一本| 亚洲精品婷婷| 国产精品人人爽人人做我的可爱| 成人片免费看| 成人三级高清视频在线看| 国产日韩欧美三级| 婷婷视频一区二区三区| 欧美日韩国产欧| 欧美日韩中文字幕一区二区三区| 国产精品啊v在线| 日本久久二区| 视频一区视频二区中文| 亚洲va在线| 国产成人精品一区二区三区免费 | 青青草91视频| 亚洲免费毛片| 亚洲天堂av资源在线观看| 免费视频亚洲| 欧美高清一区| 国产一区二区中文| 婷婷成人在线| 亚洲手机在线| 亚洲午夜av| 久久在线免费| 中文一区一区三区高中清不卡免费| 精品72久久久久中文字幕| 精品一区二区三区免费看| 久久亚洲精品中文字幕| 开心激情综合| 精品久久在线| 精品三级国产| 黑人精品一区| 久久精品国产亚洲夜色av网站| 性欧美videohd高精| 日韩欧美少妇| 久久精品国产亚洲夜色av网站| 亚洲性色视频| 欧美午夜不卡| 日韩在线一区二区| 免费在线观看视频一区| 日日摸夜夜添夜夜添国产精品| 日本在线视频一区二区| 青青草国产精品亚洲专区无| 国产剧情在线观看一区| 精品伊人久久| 日韩国产欧美| 91久久中文| 亚洲精品一区二区在线播放∴| 91大神在线观看线路一区| 免费一区二区三区在线视频| 国产中文在线播放| 欧美成人综合| 综合亚洲视频| 国产午夜一区| 久久精品国产99国产精品| 三上悠亚国产精品一区二区三区| 成人av动漫在线观看| 男女男精品网站| 日韩不卡免费视频| 蜜桃久久久久| 欧美1区免费| 亚洲69av| 国产 日韩 欧美 综合 一区| 国产一区二区三区自拍| 亚洲日本在线观看视频| 国产精品毛片视频| 青青青免费在线视频| 在线观看免费一区二区| 亚洲a成人v| 久久不见久久见免费视频7| 日本精品不卡| 一区二区三区午夜视频| 欧美91在线| 亚洲精品极品少妇16p| 91精品福利观看| 中文另类视频| 日本一区二区中文字幕| 精品99久久| 免费国产自线拍一欧美视频| 久久gogo国模啪啪裸体| 1024精品久久久久久久久| 国产日韩欧美三级| 美女少妇全过程你懂的久久| 国产精品一卡| 欧美aa国产视频| 国产精品第十页| av亚洲免费| 麻豆一区二区三区| 美女精品网站| 中文在线资源| 日韩精品国产精品| 国产一区二区三区日韩精品| 国产一级久久| а√天堂中文在线资源8| 亚洲色图综合| 日韩中文影院| 国产精品尤物| 日韩精品一级中文字幕精品视频免费观看| 麻豆精品视频在线观看| 午夜日韩在线| 久久不见久久见免费视频7| 亚洲精品1区2区| 国产激情在线播放| 亚洲91网站| 亚洲国产专区校园欧美| 麻豆久久一区| 亚洲精品无播放器在线播放| 久久精品亚洲人成影院| 欧美国产极品| 日韩中文字幕无砖|