Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

Stella981
• 阅读 702

一、淘宝商品详情页效果

Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

我们的效果

Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

二、实现思路

     使用两个scrollView,两个scrollView 竖直排列,通过自定义viewGroup来控制两个scrollView的竖直排列,以及滑动事件的处理。如下图

三、具体实现

1、继承viewGroup自定义布局View 重写onMeasure()和onLayout方法,在onLayout方法中完成对两个子ScrollView的竖直排列布局,代码如下:
布局文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

<RelativeLayout xmlns:android= "[http://schemas.android.com/apk/res/android](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fschemas.android.com%2Fapk%2Fres%2Fandroid)"

xmlns:tools= "[http://schemas.android.com/tools](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fschemas.android.com%2Ftools)"

android:layout_width= "match_parent"

android:layout_height= "match_parent"

tools:context= "com.baoyunlong.view.pulluptoloadmore.MainActivity" >

<com.baoyunlong.view.pulluptoloadmore.PullUpToLoadMore

android:layout_width= "match_parent"

android:layout_height= "match_parent"

android:orientation= "vertical" >

<com.baoyunlong.view.pulluptoloadmore.MyScrollView

android:layout_width= "match_parent"

android:layout_height= "match_parent"

android:fillViewport= "true" >

<LinearLayout

android:layout_width= "match_parent"

android:layout_height= "match_parent"

android:orientation= "vertical" >

<ImageView

android:scaleType= "fitXY"

android:src= "@drawable/a1"

android:layout_width= "match_parent"

android:layout_height= "180dp" />

<TextView

android:text= "这里是标题"

android:textSize= "18dp"

android:layout_marginRight= "10dp"

android:layout_marginLeft= "10dp"

android:layout_marginTop= "10dp"

android:layout_width= "match_parent"

android:layout_height= "wrap_content" />

<TextView

android:layout_marginTop= "10dp"

android:text= "子标题"

android:layout_marginLeft= "10dp"

android:layout_marginRight= "10dp"

android:textSize= "18dp"

android:layout_width= "match_parent"

android:layout_height= "wrap_content" />

..............

<LinearLayout

android:layout_height= "0dp"

android:layout_weight= "1"

android:gravity= "bottom"

android:layout_width= "match_parent" >

<TextView

android:layout_width= "match_parent"

android:layout_height= "wrap_content"

android:height= "50dp"

android:background= "#b11"

android:gravity= "center"

android:text= "继续拖动查看图文详情"

android:textColor= "#000" />

</LinearLayout>

</LinearLayout>

</com.baoyunlong.view.pulluptoloadmore.MyScrollView>

<com.baoyunlong.view.pulluptoloadmore.MyScrollView

android:layout_width= "match_parent"

android:layout_height= "match_parent"

android:fillViewport= "true" >

<LinearLayout

android:layout_width= "match_parent"

android:layout_height= "match_parent"

android:gravity= "center"

android:orientation= "vertical" >

<ImageView

android:layout_width= "wrap_content"

android:layout_height= "wrap_content"

android:src= "@drawable/a1" />

<ImageView

android:layout_width= "wrap_content"

android:layout_height= "wrap_content"

android:src= "@drawable/a3" />

.........

</LinearLayout>

</com.baoyunlong.view.pulluptoloadmore.MyScrollView> </com.baoyunlong.view.pulluptoloadmore.PullUpToLoadMore>

</RelativeLayout>

代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

