Android自定义View生命周期及其移动方法

 

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();//必须要调用

  }

}

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章