相關(guān)文章:
Android View體系(一)視圖坐標(biāo)系
1.View的滑動(dòng)簡(jiǎn)介
View的滑動(dòng)是Android實(shí)現(xiàn)自定義控件的基礎(chǔ)糊闽,同時(shí)在開發(fā)中我們也難免會(huì)遇到View的滑動(dòng)的處理。其實(shí)不管是那種滑動(dòng)的方式基本思想都是類似的:當(dāng)觸摸事件傳到View時(shí)谍珊,系統(tǒng)記下觸摸點(diǎn)的坐標(biāo)治宣,手指移動(dòng)時(shí)系統(tǒng)記下移動(dòng)后的觸摸的坐標(biāo)并算出偏移量,并通過偏移量來修改View的坐標(biāo)。
實(shí)現(xiàn)View滑動(dòng)有很多種方法侮邀,這篇文章主要講解六種滑動(dòng)的方法坏怪,分別是:layout()、offsetLeftAndRight()與offsetTopAndBottom()绊茧、LayoutParams铝宵、動(dòng)畫、scollTo與scollBy和Scroller华畏;在下一篇文章我們會(huì)詳細(xì)介紹屬性動(dòng)畫鹏秋。
2.實(shí)現(xiàn)View滑動(dòng)的六種方法
layout()
view進(jìn)行繪制的時(shí)候會(huì)調(diào)用onLayout()方法來設(shè)置顯示的位置,因此我們同樣也可以通過修改View的left亡笑、top侣夷、right、bottom這四種屬性來控制View的坐標(biāo)况芒。首先我們要自定義一個(gè)View惜纸,在onTouchEvent()方法中獲取觸摸點(diǎn)的坐標(biāo):
public boolean onTouchEvent(MotionEvent event) {
//獲取到手指處的橫坐標(biāo)和縱坐標(biāo)
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
...
接下來我們?cè)贏CTION_MOVE事件中計(jì)算偏移量,再調(diào)用layout()方法重新放置這個(gè)自定義View的位置就好了:
case MotionEvent.ACTION_MOVE:
//計(jì)算移動(dòng)的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//調(diào)用layout方法來重新放置它的位置
layout(getLeft()+offsetX, getTop()+offsetY,
getRight()+offsetX , getBottom()+offsetY);
break;
當(dāng)我們每次移動(dòng)時(shí)都會(huì)調(diào)用layout()方法來對(duì)自己重新布局绝骚,從而達(dá)到移動(dòng)View的效果耐版。
自定義View的全部代碼(CustomView.java):
package com.example.liuwangshu.moonviewslide;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
private int lastX;
private int lastY;
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context) {
super(context);
}
public boolean onTouchEvent(MotionEvent event) {
//獲取到手指處的橫坐標(biāo)和縱坐標(biāo)
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:
//計(jì)算移動(dòng)的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//調(diào)用layout方法來重新放置它的位置
layout(getLeft()+offsetX, getTop()+offsetY,
getRight()+offsetX , getBottom()+offsetY);
break;
}
return true;
}
}
布局中引用自定義View:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.liuwangshu.moonviewslide.CustomView
android:id="@+id/customview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="50dp"
android:background="@android:color/holo_red_light" />
</LinearLayout>
offsetLeftAndRight()與offsetTopAndBottom()
這兩種方法和layout()方法效果方法差不多,使用也差不多压汪,我們將ACTION_MOVE中的代碼替換成如下代碼:
case MotionEvent.ACTION_MOVE:
//計(jì)算移動(dòng)的距離
int offsetX = x - lastX;
int offsetY = y - lastY;
//對(duì)left和right進(jìn)行偏移
offsetLeftAndRight(offsetX);
//對(duì)top和bottom進(jìn)行偏移
offsetTopAndBottom(offsetY);
break;
LayoutParams(改變布局參數(shù))
LayoutParams主要保存了一個(gè)View的布局參數(shù)粪牲,因此我們可以通過LayoutParams來改變View的布局的參數(shù)從而達(dá)到了改變View的位置的效果。同樣的我們將ACTION_MOVE中的代碼替換成如下代碼:
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
因?yàn)楦缚丶荓inearLayout止剖,所以我們用了LinearLayout.LayoutParams腺阳,如果父控件是RelativeLayout則要使用RelativeLayout.LayoutParams。除了使用布局的LayoutParams外穿香,我們還可以用ViewGroup.MarginLayoutParams來實(shí)現(xiàn):
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
動(dòng)畫
可以采用View動(dòng)畫來移動(dòng)亭引,在res目錄新建anim文件夾并創(chuàng)建translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="300" android:duration="1000"/>
</set>
在Java代碼中引用:
mCustomView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));
當(dāng)然使用屬性動(dòng)畫移動(dòng)那就更簡(jiǎn)單了,我們讓CustomView在1000毫秒內(nèi)沿著X軸像右平移300像素:
ObjectAnimator.ofFloat(mCustomView,"translationX",0,300).setDuration(1000).start();
scollTo與scollBy
scollTo(x,y)表示移動(dòng)到一個(gè)具體的坐標(biāo)點(diǎn)皮获,而scollBy(dx,dy)則表示移動(dòng)的增量為dx焙蚓、dy。其中scollBy最終也是要調(diào)用scollTo的洒宝。scollTo购公、scollBy移動(dòng)的是View的內(nèi)容,如果在ViewGroup中使用則是移動(dòng)他所有的子View雁歌。我們將ACTION_MOVE中的代碼替換成如下代碼:
((View)getParent()).scrollBy(-offsetX,-offsetY);
這里要實(shí)現(xiàn)CustomView隨著我們手指移動(dòng)的效果的話宏浩,我們就需要將偏移量設(shè)置為負(fù)值。
Scroller
我們用scollTo/scollBy方法來進(jìn)行滑動(dòng)時(shí)靠瞎,這個(gè)過程是瞬間完成的比庄,所以用戶體驗(yàn)不大好求妹。這里我們可以使用Scroller來實(shí)現(xiàn)有過度效果的滑動(dòng),這個(gè)過程不是瞬間完成的印蔗,而是在一定的時(shí)間間隔完成的扒最。Scroller本身是不能實(shí)現(xiàn)View的滑動(dòng)的,它需要配合View的computeScroll()方法才能彈性滑動(dòng)的效果华嘹。
在這里我們實(shí)現(xiàn)CustomView平滑的向右移動(dòng)吧趣。
- 首先我們要初始化Scroller:
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
- 接下來重寫computeScroll()方法,系統(tǒng)會(huì)在繪制View的時(shí)候在draw()方法中調(diào)用該方法,這個(gè)方法中我們調(diào)用父類的scrollTo()方法并通過Scroller來不斷獲取當(dāng)前的滾動(dòng)值,每滑動(dòng)一小段距離我們就調(diào)用invalidate()方法不斷的進(jìn)行重繪瓢颅,重繪就會(huì)調(diào)用computeScroll()方法劝贸,這樣我們就通過不斷的移動(dòng)一個(gè)小的距離并連貫起來就實(shí)現(xiàn)了平滑移動(dòng)的效果:
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
//通過不斷的重繪不斷的調(diào)用computeScroll方法
invalidate();
}
}
- 調(diào)用Scroller.startScroll()方法。我們?cè)贑ustomView中寫一個(gè)smoothScrollTo()方法,調(diào)用Scroller.startScroll()方法,在2000毫秒內(nèi)沿X軸平移delta像素:
public void smoothScrollTo(int destX,int destY){
int scrollX=getScrollX();
int delta=destX-scrollX;
//1000秒內(nèi)滑向destX
mScroller.startScroll(scrollX,0,delta,0,2000);
invalidate();
}
- 最后我們?cè)赩iewSlideActivity.java中調(diào)用CustomView的smoothScrollTo()方法:
//使用Scroll來進(jìn)行平滑移動(dòng)
mCustomView.smoothScrollTo(-400,0);
這里我們是設(shè)定CustomView沿著X軸向右平移400像素。