Android模仿淘宝 商品详情拖动查看图文详情效果

tx.gif

最近要在公司的项目中实现这种效果,就自己动手实现了一下。

使用

GraphicDetailsLayout gdLayout = (GraphicDetailsLayout) findViewById(R.id.gdlayout);
gdLayout.addFragment(new Fragment[] {new SpFragment(), new DeFragment()}, getSupportFragmentManager());

还是很简单的,把上下两个fragment添加到GraphicDetailsLayout 中就可以了

思路

从效果中可以看到上下两个控件都是可以滚动的,初始化状态下,下面的控件是隐藏在屏幕下面的;那我们设计最外面的布局是LinearLayout,然后LinearLayout里面放两个ScrollView,ScrollView滚动到顶部或顶部的时候,告诉LinearLayout拦截事件,来实现两个ScrollView的上下拖动效果。

实现

首先自定义一个ScrollView

public class GDScrollView extends ScrollView {    

   private LinearLayout mLl;    
   private int mLlHeight;    
   public static final String TAG_ONE = "up";    
   public static final String TAG_TWO = "down";    
   public static final int ID_ONE = 11111;    
   public static final int ID_TWO = 22222;    
   private GraphicDetailsLayout.ScrollListener mScrollListener;   

   public GDScrollView(Context context) {        
       super(context);    }    

   public GDScrollView(Context context, AttributeSet attrs) {   
       super(context, attrs);    
   }    

   public GDScrollView(Context context, AttributeSet attrs, int defStyleAttr) {        
       super(context, attrs, defStyleAttr);    
   }    

   @TargetApi(Build.VERSION_CODES.LOLLIPOP)    
   public GDScrollView(Context context, AttributeSet attrs, int defStyleAttr, 
    int defStyleRes) {        
       super(context, attrs, defStyleAttr, defStyleRes);    
   }    

   public void setScrollListener(GraphicDetailsLayout.ScrollListener scrollListener) {        
        mScrollListener = scrollListener;    
   }    

   public void addFragment(Fragment fragment, FragmentManager fragmentManager) {        
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        
        if(mLl == null) mLl = (LinearLayout) getChildAt(0);        
        fragmentTransaction.replace(mLl.getId(), fragment);  
        fragmentTransaction.commit();    }    

   @Override    
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        mLlHeight = mLl.getMeasuredHeight();    
   }    

   @Override    
   protected void onFinishInflate() {        
        super.onFinishInflate();        
        mLl = (LinearLayout) getChildAt(0);    
   }    

   @Override    
   protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {        
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);   
        if(getTag().equals(TAG_ONE)) {            
          //上面的界面滚动到底部的时候            
           if(isScrollBottom()) {                
               criticalPointOperation(false, true, TAG_ONE);           
           }        
         }       

        if(getTag().equals(TAG_TWO)) {    
          //下面的界面滚动到顶部的时候        
          if(getScrollY() <= 0) {                
              criticalPointOperation(false, true, TAG_TWO);           
           }        
         }    
    }    

   private void criticalPointOperation(boolean allow, boolean intercept, String tag) {      
       getParent().requestDisallowInterceptTouchEvent(allow);  
       if(mScrollListener != null) mScrollListener.scrollBottom(intercept, tag);    }    

   public boolean isScrollBottom() {        
       return getScrollY() >= (mLlHeight - getMeasuredHeight());    
   }
}

重写onOverScrolled方法监控滚动的状态,判断不同的ScrollView滚动到顶部或者顶部触发回调事件,把触摸事件交给上层LinarLayout控件,来看看LinearLayout的onTouchEvent方法

@Override
public boolean onTouchEvent(MotionEvent event) {    
   switch (event.getAction()) {        
     case MotionEvent.ACTION_MOVE:            
       if(mInitY == 0) {                
          mInitY = event.getY();            
       } else {                
          int offset = (int) Math.abs(event.getY() - mInitY);             
          if(offset > mTouchSlop) {                    
            int delayOffset = offset * 7 / 10;    
            if(mCurrentTag.equals(GDScrollView.TAG_ONE)) {   
                  mUpSVMarginTop = mInitMarginTop - delayOffset;      
              } else {                        
                  mUpSVMarginTop = - halfHeight + delayOffset;       
             }             
       
          if(mUpSVMarginTop > 0) mUpSVMarginTop = 0;      
            requestLayout();                
         }            
      }            
      break;        
    case MotionEvent.ACTION_UP:            
       mIntercept = false;            
       mInitY = 0;   
       if(mCurrentTag.equals(GDScrollView.TAG_ONE)) {          
           if(Math.abs(mUpSVMarginTop) > halfHeight / 3) {   
              startAnimation(mUpSVMarginTop, halfHeight - Math.abs(mUpSVMarginTop), false);                
           } else {                    
              startAnimation(mUpSVMarginTop, Math.abs(mUpSVMarginTop), true);                
           }            
        } else {                
           if(Math.abs(mUpSVMarginTop) < halfHeight * 2 / 3) {        
                startAnimation(mUpSVMarginTop, Math.abs(mUpSVMarginTop), true);                
            } else {                    
                startAnimation(mUpSVMarginTop, halfHeight - Math.abs(mUpSVMarginTop), false);                
            }            
        }            
         requestLayout();            
         break;    
      }    
      return true;
  }

LinearLayout拦截到事件以后重写onTouchEvent方法,通过手势拖动来不断的计算ScrooView距离顶部的高度mUpSVMarginTop,调用requestLayout方法发起重新布局,重写onLayout方法

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {    
    super.onLayout(changed, l, t, r, b);    
    halfHeight = getMeasuredHeight() / 2;    
    mUpScrollView.layout(0, mUpSVMarginTop, getMeasuredWidth(), mUpSVMarginTop + halfHeight);    
    mBottomScrollView.layout(0, mUpSVMarginTop + halfHeight , getMeasuredWidth(), mUpSVMarginTop + getMeasuredHeight());}

大致的思路和实现已经讲解完成了,想看具体实现代码
https://github.com/chenpengfei88/GraphicDetailsLayout
欢迎start,follow。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容