public class PullUpToLoadMore extends ViewGroup {

public PullUpToLoadMore(Context context) {

super (context);

}

public PullUpToLoadMore(Context context, AttributeSet attrs) {

super (context, attrs);

}

public PullUpToLoadMore(Context context, AttributeSet attrs, int defStyleAttr) {

super (context, attrs, defStyleAttr);

}

[@Override](https://my.oschina.net/u/1162528)

protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {

super .onMeasure(widthMeasureSpec, heightMeasureSpec);

measureChildren(widthMeasureSpec, heightMeasureSpec);

}

[@Override](https://my.oschina.net/u/1162528)

protected void onLayout( boolean changed, int l, int t, int r, int b) {

int childCount = getChildCount();

int childTop = t;

for ( int i = 0 ; i < childCount; i++) {

View child = getChildAt(i);

child.layout(l, childTop, r, childTop + child.getMeasuredHeight());

childTop += child.getMeasuredHeight();

}

}

}

2、处理滑动事件   

   规则如下 :     

   (1)、当处于第一屏时 第一个ScrollView已经滑动到底部并且滑动方向是往上滑动,这个时候滑动事件应该交给父view处理也就是拦截事件让onInterceptTouchEvent返回true.然后父view通过scrollBy()方法滚动,显示出第二个scrollView。   

   (2)、当处于第二屏时 第二个ScrollView已经滑动到顶部并且滑动方向是往下滑动,这个时候滑动事件交给父view处理,根据滑动事件显示出第一个ScrollView。

   (3)、当手指离开屏幕时,根据滑动速度来决定是回弹到第一个ScrollView还是第二个ScrollView,通过VelocityTracker来获取滑动速度。

3、一些细节的处理

        (1)、如果仔细看观察淘宝的实现效果你会发现,当你滑动到刚刚看到 “继续拖动,查看图文详情”的时候,手指抬起,然后再按下重新向上拖动你会发现,第二页并不会划出来,而是停留在了“继续拖动,查看图文详情”的底部,京东的效果也是一样。这样用户体验不太好,我们来优化一下。其实通过查看ScrollView的源码可以看出来,这是因为ScrollView类的onTouchEvent方法的默认实现,调用了parent.requestDisallowInterceptTouchEvent(true)方法 阻止了我们拦截事件,导致我们父view的onInterceptTouchEvent方法无法执行,也就拦截不到事件,拦截不到事件我们的onTouchEvent就无法执行,onTouchEvent无法执行,我们写在onTouchEvent里面的滚动逻辑就执行不到了,导致了上面我们看到的划不动的效果。解决方法就是,我们需要重写dispatchTouchEvent()方法,防止子view干扰我们,这样我们滑动的时候就可以一气呵成了。代码如下:

1

2

3

4

5

6

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

//防止子View禁止父view拦截事件

this .requestDisallowInterceptTouchEvent( false );

return super .dispatchTouchEvent(ev);

}

      (2)、监听ScrollView滑动事件的问题

