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();//必須要調用

  }

}

 

 

 

 

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