今天來寫個可拖動的按鈕吧
在學習Android的過程中,一個按鈕、一個文字示圖可以說是基本到不能再基本的東西了
不過在某些APP中,有些按鈕或圖片等等都是可以拖曳的(-‿◦)
..阿咧?不太懂我在說什麼嗎?好吧,直接看範例
今天要做的大概就是這麼一個東西~
大概理解了嗎?那麼我們開始吧:D
喔對了!
這次沒有Github喔!需要程式碼的在底下留Mail我會給你
1. 原理
首先整體來說,原理就是跟寫一個自定義元件是一樣的道理
可以參照一下我之前寫的
->碼農日常-『Android studio』簡單地教你實現在Android畫一個弧形儀表盤
但是這次的沒有那麼複雜,這次我們要直接搬Android之中的原生按鈕來做魔改
那麼,先來看一下這次的檔案內容
而因為這次相當於要寫一個新的自定義View, 所以介面就娜到最後囉
2. 程式內容
直接看一下程式內容
public class DraggableButton extends androidx.appcompat.widget.AppCompatButton { float lastX = 0f, lastY = 0f; private float beginX = 0f, beginY = 0f; int screenWidth = 720, screenHeight = 1280; public DraggableButton(@NonNull Context context) { super(context); } public DraggableButton(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public DraggableButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); assert wm != null; wm.getDefaultDisplay().getMetrics(dm); /**取得螢幕的總寬跟總高*/ screenHeight = dm.heightPixels-200; screenWidth = dm.widthPixels; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: /**當按鈕被按下*/ //取得目前位置 lastX = event.getRawX(); lastY = event.getRawY(); //將位置寫為起始位置 beginX = lastX; beginY = lastY; break; case MotionEvent.ACTION_MOVE: //取得觸摸點相對於螢幕的座標 float dx = event.getRawX() - lastX; float dy = event.getRawY() - lastY; float left, top, right, bottom; left = getLeft() + dx; top = getTop() + dy; right = getRight() + dx; bottom = getBottom() + dy; //以下判斷為避免物件被拉出畫面 if(left < 0){ left = 0; right = left + getWidth(); } if(right > screenWidth){ right = screenWidth; left = right - getWidth(); } if(top < 0){ top = 0; bottom = top + getHeight(); } if(bottom>screenHeight){ bottom = screenHeight; top = bottom - getHeight(); } //設置被拉到的位置 layout(Math.round(left),Math.round(top),Math.round(right),Math.round(bottom)); lastY = event.getRawY(); lastX = event.getRawX(); break; case MotionEvent.ACTION_UP: //如果移動距離小於10,則被視為點擊按鈕;反之則為拖曳按鈕 if (Math.abs(lastX - beginX)< 10 && Math.abs(lastY - beginY)< 10){ return super.onTouchEvent(event); }else{ setPressed(false); return true; } } return super.onTouchEvent(event); } }
首先,開頭的那個public一定記得要加
不然後面東西跑不出來不要怪我(´・д・`)
重點稍微講一下
首先,我們必須先知道螢幕整體大小
而該程式就寫在onMeasure內
@SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); assert wm != null; wm.getDefaultDisplay().getMetrics(dm); /**取得螢幕的總寬跟總高*/ screenHeight = dm.heightPixels-200; screenWidth = dm.widthPixels; }
再來,請要讓這個View可以被拖動
而相關程式就是onTouchEvent的部分
首先,請讓程式複寫onTouchEvent
然後,讓他攔截
按下(MotionEvent.ACTION_DOWN)
拖曳中(MotionEvent.ACTION_MOVE)
放開(MotionEvent.ACTION_UP)
三個事件
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: } return super.onTouchEvent(event); }
然後,在MotionEvent.ACTION_DOWN寫入
case MotionEvent.ACTION_DOWN: /**當按鈕被按下*/ //取得目前位置 lastX = event.getRawX(); lastY = event.getRawY(); //將位置寫為起始位置 beginX = lastX; beginY = lastY; break;
在MotionEvent.ACTION_MOVE寫入
case MotionEvent.ACTION_MOVE: //取得觸摸點相對於螢幕的座標 float dx = event.getRawX() - lastX; float dy = event.getRawY() - lastY; float left, top, right, bottom; left = getLeft() + dx; top = getTop() + dy; right = getRight() + dx; bottom = getBottom() + dy; //以下判斷為避免物件被拉出畫面 if(left < 0){ left = 0; right = left + getWidth(); } if(right > screenWidth){ right = screenWidth; left = right - getWidth(); } if(top < 0){ top = 0; bottom = top + getHeight(); } if(bottom>screenHeight){ bottom = screenHeight; top = bottom - getHeight(); } //設置被拉到的位置 layout(Math.round(left),Math.round(top),Math.round(right),Math.round(bottom)); lastY = event.getRawY(); lastX = event.getRawX(); break;
在MotionEvent.ACTION_UP寫入
case MotionEvent.ACTION_UP: //如果移動距離小於10,則被視為點擊按鈕;反之則為拖曳按鈕 if (Math.abs(lastX - beginX)< 10 && Math.abs(lastY - beginY)< 10){ return super.onTouchEvent(event); }else{ setPressed(false); return true; }
基本上,重點是在最後的那個放開事件
因為拖曳本質上也是先要按下按鈕
所以在放開的部分,便是先偵測使用者究竟是拖曳還是點擊,如果是點擊的話則設定按鈕"被按下",
反之則不回傳任何事件
3. 介面
最後就是介面了
界面超級簡單,基本上跟放一個Button完全一樣
BTW,由於本元件繼承Button撰寫,因此所有的功能都跟Button一樣喔~
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.noahliu.draggablebuttondemo.DraggableButton android:id="@+id/button_Hello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
然後使用之
MainActivity.java
public class MainActivity extends AppCompatActivity { public static final String TAG = MainActivity.class.getSimpleName()+"My"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DraggableButton btHello = findViewById(R.id.button_Hello); btHello.setOnClickListener(v->{ Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show(); }); } }
其實寫完後,發現這也不是多了不起的功能麻(゜ロ゜)
是的,其實我之前學寫這個的時候想說很難,寫了一大堆東西
後來才想到..靠,既然是按鈕不就直接繼承按鈕就好??
所以其實當時也是走了不少冤枉路的
那希望有緣看到這篇文的你~在寫程式的路上可以少點冤枉路囉:D
最後..
留言列表