          ScrollView没有提供滚动事件的监听方法,也就没法判断是否滚动到了顶部,或者底部,这里我们继承ScrollView 自己实现滚动事件监听。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/**

* Created by baoyunlong on 16/6/8.

*/

public class MyScrollView extends ScrollView {

private static String TAG=MyScrollView. class .getName();

public void setScrollListener(ScrollListener scrollListener) {

this .mScrollListener = scrollListener;

}

private ScrollListener mScrollListener;

public MyScrollView(Context context) {

super (context);

}

public MyScrollView(Context context, AttributeSet attrs) {

super (context, attrs);

}

public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {

super (context, attrs, defStyleAttr);

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

switch (ev.getAction()){

case MotionEvent.ACTION_MOVE:

if (mScrollListener!= null ){

int contentHeight=getChildAt( 0 ).getHeight();

int scrollHeight=getHeight();

int scrollY=getScrollY();

mScrollListener.onScroll(scrollY);

if (scrollY+scrollHeight>=contentHeight||contentHeight<=scrollHeight){

mScrollListener.onScrollToBottom();

} else {

mScrollListener.notBottom();

}

if (scrollY== 0 ){

mScrollListener.onScrollToTop();

}

}

break ;

}

boolean result= super .onTouchEvent(ev);

requestDisallowInterceptTouchEvent( false );

return result;

}

public interface ScrollListener{

void onScrollToBottom();

void onScrollToTop();

void onScroll( int scrollY);

void notBottom();

}

4、完整代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

/**

* Created by baoyunlong on 16/6/8.

*/

public class PullUpToLoadMore extends ViewGroup {

public static String TAG = PullUpToLoadMore. class .getName();

MyScrollView topScrollView, bottomScrollView;

VelocityTracker velocityTracker = VelocityTracker.obtain();

Scroller scroller = new Scroller(getContext());

int currPosition = 0 ;

int position1Y;

int lastY;

public int scaledTouchSlop; //最小滑动距离

int speed = 200 ;

boolean isIntercept;

public boolean bottomScrollVIewIsInTop = false ;

public boolean topScrollViewIsBottom = false ;

public PullUpToLoadMore(Context context) {

super (context);

init();

}

public PullUpToLoadMore(Context context, AttributeSet attrs) {

super (context, attrs);

init();

}

public PullUpToLoadMore(Context context, AttributeSet attrs, int defStyleAttr) {

super (context, attrs, defStyleAttr);

init();

}

private void init() {

post( new Runnable() {

@Override

public void run() {

topScrollView = (MyScrollView) getChildAt( 0 );

bottomScrollView = (MyScrollView) getChildAt( 1 );

topScrollView.setScrollListener( new MyScrollView.ScrollListener() {

@Override

public void onScrollToBottom() {

topScrollViewIsBottom = true ;

}

@Override

public void onScrollToTop() {

}

@Override

public void onScroll( int scrollY) {

}

@Override

public void notBottom() {

topScrollViewIsBottom = false ;

}

});

bottomScrollView.setScrollListener( new MyScrollView.ScrollListener() {

@Override

public void onScrollToBottom() {

}

@Override

public void onScrollToTop() {

}

@Override

public void onScroll( int scrollY) {

if (scrollY == 0 ) {

bottomScrollVIewIsInTop = true ;

} else {

bottomScrollVIewIsInTop = false ;

}

}

@Override

public void notBottom() {

}

});

position1Y = topScrollView.getBottom();

scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

}

});

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

//防止子View禁止父view拦截事件

this .requestDisallowInterceptTouchEvent( false );

return super .dispatchTouchEvent(ev);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

int y = ( int ) ev.getY();

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

lastY = y;

break ;

case MotionEvent.ACTION_MOVE:

//判断是否已经滚动到了底部

if (topScrollViewIsBottom) {

int dy = lastY - y;

//判断是否是向上滑动和是否在第一屏

if (dy > 0 && currPosition == 0 ) {

if (dy >= scaledTouchSlop) {

isIntercept = true ; //拦截事件

lastY=y;

}

}

}

if (bottomScrollVIewIsInTop) {

int dy = lastY - y;

//判断是否是向下滑动和是否在第二屏

if (dy < 0 && currPosition == 1 ) {

if (Math.abs(dy) >= scaledTouchSlop) {

isIntercept = true ;

}

}

}

break ;

}

return isIntercept;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

int y = ( int ) event.getY();

velocityTracker.addMovement(event);

switch (event.getAction()) {

case MotionEvent.ACTION_MOVE:

int dy = lastY - y;

if (getScrollY() + dy < 0 ) {

dy = getScrollY() + dy + Math.abs(getScrollY() + dy);

}

if (getScrollY() + dy + getHeight() > bottomScrollView.getBottom()) {

dy = dy - (getScrollY() + dy - (bottomScrollView.getBottom() - getHeight()));

}

scrollBy( 0 , dy);

break ;

case MotionEvent.ACTION_UP:

isIntercept = false ;

velocityTracker.computeCurrentVelocity( 1000 );

float yVelocity = velocityTracker.getYVelocity();

if (currPosition == 0 ) {

if (yVelocity < 0 && yVelocity < -speed) {

smoothScroll(position1Y);

currPosition = 1 ;

} else {

smoothScroll( 0 );

}

} else {

if (yVelocity > 0 && yVelocity > speed) {

smoothScroll( 0 );

currPosition = 0 ;

} else {

smoothScroll(position1Y);

}

}

break ;

}

lastY = y;

return true ;

}

@Override

protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {

super .onMeasure(widthMeasureSpec, heightMeasureSpec);

measureChildren(widthMeasureSpec, heightMeasureSpec);

}

@Override

protected void onLayout( boolean changed, int l, int t, int r, int b) {

int childCount = getChildCount();

int childTop = t;

for ( int i = 0 ; i < childCount; i++) {

View child = getChildAt(i);

child.layout(l, childTop, r, childTop + child.getMeasuredHeight());

childTop += child.getMeasuredHeight();

}

}

//通过Scroller实现弹性滑动

private void smoothScroll( int tartY) {

int dy = tartY - getScrollY();

scroller.startScroll(getScrollX(), getScrollY(), 0 , dy);

invalidate();

}

@Override

public void computeScroll() {

if (scroller.computeScrollOffset()) {

scrollTo(scroller.getCurrX(), scroller.getCurrY());

postInvalidate();

}

}

}

源码:

github地址

以上所述是小编给大家介绍的Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

您可能感兴趣的文章:

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这