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