国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android自定義LinearLayout實(shí)現(xiàn)淘寶詳情頁

2019-12-12 05:07:39
字體:
供稿:網(wǎng)友

1.簡單說明

淘寶詳情頁就不用我一一介紹了,昨天逛淘寶看到這個(gè)效果時(shí),讓我想起了去年剛學(xué)習(xí)Android只會使用現(xiàn)成的時(shí)候,當(dāng)時(shí)在網(wǎng)上找了一個(gè)這種效果的使用了,并不懂怎么實(shí)現(xiàn)的。現(xiàn)在就看到一種效果就想自己實(shí)現(xiàn)一下,我想這就是剛接觸某個(gè)知識時(shí)的好奇心吧

說走咱就走啊,本文只是介紹一種實(shí)現(xiàn)思路,網(wǎng)上也已經(jīng)有了很多種實(shí)現(xiàn)方式,有問題請指正

效果圖(我有很用心的找美女圖的)

2.實(shí)現(xiàn)思路

繼承LinearLayout,設(shè)置方向?yàn)榇怪?br />控件中有兩個(gè)ScrollView,至于為什么要使用ScrollView,主要是因?yàn)閮?nèi)容超過一頁時(shí)省去自己處理滑動(dòng)
關(guān)鍵是事件分發(fā)處理。監(jiān)聽兩個(gè)ScrollView的滑動(dòng)事件,當(dāng)?shù)谝豁摶瑒?dòng)到底部時(shí),再向上拖動(dòng)時(shí),攔截事件,判斷距離,超過設(shè)定值時(shí),滑動(dòng)到第二頁,否則回彈;同理,當(dāng)?shù)诙摶瑒?dòng)到頂部時(shí),再向下拖動(dòng)時(shí),攔截事件,判斷距離,超過設(shè)定值時(shí),滑動(dòng)到第一頁,否則回彈(還有很多細(xì)節(jié)需要結(jié)合代碼講解)
關(guān)于回彈和滑動(dòng)換頁使用的是Scroller,對于Scroller的使用,本文不做過多解釋

3.實(shí)現(xiàn)

3.1重寫ScrollView

根據(jù)實(shí)現(xiàn)思路,我們需要監(jiān)聽ScrollView是否滑動(dòng)到頂部和底部,但是ScrollView的setOnScrollChangeListener()方法在api23才添加。主要是重寫ScrollViewonScrollChanged(int l, int t, int oldl, int oldt)方法。

l:當(dāng)前水平方向滾動(dòng)值,和getScrollX()相等
t:當(dāng)前豎直方向滾動(dòng)值,和getScrollY()相等
oldl:上一次水平滾動(dòng)值
oldt:上一次豎直滾動(dòng)值

監(jiān)聽接口:

public interface OnScrollEndListener { void scrollToBottom(View view); void scrollToTop(View view); void scrollToMiddle(View view); }

onScrollChanged方法

 @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if(t == 0){  if (mOnScrollBottomListener != null) {  mOnScrollBottomListener.scrollToTop(this);  } } else if(t + getMeasuredHeight() >= getChildAt(0).getMeasuredHeight()){  if (mOnScrollBottomListener != null) {  mOnScrollBottomListener.scrollToBottom(this);  } } else {  if (mOnScrollBottomListener != null) {  mOnScrollBottomListener.scrollToMiddle(this);  } } }

3.2重寫onMeasure方法、page的獲取與設(shè)置

顯示調(diào)用第二個(gè)自孩子的測量方法,不然尺寸有可能為0

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /**  * 顯示調(diào)用第二個(gè)自孩子的測量方法,不然尺寸有可能為0  */ View child2 = getChildAt(1); if (child2 != null) {  child2.measure(widthMeasureSpec, heightMeasureSpec); } }

在onFinishInflate中初始化兩個(gè)頁面

@Override protected void onFinishInflate() { super.onFinishInflate(); if(getChildCount() == 2){  View child1 = getChildAt(0);  if (child1 instanceof ScrollEndScrollView){  scrollView1 = (ScrollEndScrollView) child1;  }  View child2 = getChildAt(1);  if(child2 instanceof ScrollEndScrollView){  scrollView2 = (ScrollEndScrollView) child2;  } } initEvent(); }

