編輯:關於Android編程
下面通過一個例子來總結實現滑動的幾種方式,例子的主要功能就是讓我們的自定義View能夠隨著手指的移動而移動。
布局文件如下:
在View進行繪制時,會調用onLayout()方法來設置顯示的位置,因此,我們可以通過修改View的left、top、right、bottom四個屬性來控制View的坐標。要控制View隨手指滑動,因此需要在onTouchEvent()事件中進行滑動控制。代碼如下:
public class DragView extends View{
private int mLastX;
private int mLastY;
public DragView(Context context) {
super(context);
init();
}
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
setBackgroundColor(Color.BLUE);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//調整layout的四個坐標
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
break;
}
return true;
}
}
這兩個方法其實是對上面那種layout設置方式的封裝、簡化,在layout中,左left、右right兩個方向都是加上offsetX,上top、下bottom兩個方向都是加上offsetY,為了簡化設置四個方向,Android提供了offsetLeftAndRight()來代替左右方向的設置,用offsetTopAndBottom()來代替上下方向的設置。
我們只需要修改上面代碼ACTION_MOVE的部分,如下:
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//調整layout的四個坐標
//layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
//使用簡寫形式
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
break;
LayoutParams保存了一個View的布局參數,因此我們可以通過動態改變LayoutParams中的布局參數來達到改變View的位置效果。通過getLayoutParams()方法來獲取View的LayoutParams,這裡獲取到的LayoutParams需要根據View所在父布局的類型來設置不同的類型,比如,我們這個自定義View是放在LinearLayout中的,那麼通過getLayoutParams()獲取到的就是LinearLayout.LayoutParams。因此,通過getLayoutParams()獲取到LayoutParams的前提就是這個View需要有一個父布局。
同樣,我們只需要修改上面代碼ACTION_MOVE的部分,如下:
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
lp.leftMargin = getLeft() + offsetX;
lp.topMargin = getTop() + offsetY;
setLayoutParams(lp);
break;
可以看到,通過LayoutParams改變一個View的位置時,改變的是這個View的Margin屬性,這也是為什麼這種方式一定要有父布局的原因,只有有了父布局,margin屬性的設置才會起作用。
對於使用LayoutParams這種方式改變View位置,如果我們不想考慮父布局的類型,還可以使用ViewGroup.MarginLayoutParams來進行設置,這樣也更加方便。如下:
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
lp.leftMargin = getLeft() + offsetX;
lp.topMargin = getTop() + offsetY;
setLayoutParams(lp);
break;
效果是一樣的。
關於scrollTo()和scrollBy()方法,這篇文章《Scroller大揭秘》中有詳細介紹。
使用scrollTo()和scrollBy()方法需要注意的一點是,scrollTo()和scrollBy()方法移動的是View的content,即讓View的內容移動,如果在ViewGroup中使用scrollTo()和scrollBy()方法,那麼移動的將是所有的子View,如果在View中使用,那麼移動的將是View的內容。例如,TextView,content就是它的文本,ImageView,content就是它的drawable對象。
因此,上面例子中我們如果直接這樣使用:
scrollBy(offsetX,offsetY);
發現View並沒有移動,但其實是發生了移動的,只不過此時移動的是View中的內容,而我們例子中的content什麼也沒有。
所以,我們要想使這個View發生移動,我們就應該在View所在的ViewGroup中使用scrollBy或scrollTo方法來進行移動。同時,使用者兩個方法進行移動的時候,注意此時的坐標方向與平常是相反的,具體在《Scroller大揭秘》有講解。代碼如下:
case MotionEvent.ACTION_MOVE:
//int offsetX = x - mLastX;
//int offsetY = y - mLastY;
//此時,計算坐標是相反的
int offsetX = mLastX - x;
int offsetY = mLastY - y;
//讓View所在的ViewGroup進行移動
((View)getParent()).scrollBy(offsetX,offsetY);
break;
通過Scroller這個輔助類,配合scrollTo和scrollBy可以實現一些更加高級的滑動效果,關於Scroller類的具體介紹,同樣在這篇文章中有詳解《Scroller大揭秘》。
這裡,我們只是結合上面這個例子實現一個簡單的功能,當我們滑動完畢抬起手指後,View自動回彈到原來的位置。代碼如下:
public class DragView extends View{
private int mLastX;
private int mLastY;
private Scroller mScroller;
public DragView(Context context) {
super(context);
init(context);
}
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
setBackgroundColor(Color.BLUE);
mScroller = new Scroller(context);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
//int offsetX = x - mLastX;
//int offsetY = y - mLastY;
//此時,計算坐標是相反的
int offsetX = mLastX - x;
int offsetY = mLastY - y;
//讓View所在的ViewGroup進行移動
((View)getParent()).scrollBy(offsetX,offsetY);
break;
case MotionEvent.ACTION_UP:
View viewGroup = (View) getParent();
mScroller.startScroll(viewGroup.getScrollX(),viewGroup.getScrollY(),-viewGroup.getScrollX(),-viewGroup.getScrollY());
//記住需要invalidate
invalidate();
break;
}
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
//記住,需要不斷調用invalidate進行重繪
invalidate();
}
}
}
以上五種方法就是常用的滑動View的方法。還有兩種方式能夠控制一個View的移動效果:屬性動畫和使用ViewDragHelper,對於這兩種方法,大家可以查閱網上資料,就不詳細介紹了。
Android模擬登錄評論CSDN實現代碼
有時候作為非官方開發的APP集成了官方的所有信息,但是現在需要實現另一個功能那就是登錄發表評論到官方的網站,而非官方的APP並不知道官方網站是怎麼實現登錄與評論的,而且越
QtAndroid詳解(4):JNI調用Android系統功能(1)
前面幾篇我們講解了 QtAndroid 名字空間的基本用法,這次我們使用前面講過的方法和類庫,展示一些簡單的小示例。我在《Qt on Android核心編程》一書中主要通
Android開發之ListView利用OnScrollListener實現分頁加載數據
上篇博文和大家分享了下拉刷新,這是一個用戶體驗非常好的操作方式。新浪微薄就是使用這種方式的典型。 還有個問題,當用戶從網絡上讀取微薄的時候,如果一下子全部加載用戶未讀
XML 文件解析總結
一.基礎概念的介紹? ??XML在各種開發中都廣泛應用,Android也不例外。作為承載數據的一個重要角色,如何讀寫XML成為Android開發中一項重要的技能。今天就由