一、layou方法
代碼示例如下宫患,自定義一個view,在onTouchEvent()方法中計算手指滑動時的偏移量哩掺,調(diào)用view的layout()方法检眯,在當(dāng)前l(fā)eft厘擂、top、right锰瘸、bottom上加上偏移量刽严,實(shí)現(xiàn)view的滑動。
public class TestView extends View {
private static final String TAG = "TestView";
public TestView(Context context) {
super(context);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//記錄初始觸摸點(diǎn)坐標(biāo)
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//手指滑動過程中經(jīng)過的點(diǎn)坐標(biāo)
float lastX = event.getRawX();
float lastY = event.getRawY();
//計算偏移量
int offsetX = (int) (lastX - startX);
int offsetY = (int) (lastY - startY);
//在當(dāng)前的left获茬、top港庄、right、bottom上加上偏移量
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
Log.e(TAG, "onTouchEvent: layout: " + offsetX + "/" + offsetY);
//重新設(shè)置初始坐標(biāo) 恕曲,這一步很重要
startX = lastX;
startY = lastY;
break;
}
return true;
}
}
二鹏氧、offsetLeftAndRight()與offsetTopAndBottom()方法
這兩個方法相當(dāng)于系統(tǒng)提供的一個對左右、上下移動的API的封裝佩谣。使用方法同樣先在onTouchEvent()方法中計算出手指滑動的偏移量把还,然后將偏移量作為方法參數(shù)傳入。關(guān)鍵代碼如下:
float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//記錄初始觸摸點(diǎn)坐標(biāo)
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//手指滑動過程中經(jīng)過的點(diǎn)坐標(biāo)
float lastX = event.getRawX();
float lastY = event.getRawY();
//計算偏移量
int offsetX = (int) (lastX - startX);
int offsetY = (int) (lastY - startY);
//調(diào)用方法,參數(shù)為計算出的偏移量
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
Log.e(TAG, "onTouchEvent: offsetLeftAndRight_offsetTopAndBottom: " + offsetX + "/" + offsetY);
//重新設(shè)置初始坐標(biāo) 吊履,這一步很重要
startX = lastX;
startY = lastY;
break;
}
return true;
}
三安皱、修改view的LayoutParams
LayoutParams保存著一個View的布局參數(shù)⊥а祝可以通過改變LayoutParams來動態(tài)修改一個View的位置參數(shù)酌伊,以此達(dá)到改變View位置的效果。關(guān)鍵代碼如下:
float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//記錄初始觸摸點(diǎn)坐標(biāo)
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//手指滑動過程中經(jīng)過的點(diǎn)坐標(biāo)
float lastX = event.getRawX();
float lastY = event.getRawY();
//計算偏移量
int offsetX = (int) (lastX - startX);
int offsetY = (int) (lastY - startY);
//修改LayoutParams參數(shù)缀踪,這里要改變位置主要是修改LayoutParams的margin屬性居砖,因此這里直接使用
// ViewGroup.MarginLayoutParams以求方便,不必考慮view的父布局類型驴娃。在一般應(yīng)用中奏候,如若有需要
// 修改LayoutParams的其他屬性,要注意getLayoutParams()必須獲取的是父布局類型的LayoutParams唇敞。
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
Log.e(TAG, "onTouchEvent: layoutParams: " + offsetX + "/" + offsetY);
//重新設(shè)置初始坐標(biāo)蔗草,這一步很重要
startX = lastX;
startY = lastY;
break;
}
return true;
}
四、scrollTo與scrollBy
1.scrollTo(int x,int y):移動到一個坐標(biāo)點(diǎn)
2.scrollBy(int x,int y):按照x軸增量x疆柔,y軸增量y的方式移動
這兩個方法實(shí)質(zhì)上移動的是調(diào)用方法的對象所包含的內(nèi)容咒精。即:如果對一個ViewGroup(如LinearLayout)調(diào)用這兩個方法,會發(fā)現(xiàn)其內(nèi)部所有的子view會一起發(fā)生移動婆硬;而如果對一個view(如TextView)調(diào)用狠轻,則會發(fā)現(xiàn)看不到移動的效果。要對view使用這兩個方法彬犯,實(shí)際上是需要對該view 的parent使用向楼。代碼示例如下:
1.在ViewGroup中,可以直接調(diào)用
float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float lastX = event.getRawX();
float lastY = event.getRawY();
int offsetX = (int) (lastX - startX);
int offsetY = (int) (lastY - startY);
//注意這里的參數(shù)要變成相反數(shù)谐区。由于作用對象是parent湖蜕,所以實(shí)際發(fā)生
//移動的應(yīng)該是parent,但我們的目標(biāo)是子view宋列。因此昭抒,以相對視圖關(guān)系
//來看,要讓子view產(chǎn)生所期望的移動效果炼杖,parent應(yīng)該要向反方向移動灭返。
scrollBy(-offsetX, -offsetY);
startX = lastX;
startY = lastY;
break;
}
return true;
}
2.在view中,需要由view的parent來調(diào)用
((View) getParent()).scrollBy(-offsetX, -offsetY);
五坤邪、使用Scroller實(shí)現(xiàn)帶過度動畫的滑動
使用前面幾種方法實(shí)現(xiàn)view位置的改變熙含,如果不是通過手指觸摸事件,而是通過如按鈕點(diǎn)擊事件調(diào)用艇纺,view的位置變化是瞬間完成的怎静。如果要讓位置變化有一個過渡動畫的過程邮弹,可以使用Scroller來實(shí)現(xiàn)。Scroller的使用可以分為3步驟:
1.初始化Scroller: Scroller scroller=new Scroller(context);
2.重寫computeScroll()方法蚓聘,實(shí)現(xiàn)模擬滑動
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}
3.startScroll()方法開啟模擬過程腌乡。可以在自定義view內(nèi)部合適的位置調(diào)用下列方法夜牡,也可以在自定義view內(nèi)部聲明一個調(diào)用該方法的方法与纽,然后在外部Activity等需要調(diào)用的位置調(diào)用。
scroller.startScroll(startX, startY, dx, dy, duration);
代碼示例:
自定義View
public class TestView extends View {
private static final String TAG = "TestView";
private Scroller mScroller;
public TestView(Context context) {
super(context);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
public void scrollAnimation(int startX, int startY, int dx, int dy, int duration) {
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();
}
}
在Activity中調(diào)用:
@Override
public void onClick(View v) {
mTestView.scrollAnimation(0,0,-300,-400,2000);
}