為兩個(gè)頁面設(shè)置滑動(dòng)監(jiān)聽

private ScrollEndScrollView.OnScrollEndListener scrollEndListener = new ScrollEndScrollView.OnScrollEndListener() { @Override public void scrollToBottom(View view) {  if(view == scrollView1){  isToBotttom = true;  } } @Override public void scrollToTop(View view) {  if(view == scrollView2){  isToTop = true;  } } @Override public void scrollToMiddle(View view) {  if(view == scrollView1){  isToBotttom = false;  }  if(view == scrollView2){  isToTop = false;  } } };

3.3Scroller使用的幾步

Scroller的英文解釋是:
This class encapsulates scrolling. You can use scrollers (Scroller or OverScroller) to collect the data you need to produce a scrolling animation―for example, in response to a fling gesture. Scrollers track scroll offsets for you over time, but they don't automatically apply those positions to your view. It's your responsibility to get and apply new coordinates at a rate that will make the scrolling animation look smooth.

此類封裝滾動(dòng)。您可以使用滾動(dòng)條(滾輪或OverScroller)收集你需要制作一個(gè)滾動(dòng)的動(dòng)畫,例如,響應(yīng)一扔手勢的數(shù)據(jù)。滾動(dòng)條為您跟蹤滾動(dòng)偏移量隨著時(shí)間的推移,但他們不會自動(dòng)將新的位置設(shè)置到View中。你的任務(wù)是獲取并使用一個(gè)合適的速度,使?jié)L動(dòng)動(dòng)畫看起來更平滑。

簡而言之,有關(guān)滑動(dòng)的你都可以使用這個(gè)實(shí)現(xiàn)。

需要重寫的方法

@Override public void computeScroll() { super.computeScroll(); //先判斷mScroller滾動(dòng)是否完成 if (mScroller.computeScrollOffset()) {  //這里調(diào)用View的scrollTo()完成實(shí)際的滾動(dòng)  scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  //必須調(diào)用該方法,否則不一定能看到滾動(dòng)效果  postInvalidate(); } }

輔助方法

 //調(diào)用此方法設(shè)置滾動(dòng)的相對偏移 public void smoothScrollBy(int dx, int dy) { //設(shè)置mScroller的滾動(dòng)偏移量 mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, Math.max(300, Math.abs(dy))); invalidate();//這里必須調(diào)用invalidate()才能保證computeScroll()會被調(diào)用,否則不一定會刷新界面,看不到滾動(dòng)效果 }//調(diào)用此方法滾動(dòng)到目標(biāo)位置 public void smoothScrollTo(int fx, int fy) { int dx = fx - mScroller.getFinalX(); int dy = fy - mScroller.getFinalY(); smoothScrollBy(dx, dy); }

3.4事件分發(fā)

最關(guān)鍵的部分,邏輯稍復(fù)雜,細(xì)節(jié)處理較多。這里重寫dispatchTouchEvent。

顯示第一頁時(shí)
未滑動(dòng)到底部,事件由scrollView1自己處理
滑動(dòng)到底部時(shí),如果繼續(xù)向上拖動(dòng),攔截事件,父控件處理滑動(dòng);繼續(xù)向下拖動(dòng)時(shí),如果父控件(即該控件)當(dāng)前滾動(dòng)最后位置(mScroller.getFinalY())不為0, 如果父控件繼續(xù)滾動(dòng)不會出現(xiàn)負(fù)值時(shí)(出現(xiàn)負(fù)值時(shí)會導(dǎo)致頭部空白,因?yàn)檫@時(shí)是父控件控制,scrollView1不可滑動(dòng)),不攔截事件,父控件處理滑動(dòng),否則,強(qiáng)制滑動(dòng)到0位置,并把事件下發(fā)給子控件

