Android中的滑屏功能的原理是很值得我们去研究的,在知道这两个原理之前,有必要先说说View的两个重要方法,它们就是scrollTo 和scrollBy。
Android View视图是没有边界的,Canvas是没有边界的,只不过我们通过绘制特定的View时对
Canvas对象进行了一定的操作,例如 : translate(平移)、clipRect(剪切)等,以便达到我们的对该Canvas对象绘制的要求 ,
我们可以将这种无边界的视图称为“视图坐标”-----它不受物理屏幕限制。通常我们所理解的一个Layout布局文件只是该视
图的显示区域,超过了这个显示区域将不能显示到父视图的区域中 ,对应的,我们可以将这种无边界的视图称为“布局坐标”
------ 父视图给子视图分配的布局(layout)大小。 而且, 一个视图的在屏幕的其实坐标位于视图坐标起始处,如下图所示。
这么来说吧 ,世界本是无边无界的,可是我们的眼睛我们的心约束了我们所看到的“世界” 。
如下所示:
黑色框框表示该子视图的布局坐标, 褐色框框表示该子视图的视图坐标--该坐标是无限的,超过了父视图给子视图
规定的区域后,不再显示该超出内容。
那么下面的问题就是:如何将我们的视图的任意坐标能显示到该视图的中心坐标上呢? 由于该布局位置是只能显示特定的
一块视图内容 ,因此我们需要通过scrollTo()或者scrollBy()方法将我们期望的视图“滚动”至布局坐标上。
在View.java中提供了了如下两个变量以及相应的属性方法去读取滚动值 ,如下: View.java类中
01
|
/**
|
02
|
*
The offset, in pixels, by which the content of this view is scrolled
|
03
|
*
horizontally.
|
04
|
*
{@hide}
|
05
|
*/
|
06
|
protected int mScrollX; //该视图内容相当于视图起始坐标的偏移量
, X轴 方向
|
07
|
/**
|
08
|
*
The offset, in pixels, by which the content of this view is scrolled
|
09
|
*
vertically.
|
10
|
*
{@hide}
|
11
|
*/
|
12
|
protected int mScrollY; //该视图内容相当于视图起始坐标的偏移量
, Y轴方向
|
13
|
14
|
/**
|
15
|
*
Return the scrolled left position of this view. This is the left edge of
|
16
|
*
the displayed part of your view. You do not need to draw any pixels
|
17
|
*
farther left, since those are outside of the frame of your view on
|
18
|
*
screen.
|
19
|
*
|
20
|
*
@return The left edge of the displayed part of your view, in pixels.
|
21
|
*/
|
22
|
public final int getScrollX()
{
|
23
|
return mScrollX;
|
24
|
}
|
25
|
26
|
/**
|
27
|
*
Return the scrolled top position of this view. This is the top edge of
|
28
|
*
the displayed part of your view. You do not need to draw any pixels above
|
29
|
*
it, since those are outside of the frame of your view on screen.
|
30
|
*
|
31
|
*
@return The top edge of the displayed part of your view, in pixels.
|
32
|
*/
|
33
|
public final int getScrollY()
{
|
34
|
return mScrollY;
|
35
|
}
|
注意,所谓的“by which the content of this view is scrolled”表示该偏移量只针对于该View中onDraw()方法里的
具体内容实现,而不针对背景图片等 。具体原因可参考<Android中View绘制流程以及invalidate()等相关方法分析>
提示:下文中提到的当前视图内容是在绘制在布局坐标处的内容。
public void scrollTo(int x, int y)
说明:在当前视图内容偏移至(x , y)坐标处,即显示(可视)区域位于(x , y)坐标处。
方法原型为: View.java类中
01
|
/**
|
02
|
*
Set the scrolled position of your view. This will cause a call to
|
03
|
*
{@link #onScrollChanged(int, int, int, int)} and the view will be
|
04
|
*
invalidated.
|
05
|
*
@param x the x position to scroll to
|
06
|
*
@param y the y position to scroll to
|
07
|
*/
|
08
|
public void scrollTo( int x, int y)
{
|
09
|
//偏移位置发生了改变
|
10
|
if (mScrollX
!= x || mScrollY != y) {
|
11
|
int oldX
= mScrollX;
|
12
|
int oldY
= mScrollY;
|
13
|
mScrollX
= x; //赋新值,保存当前偏移量
|
14
|
mScrollY
= y;
|
15
|
//回调onScrollChanged方法
|
16
|
onScrollChanged(mScrollX,
mScrollY, oldX, oldY);
|
17
|
if (!awakenScrollBars())
{
|
18
|
invalidate(); //一般都引起重绘
|
19
|
}
|
20
|
}
|
21
|
}
|
public void scrollBy(int x, int y)
说明:在当前视图内容继续偏移(x , y)个单位,显示(可视)区域也跟着偏移(x,y)个单位。
方法原型为: View.java类中
01
|
/**
|
02
|
*
Move the scrolled position of your view. This will cause a call to
|
03
|
*
{@link #onScrollChanged(int, int, int, int)} and the view will be
|
04
|
*
invalidated.
|
05
|
*
@param x the amount of pixels to scroll by horizontally
|
06
|
*
@param y the amount of pixels to scroll by vertically
|
07
|
*/
|
08
|
//
看出原因了吧 。。 mScrollX 与 mScrollY 代表我们当前偏移的位置 , 在当前位置继续偏移(x ,y)个单位
|
09
|
public void scrollBy( int x, int y)
{
|
10
|
scrollTo(mScrollX
+ x, mScrollY + y);
|
11
|
}
|
第一个小Demo非常简单 ,大家重点理解与掌握scrollTo() 与 scrollBy()函数的用法和区别。
第二个小Demo则有了Launcher的模样,能够左右切换屏幕 。实现功能如下: 采用了一个自定义ViewGroup,该ViewGroup
对象包含了3个LinearLayout子视图,并且以一定的布局坐标(由layout()方法指定)显示在ViewGroup上。 接下来,即可调用该
ViewGroup对象的scrollTo或者scrollBy()方法切换指定视图内容了,即切换屏幕。 呵呵 ,挺好玩的吧 。
如果对View绘制流程不懂的,可以参考我的这篇博客<Android中View绘制流程以及invalidate()等相关方法分析> 。
截图如下:
自定义ViewGroup如下:
01
|
//自定义ViewGroup
, 包含了三个LinearLayout控件,存放在不同的布局位置,通过scrollBy或者scrollTo方法切换
|
02
|
public class MultiViewGroup extends ViewGroup
{
|
03
|
04
|
private Context
mContext;
|
05
|
06
|
private static String
TAG = "MultiViewGroup" ;
|
07
|
08
|
public MultiViewGroup(Context
context) {
|
09
|
super (context);
|
10
|
mContext
= context;
|
11
|
init();
|
12
|
}
|
13
|
14
|
public MultiViewGroup(Context
context, AttributeSet attrs) {
|
15
|
super (context,
attrs);
|
16
|
mContext
= context;
|
17
|
init();
|
18
|
}
|
19
|
20
|
private void init()
{
|
21
|
//
初始化3个 LinearLayout控件
|
22
|
LinearLayout
oneLL = new LinearLayout(mContext);
|
23
|
oneLL.setBackgroundColor(Color.RED);
|
24
|
addView(oneLL);
|
25
|
|
26
|
LinearLayout
twoLL = new LinearLayout(mContext);
|
27
|
twoLL.setBackgroundColor(Color.YELLOW);
|
28
|
addView(twoLL);
|
29
|
|
30
|
LinearLayout
threeLL = new LinearLayout(mContext);
|
31
|
threeLL.setBackgroundColor(Color.BLUE);
|
32
|
addView(threeLL);
|
33
|
}
|
34
|
35
|
//
measure过程
|
36
|
@Override
|
37
|
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec)
{
|
38
|
39
|
Log.i(TAG, "---
start onMeasure --" );
|
40
|
41
|
//
设置该ViewGroup的大小
|
42
|
int width
= MeasureSpec.getSize(widthMeasureSpec);
|
43
|
int height
= MeasureSpec.getSize(heightMeasureSpec);
|
44
|
setMeasuredDimension(width,
height);
|
45
|
46
|
int childCount
= getChildCount();
|
47
|
Log.i(TAG, "---
onMeasure childCount is -->" +
childCount);
|
48
|
for ( int i
= 0 ;
i < childCount; i++) {
|
49
|
View
child = getChildAt(i);
|
50
|
//
设置每个子视图的大小 , 即全屏
|
51
|
child.measure(MultiScreenActivity.screenWidth,
MultiScreenActivity.scrrenHeight);
|
52
|
}
|
53
|
}
|
54
|
55
|
//
layout过程
|
56
|
@Override
|
57
|
protected void onLayout( boolean changed, int l, int t, int r, int b)
{
|
58
|
//
TODO Auto-generated method stub
|
59
|
Log.i(TAG, "---
start onLayout --" );
|
60
|
int startLeft
= 0 ; //
每个子视图的起始布局坐标
|
61
|
int startTop
= 10 ; //
间距设置为10px 相当于 android:marginTop= "10px"
|
62
|
int childCount
= getChildCount();
|
63
|
Log.i(TAG, "---
onLayout childCount is -->" +
childCount);
|
64
|
for ( int i
= 0 ;
i < childCount; i++) {
|
65
|
View
child = getChildAt(i);
|
66
|
child.layout(startLeft,
startTop,
|
67
|
startLeft
+ MultiScreenActivity.screenWidth,
|
68
|
startTop
+ MultiScreenActivity.scrrenHeight);
|
69
|
startLeft
= startLeft + MultiScreenActivity.screenWidth ; //校准每个子View的起始布局位置
|
70
|
//三个子视图的在屏幕中的分布如下
[0 , 320] / [320,640] / [640,960]
|
71
|
}
|
72
|
}
|
73
|
74
|
}
|
PS :大家可以分别给这几个LinearLayout试着添加几个子View,例如TextView, Button等 。
至于Launcher上滑屏功能的实现,我尝试着去掌握,可能天资愚钝吧,对Scoller类很是感冒,现今还没有掌握好,不过在此
给大家推荐几个不错的学习资源 。
1、 Scoller类介绍:android 中文 api (64) —— Scroller
2、相关资源汇总:Http://blog.csdn.net/dellheng/article/details/7164275
3,launcher修改--左右滑动屏幕切换源码追踪