編輯:關於Android編程
淘寶詳情頁就不用我一一介紹了,昨天逛淘寶看到這個效果時,讓我想起了去年剛學習Android只會使用現成的時候,當時在網上找了一個這種效果的使用了,並不懂怎麼實現的。現在就看到一種效果就想自己實現一下,我想這就是剛接觸某個知識時的好奇心吧
說走咱就走啊,本文只是介紹一種實現思路,網上也已經有了很多種實現方式,有問題請指正
效果圖(我有很用心的找美女圖的)

根據實現思路,我們需要監聽ScrollView是否滑動到頂部和底部,但是ScrollView的setOnScrollChangeListener()方法在api23才添加。主要是重寫ScrollViewonScrollChanged(int l, int t, int oldl, int oldt)方法。
l:當前水平方向滾動值,和getScrollX()相等
t:當前豎直方向滾動值,和getScrollY()相等
oldl:上一次水平滾動值
oldt:上一次豎直滾動值
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);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 顯示調用第二個自孩子的測量方法,不然尺寸有可能為0
*/
View child2 = getChildAt(1);
if (child2 != null) {
child2.measure(widthMeasureSpec, heightMeasureSpec);
}
}
在onFinishInflate中初始化兩個頁面
@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();
}
為兩個頁面設置滑動監聽
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;
}
}
};
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.
此類封裝滾動。您可以使用滾動條(滾輪或OverScroller)收集你需要制作一個滾動的動畫,例如,響應一扔手勢的數據。滾動條為您跟蹤滾動偏移量隨著時間的推移,但他們不會自動將新的位置設置到View中。你的任務是獲取並使用一個合適的速度,使滾動動畫看起來更平滑。
簡而言之,有關滑動的你都可以使用這個實現。
需要重寫的方法
@Override
public void computeScroll() {
super.computeScroll();
//先判斷mScroller滾動是否完成
if (mScroller.computeScrollOffset()) {
//這裡調用View的scrollTo()完成實際的滾動
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//必須調用該方法,否則不一定能看到滾動效果
postInvalidate();
}
}
輔助方法
//調用此方法設置滾動的相對偏移
public void smoothScrollBy(int dx, int dy) {
//設置mScroller的滾動偏移量
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, Math.max(300, Math.abs(dy)));
invalidate();//這裡必須調用invalidate()才能保證computeScroll()會被調用,否則不一定會刷新界面,看不到滾動效果
}
//調用此方法滾動到目標位置
public void smoothScrollTo(int fx, int fy) {
int dx = fx - mScroller.getFinalX();
int dy = fy - mScroller.getFinalY();
smoothScrollBy(dx, dy);
}
最關鍵的部分,邏輯稍復雜,細節處理較多。這裡重寫dispatchTouchEvent。
未滑動到底部,事件由scrollView1自己處理 滑動到底部時,如果繼續向上拖動,攔截事件,父控件處理滑動;繼續向下拖動時,如果父控件(即該控件)當前滾動最後位置(mScroller.getFinalY())不為0, 如果父控件繼續滾動不會出現負值時(出現負值時會導致頭部空白,因為這時是父控件控制,scrollView1不可滑動),不攔截事件,父控件處理滑動,否則,強制滑動到0位置,並把事件下發給子控件顯示第一頁時
未滑動到最頂部時,事件由scrollView2自己處理 滑動到頂部時,如果繼續向下拖動,攔截事件,父控件處理滑動;繼續向上拖動時,如果父控件當前滾動位置小於第一頁高度,攔截事件,父控件處理滑動,否則,滑動到第二頁起始位置,並把事件下發給子控件顯示第二頁時
@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);
}
}
}
//處理快速滑動時兩頁覆蓋問題
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){
//移動到第二頁
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){
//移動到第一頁
pageIndex = 0;
smoothScrollTo(0, 0);
isToBotttom = true;
isToTop = false;
} else {
//回彈
smoothScrollTo(0, scrollView1.getHeight());
}
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
實現該控件,需要掌握的知識點主要是自定義控件的基本步驟、Scroller的基本使用和事件分發,當然這裡最關鍵的處理還是事件分發。開頭也說了,雖然這個有很多人實現過了,但還是想用自己的方式實現一遍。大笑三聲,哈哈哈,又實現一個自定義控件…博主還在自定義控件學習階段,請謹慎使用該控件到項目中。
https://github.com/LineChen/TwoPageLayout
Android View 觸摸事件傳遞機制
PS:以現在的眼光看以前寫的博客感覺寫的很爛,或許或一段時間再看現在的博客會有同樣的感覺。所以每時每刻都去學習,去發現和理解新的東西。引言由於之前寫的一篇關於Androi
android--context分析
前言 Context在android中的作用不言而喻,當我們訪問當前應用的資源,啟動一個新的activity的時候都需要提供Context,而這個Context到底是什麼
Android仿支付寶笑臉刷新加載動畫的實現代碼
看到支付寶的下拉刷新有一個笑臉的動畫,因此自己也動手實現一下。效果圖如下:一、總體思路1、靜態部分的笑臉。這一部分的笑臉就是一個半圓弧,加上兩顆眼睛,這部分比較簡單,用於
接入微信分享過程的喜和淚
背景故事:4月份從公司回到學校,要開始著手做大四的畢業設計。然而畢設的其中一個功能模塊便是——心情分享模塊,在記錄心情的同時可以把心情分享到朋友圈