怎么在Android中通过自定义ViewGroup实现淘宝商品详情页

发布时间:2021-05-24 18:12:52 作者:Leah
来源:亿速云 阅读:189

这篇文章给大家介绍怎么在Android中通过自定义ViewGroup实现淘宝商品详情页,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

package com.mcoy.snapscrollview;
 
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
 

public class McoySnapPageLayout extends ViewGroup {
 
  。。。。
 
 
 public interface McoySnapPage {
 /**
 * 返回page根节点
 * 
 * @return
 */
 View getRootView();
 
 /**
 * 是否滑动到最顶端
 * 第二页必须自己实现此方法,来判断是否已经滑动到第二页的顶部
 * 并决定是否要继续滑动到第一页
 */
 boolean isAtTop();
 
 /**
 * 是否滑动到最底部
 * 第一页必须自己实现此方法,来判断是否已经滑动到第二页的底部
 * 并决定是否要继续滑动到第二页
 */
 boolean isAtBottom();
 }
 
 public interface PageSnapedListener {
 
 /**
 * @mcoy
 * 当从某一页滑动到另一页完成时的回调函数
 */
 void onSnapedCompleted(int derection);
 }
 
  。。。。。。
 
 /**
 * 设置上下页面
 * @param pageTop
 * @param pageBottom
 */
 public void setSnapPages(McoySnapPage pageTop, McoySnapPage pageBottom) {
 mPageTop = pageTop;
 mPageBottom = pageBottom;
 addPagesAndRefresh();
 }
 
 private void addPagesAndRefresh() {
 // 设置页面id
 mPageTop.getRootView().setId(0);
 mPageBottom.getRootView().setId(1);
 addView(mPageTop.getRootView());
 addView(mPageBottom.getRootView());
 postInvalidate();
 }
 
 /**
 * @mcoy add
 * computeScroll方法会调用postInvalidate()方法, 而postInvalidate()方法中系统
 * 又会调用computeScroll方法, 因此会一直在循环互相调用, 循环的终结点是在computeScrollOffset()
 * 当computeScrollOffset这个方法返回false时,说明已经结束滚动。
 * 
 * 重要:真正的实现此view的滚动是调用scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
 */
 @Override
 public void computeScroll() {
 //先判断mScroller滚动是否完成
 if (mScroller.computeScrollOffset()) {
 if (mScroller.getCurrY() == (mScroller.getFinalY())) {
 if (mNextDataIndex > mDataIndex) {
  mFlipDrection = FLIP_DIRECTION_DOWN;
  makePageToNext(mNextDataIndex);
 } else if (mNextDataIndex < mDataIndex) {
  mFlipDrection = FLIP_DIRECTION_UP;
  makePageToPrev(mNextDataIndex);
 }else{
  mFlipDrection = FLIP_DIRECTION_CUR;
 }
 if(mPageSnapedListener != null){
  mPageSnapedListener.onSnapedCompleted(mFlipDrection);
 }
 }
 //这里调用View的scrollTo()完成实际的滚动
 scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
 //必须调用该方法,否则不一定能看到滚动效果
 postInvalidate();
 }
 }
 
 private void makePageToNext(int dataIndex) {
 mDataIndex = dataIndex;
  mCurrentScreen = getCurrentScreen();
 }
 
 private void makePageToPrev(int dataIndex) {
 mDataIndex = dataIndex;
  mCurrentScreen = getCurrentScreen();
 }
 
 public int getCurrentScreen() {
 for (int i = 0; i < getChildCount(); i++) {
 if (getChildAt(i).getId() == mDataIndex) {
 return i;
 }
 }
 return mCurrentScreen;
 }
 
 public View getCurrentView() {
 for (int i = 0; i < getChildCount(); i++) {
 if (getChildAt(i).getId() == mDataIndex) {
 return getChildAt(i);
 }
 }
 return null;
 }
 
 /*
 * (non-Javadoc)
 * 
 * @see
 * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
 * 重写了父类的onInterceptTouchEvent(),主要功能是在onTouchEvent()方法之前处理
 * touch事件。包括:down、up、move事件。
 * 当onInterceptTouchEvent()返回true时进入onTouchEvent()。
 */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
 final int action = ev.getAction();
 if ((action == MotionEvent.ACTION_MOVE)
 && (mTouchState != TOUCH_STATE_REST)) {
 return true;
 }
 final float x = ev.getX();
 final float y = ev.getY();
 
