1.View生命周期
运行日志:
创建view 的日志信息 (自定义View 配置到xml文件中):
android:visibility=gone
08-11 17:04:23.047: V/yixi(1490):--------->construct(),input params=2
08-11 17:04:23.047: V/yixi(1490):--------->onFinishInflate()
08-11 17:04:23.067: V/yixi(1490):--------->onAttachedToWindow()
08-11 17:04:23.067: V/yixi(1490):--------->onWindowVisibilityChanged()
08-11 17:04:23.197: V/yixi(1490):--------->onWindowFocusChanged()
android:visibility=invisible
08-11 17:02:26.737: V/yixi(670):--------->construct(),input params=2
08-11 17:02:26.737: V/yixi(670):--------->onFinishInflate()
08-11 17:02:26.767: V/yixi(670):--------->onAttachedToWindow()
08-11 17:02:26.767: V/yixi(670):--------->onWindowVisibilityChanged()
08-11 17:02:26.777: V/yixi(670):--------->onMeasure()
08-11 17:02:26.847: V/yixi(670):--------->onSizeChanged()
08-11 17:02:26.847: V/yixi(670): --------->onLayout()
08-11 17:02:26.857: V/yixi(670):--------->onWindowFocusChanged()
android:visibility=visible
08-11 17:00:17.657: V/yixi(32491):--------->construct(),input params=2
08-11 17:00:17.657: V/yixi(32491):--------->onFinishInflate()
08-11 17:00:17.687: V/yixi(32491):--------->onAttachedToWindow()
08-11 17:00:17.687: V/yixi(32491):--------->onWindowVisibilityChanged()
08-11 17:00:17.687: V/yixi(32491):--------->onMeasure()
08-11 17:00:17.727: V/yixi(32491):--------->onSizeChanged()
08-11 17:00:17.727: V/yixi(32491):--------->onLayout()
08-11 17:00:17.737: V/yixi(32491):--------->onWindowFocusChanged()
08-11 17:00:17.757: V/yixi(32491):--------->onDraw()
接下来我们看销毁 View 的日志:
android:visibility=visible
08-11 17:01:37.377: V/yixi(32491):--------->onWindowFocusChanged()
08-11 17:01:37.517: V/yixi(32491):--------->onWindowVisibilityChanged()
08-11 17:01:38.047: V/yixi(32491):--------->onDetachedFromWindow()
android:visibility=gone
08-11 17:04:52.017: V/yixi(1490):--------->onWindowFocusChanged()
08-11 17:04:52.207: V/yixi(1490):--------->onWindowVisibilityChanged()
08-11 17:04:52.607: V/yixi(1490):--------->onDetachedFromWindow()
android:visibility=invisible
08-11 17:03:44.977: V/yixi(670):--------->onWindowFocusChanged()
08-11 17:03:45.167: V/yixi(670):--------->onWindowVisibilityChanged()
08-11 17:03:45.587: V/yixi(670):--------->onDetachedFromWindow()
结论
1)从中不难看到view 默认为可见的 , 不是默认值时先调用onVisibilityChanged ,但是此时该view 的任何位置信息都不知道。
2)可见性改变后才是调用带有两个参数的构造函数
3)从xml 文件中 inflate 完成
4)将view 加到 window 中 ( View 是gone 的 ,那么View创建生命周期也就结束 )
5)测量view的长宽 ( onMeasure )
6)定位View 在父View中的位置 ( onLayout )-->(View 是invisible , View 创建生命周期结束)
7)onDraw ( 只有可见的 View 才在 window 中绘制 )
综上所述:
View的关键生命周期为 [改变可见性] --> 构造View -->onFinishInflate --> onAttachedToWindow --> onMeasure --> onSizeChanged--> onLayout --> onDraw --> onDetackedFromWindow
2.移动View方法
布局activity_main.xml
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.waterview.MoveView
android:id="@+id/move"
android:visibility="gone"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="#ffffff00" />
</RelativeLayout>
public class MainActivity extends Activity{
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
1)layout()
如果你将滑动后的目标位置的坐标传递给layout(),这样子就会把view的位置给重新布置了一下,在视觉上就是view的一个滑动的效果。
public class MoveView extends View {
privateint lastX;
privateint lastY;
publicMoveView(Context context){
super(context);
}
publicMoveView(Context context, AttributeSet attrs){
super(context,attrs);
}
publicboolean onTouchEvent(MotionEvent event) {
//获取到手指处的横坐标和纵坐标
intx = (int) event.getX();
inty = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX= x;
lastY= y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动的距离
intoffX = x - lastX;
intoffY = y - lastY;
//调用layout方法来重新放置它的位置
layout(getLeft()+offX,getTop()+offY,
getRight()+offX , getBottom()+offY);
break;
}
returntrue;
}
}
2)offsetLeftAndRight(),offsetTopAndBottom()
其实这两个方法分别是对左右移动和上下移动的封装,传入的就是偏移量。
public boolean onTouchEvent(MotionEventevent) {
//获取到手指处的横坐标和纵坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offX = x - lastX;
int offY = y - lastY;
offsetLeftAndRight(offX);
offsetTopAndBottom(offY);
break;
}
return true;
}
3)LayoutParams
public boolean onTouchEvent(MotionEventevent) {
//获取到手指处的横坐标和纵坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offX = x - lastX;
int offY = y - lastY;
ViewGroup.MarginLayoutParams mlp =
(MarginLayoutParams) getLayoutParams();
mlp.leftMargin = getLeft()+offX;
mlp.topMargin = getTop()+offY;
setLayoutParams(mlp);
break;
}
return true;
}
4)scrollTo()scrollBy()
sceollTo(x,y)传入的应该是移动的终点坐标
scrollBy(dx,dy)传入的是移动的增量。
通过scrollBy传入的值应该是你需要的那个增量的相反数!
public boolean onTouchEvent(MotionEventevent) {
//获取到手指处的横坐标和纵坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offX = x - lastX;
int offY = y - lastY;
((View) getParent()).scrollBy(-offX,- offY);
break;
}
return true;
}
5)Scroller
步骤一:
初始化Scroller对象,即mScroller = new Scroller(context)
步骤二:
重写computeScroll()方法,实现模拟滑动。可以复制下面的末模板代码:
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
}
invalidate();//必须要调用
}
步骤三:
开启模拟过程,在合适的地方(一般都在move中)startScroll方法。它有两个重载方法如下:
startScroll(int startX,int startY, intdx,int dy,int duration)
startScroll(int startX,int startY,intdx,int dy)
需要说明的是:
1.computeScrollOffset方法用来判断是否完成了整个滑动,返回为true,则说明没有完成,否则则完成滑动。
2.getCurrY()以及getCurrX()获得的是当前的滑动坐标。
3.最后必须要用invalidate方法来刷新。因为computeScroll方法不会自动调用,是在draw方法中被调用的。所以必须使用invalidate刷新,就会调用draw方法,自然就会调用computeScroll方法了。这样子就会实现循环调用。
4.在startScroll中,偏移量跟使用scrollBy方法中的偏移量用法是一样的,即也必须填写你实际想要移动距离的相反数。也就是你实际想让它偏移一个正值,这里就填写它相应的负值,如果想偏移一个负值,这里就填写相应的正值!
public class MoveView extends View{
private int lastX;
private int lastY;
private Scroller mScroller;
public MoveView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public boolean onTouchEvent(MotionEvent event) {
//获取到手指处的横坐标和纵坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offX = x - lastX;
int offY = y - lastY;
View viewGroup = (View) getParent();
((View) getParent()).scrollBy(-offX,- offY);
break;
case MotionEvent.ACTION_UP:
View viewGroup = (View) getParent();
//开启滑动,让其回到原点
mScroller.startScroll(viewGroup.getScrollX(),
viewGroup.getScrollY(),
-viewGroup.getScrollX() ,-viewGroup.getScrollY());
break;
}
return true;
}
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()) {
((View)getParent()).scrollTo(mScroller.getCurrX(),
mScroller.getCurrY());
}
invalidate();//必须要调用
}
}