顯示第二頁時(shí)
未滑動(dòng)到最頂部時(shí),事件由scrollView2自己處理
滑動(dòng)到頂部時(shí),如果繼續(xù)向下拖動(dòng),攔截事件,父控件處理滑動(dòng);繼續(xù)向上拖動(dòng)時(shí),如果父控件當(dāng)前滾動(dòng)位置小于第一頁高度,攔截事件,父控件處理滑動(dòng),否則,滑動(dòng)到第二頁起始位置,并把事件下發(fā)給子控件
ACTION_MOVE中進(jìn)行事件分發(fā),ACTION_UP中進(jìn)行切換頁面、回彈
關(guān)于使用scroller滑動(dòng),實(shí)現(xiàn)彈性效果,簡單實(shí)現(xiàn)請看這里簡單的彈性實(shí)現(xiàn)代碼

@Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); int yPosition = (int) ev.getY(); switch (action) {  case MotionEvent.ACTION_DOWN:  mScroller.abortAnimation();  mLastY = yPosition;  mMoveY = 0;  break;  case MotionEvent.ACTION_MOVE:  mMoveY = (mLastY - yPosition);  mLastY = yPosition;  if(isToBotttom){   if(mMoveY > 0){   //向上   smoothScrollBy(0, mMoveY);   return true;   } else {   //向下   if(mScroller.getFinalY() != 0){    //這是出于第一頁和第二頁顯示連接處    if(getScrollY() + mMoveY > 0){    smoothScrollBy(0, mMoveY);    return true;    } else{    smoothScrollTo(0, 0);    return super.dispatchTouchEvent(ev);    }   }   }  }  else if(isToTop){   if(mMoveY < 0){   //向下   smoothScrollBy(0, mMoveY);   return true;   } else {   //向上   if(mScroller.getFinalY() < scrollView1.getHeight()){    //這是出于第一頁和第二頁顯示連接處    smoothScrollBy(0, mMoveY);    return true;   } else {    smoothScrollTo(0, scrollView1.getHeight());    return super.dispatchTouchEvent(ev);   }   }  }  //處理快速滑動(dòng)時(shí)兩頁覆蓋問題  if(pageIndex == 0){   smoothScrollTo(0, 0);  } else if(pageIndex == 1){   smoothScrollTo(0, scrollView1.getHeight());  }  break;  case MotionEvent.ACTION_UP:  case MotionEvent.ACTION_CANCEL:  if(isToBotttom){   if(Math.abs(getScrollY()) > TO_NEXT_PAGE_HEIGHT){   //移動(dòng)到第二頁   pageIndex = 1;   smoothScrollTo(0, scrollView1.getHeight());   isToBotttom = false;   isToTop = true;   } else {   //回彈   smoothScrollBy(0, -mScroller.getFinalY());   }  } else if(isToTop){   if(scrollView1.getHeight() - getScrollY() > TO_NEXT_PAGE_HEIGHT){   //移動(dòng)到第一頁   pageIndex = 0;   smoothScrollTo(0, 0);   isToBotttom = true;   isToTop = false;   } else {   //回彈   smoothScrollTo(0, scrollView1.getHeight());   }  }  break;  default:  break; } return super.dispatchTouchEvent(ev); }

4.總結(jié)

實(shí)現(xiàn)該控件,需要掌握的知識點(diǎn)主要是自定義控件的基本步驟、Scroller的基本使用和事件分發(fā),當(dāng)然這里最關(guān)鍵的處理還是事件分發(fā)。開頭也說了,雖然這個(gè)有很多人實(shí)現(xiàn)過了,但還是想用自己的方式實(shí)現(xiàn)一遍。大笑三聲,哈哈哈,又實(shí)現(xiàn)一個(gè)自定義控件…博主還在自定義控件學(xué)習(xí)階段,請謹(jǐn)慎使用該控件到項(xiàng)目中。

5.下載

https://github.com/LineChen/TwoPageLayout

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 金坛市| 天柱县| 六盘水市| 东源县| 苍南县| 海门市| 仁化县| 醴陵市| 汝阳县| 湖州市| 盈江县| 敦煌市| 青岛市| 蓬安县| 南乐县| 卢氏县| 平果县| 四川省| 沙坪坝区| 小金县| 全州县| 韶山市| 天镇县| 阿拉尔市| 壤塘县| 汪清县| 台江县| 长泰县| 乌拉特前旗| 周至县| 武邑县| 潢川县| 嘉义市| 阜阳市| 肇庆市| 临武县| 含山县| 固阳县| 云林县| 英吉沙县| 荣昌县|