 switch (action) {
 case MotionEvent.ACTION_MOVE:
 // 记录y与mLastMotionY差值的绝对值。
   // yDiff大于gapBetweenTopAndBottom时就认为界面拖动了足够大的距离,屏幕就可以移动了。
 final int yDiff = (int)(y - mLastMotionY);
 boolean yMoved = Math.abs(yDiff) > gapBetweenTopAndBottom;
 if (yMoved) {
 if(MCOY_DEBUG) {
  Log.e(TAG, "yDiff is " + yDiff);
  Log.e(TAG, "mPageTop.isFlipToBottom() is " + mPageTop.isAtBottom());
  Log.e(TAG, "mCurrentScreen is " + mCurrentScreen);
  Log.e(TAG, "mPageBottom.isFlipToTop() is " + mPageBottom.isAtTop());
 }
 if(yDiff < 0 && mPageTop.isAtBottom() && mCurrentScreen == 0 
  || yDiff > 0 && mPageBottom.isAtTop() && mCurrentScreen == 1){
  Log.e("mcoy", "121212121212121212121212");
  mTouchState = TOUCH_STATE_SCROLLING;
 }
 }
 break;
 case MotionEvent.ACTION_DOWN:
 // Remember location of down touch
 mLastMotionY = y;
 Log.e("mcoy", "mScroller.isFinished() is " + mScroller.isFinished());
 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
  : TOUCH_STATE_SCROLLING;
 break;
 case MotionEvent.ACTION_CANCEL:
 case MotionEvent.ACTION_UP:
 // Release the drag
 mTouchState = TOUCH_STATE_REST;
 break;
 }
 boolean intercept = mTouchState != TOUCH_STATE_REST;
 Log.e("mcoy", "McoySnapPageLayout---onInterceptTouchEvent return " + intercept);
 return intercept;
 }
 
 /*
 * (non-Javadoc)
 * 
 * @see android.view.View#onTouchEvent(android.view.MotionEvent)
 * 主要功能是处理onInterceptTouchEvent()返回值为true时传递过来的touch事件
 */
 @Override
 public boolean onTouchEvent(MotionEvent ev) {
 Log.e("mcoy", "onTouchEvent--" + System.currentTimeMillis());
  if (mVelocityTracker == null) {
   mVelocityTracker = VelocityTracker.obtain();
  }
  mVelocityTracker.addMovement(ev);
  
 final int action = ev.getAction();
 final float x = ev.getX();
 final float y = ev.getY();
 switch (action) {
 case MotionEvent.ACTION_DOWN:
 if (!mScroller.isFinished()) {
 mScroller.abortAnimation();
 }
 break;
 case MotionEvent.ACTION_MOVE:
  if(mTouchState != TOUCH_STATE_SCROLLING){
     // 记录y与mLastMotionY差值的绝对值。
     // yDiff大于gapBetweenTopAndBottom时就认为界面拖动了足够大的距离,屏幕就可以移动了。
    final int yDiff = (int) Math.abs(y - mLastMotionY);
    boolean yMoved = yDiff > gapBetweenTopAndBottom;
    if (yMoved) {
     mTouchState = TOUCH_STATE_SCROLLING;
    }
   }
   // 手指拖动屏幕的处理
   if ((mTouchState == TOUCH_STATE_SCROLLING)) {
    // Scroll to follow the motion event
    final int deltaY = (int) (mLastMotionY - y);
    mLastMotionY = y;
    final int scrollY = getScrollY();
    if(mCurrentScreen == 0){//显示第一页,只能上拉时使用
     if(mPageTop != null && mPageTop.isAtBottom()){
     scrollBy(0, Math.max(-1 * scrollY, deltaY));
     }
    }else{
     if(mPageBottom != null && mPageBottom.isAtTop()){
     scrollBy(0, deltaY);
     }
    }
   }
 break;
 case MotionEvent.ACTION_CANCEL:
 case MotionEvent.ACTION_UP:
 // 弹起手指后,切换屏幕的处理
 if (mTouchState == TOUCH_STATE_SCROLLING) {
  final VelocityTracker velocityTracker = mVelocityTracker;
    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
    int velocityY = (int) velocityTracker.getYVelocity();
    if (Math.abs(velocityY) > SNAP_VELOCITY) {
     if( velocityY > 0 && mCurrentScreen == 1 && mPageBottom.isAtTop()){
      snapToScreen(mDataIndex-1);
     }else if(velocityY < 0 && mCurrentScreen == 0){
      snapToScreen(mDataIndex+1);
     }else{
      snapToScreen(mDataIndex);
     }
    } else {
     snapToDestination();
    }
    if (mVelocityTracker != null) {
     mVelocityTracker.recycle();
     mVelocityTracker = null;
    }
 }else{
 }
 mTouchState = TOUCH_STATE_REST;
 break;
 
 default:
 break;
 }
 return true;
 }
 
 private void clearOnTouchEvents(){
 mTouchState = TOUCH_STATE_REST;
 if (mVelocityTracker != null) {
    mVelocityTracker.recycle();
    mVelocityTracker = null;
   }
 }
 
 private void snapToDestination() {
 // 计算应该去哪个屏
 final int flipHeight = getHeight() / 8;
  
  int whichScreen = -1;
  final int topEdge = getCurrentView().getTop();
 
  if(topEdge < getScrollY() && (getScrollY()-topEdge) >= flipHeight && mCurrentScreen == 0){
   //向下滑动 
   whichScreen = mDataIndex + 1;
  }else if(topEdge > getScrollY() && (topEdge - getScrollY()) >= flipHeight && mCurrentScreen == 1){
   //向上滑动
   whichScreen = mDataIndex - 1;
  }else{
   whichScreen = mDataIndex;
  }
  Log.e(TAG, "snapToDestination mDataIndex = " + mDataIndex);
  Log.e(TAG, "snapToDestination whichScreen = " + whichScreen);
  snapToScreen(whichScreen);
 }
 
 private void snapToScreen(int dataIndex) {
  if (!mScroller.isFinished())
   return;
  
  final int direction = dataIndex - mDataIndex;
  mNextDataIndex = dataIndex;
  boolean changingScreens = dataIndex != mDataIndex;
  View focusedChild = getFocusedChild();
  if (focusedChild != null && changingScreens) {
   focusedChild.clearFocus();
  }
  //在这里判断是否已到目标位置~
  int newY = 0;
 switch (direction) {
 case 1: //需要滑动到第二页
 Log.e(TAG, "the direction is 1");
 newY = getCurrentView().getBottom(); // 最终停留的位置
 break;
 case -1: //需要滑动到第一页
 Log.e(TAG, "the direction is -1");
 Log.e(TAG, "getCurrentView().getTop() is "
  + getCurrentView().getTop() + " getHeight() is "
  + getHeight());
 newY = getCurrentView().getTop() - getHeight(); // 最终停留的位置
 break;
 case 0: //滑动距离不够, 因此不造成换页,回到滑动之前的位置
 Log.e(TAG, "the direction is 0");
 newY = getCurrentView().getTop(); //第一页的top是0, 第二页的top应该是第一页的高度
 break;
 default:
 break;
 }
  final int cy = getScrollY(); // 启动的位置
  Log.e(TAG, "the newY is " + newY + " cy is " + cy);
  final int delta = newY - cy; // 滑动的距离,正值是往左滑<—,负值是往右滑—>
  mScroller.startScroll(0, cy, 0, delta, Math.abs(delta));
  invalidate();
 }
 

 
}

McoySnapPage是定义在VIewGroup的一个接口, 比如说我们需要类似某东商品详情那样,有上下两页的效果。 那我就需要自己定义两个类实现这个接口,并实现接口的方法。getRootView需要返回当前页需要显示的布局内容;isAtTop需要返回当前页是否已经在顶端; isAtBottom需要返回当前页是否已经在底部

onInterceptTouchEventonTouchEvent决定当前的滑动状态, 并决定是有当前VIewGroup拦截touch事件还是由子view去消费touch事件

Android是什么

Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。

关于怎么在Android中通过自定义ViewGroup实现淘宝商品详情页就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

推荐阅读:
  1. python如何爬取淘宝商品详情页数据
  2. Android中怎么使用ViewGroup自定义布局

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

android viewgroup

上一篇:使用ExpandableListView怎么实现一个二级列表

下一篇:怎么在Android中实现状态切换布局

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》