Android實(shí)現(xiàn)控件的縮放移動(dòng)功能
上篇文章給大家介紹了 Android控件實(shí)現(xiàn)圖片縮放功能 ,需要的朋友點(diǎn)擊查看。
1.簡(jiǎn)介
話不多說(shuō)先來(lái)張效果圖
控件縮放移動(dòng).gif
上面的gif中,依次進(jìn)行了拖動(dòng)——>觸摸右上角放大,縮小——>觸摸上方與右測(cè)邊緣——>雙指放大縮小。
2 使用步驟
2.1 布局。外層一個(gè)LinearLayout,里面一個(gè)自定義的控件DragScaleView,為了能夠更清楚的看到控件的變化過(guò)程,就給控件加了一個(gè)灰色帶虛線的邊框bg_dashgap。
layout文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#80ce3d3a" android:gravity="center_horizontal" android:fitsSystemWindows="true"> <com.xxx.xxx.ui.DragScaleView android:id="@+id/hair_dv" android:src="@drawable/ic_sure" android:background="@drawable/bg_dashgap" android:adjustViewBounds="true" android:layout_marginLeft="50dp" android:layout_marginTop="10dp" android:layout_width="100dp" android:layout_height="120dp" android:clickable="true"/> </LinearLayout>
在drawable文件夾下的bg_dashgap.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <!-- 圓角 --> <corners android:bottomLeftRadius="8dp" android:bottomRightRadius="8dp" android:radius="15dp" android:topLeftRadius="8dp" android:topRightRadius="8dp" /> <!-- 描邊 --> <stroke android:dashGap="4dp" android:dashWidth="4dp" android:width="2dp" android:color="@color/my_gery" /> </shape>
2.2 自定義的控件
單指觸摸:
當(dāng)ACTION_DOWN時(shí)如果坐標(biāo)為1.2.3.4四個(gè)區(qū)域,則對(duì)View進(jìn)行相應(yīng)的左上/右上/左下/右下拉伸;
當(dāng)ACTION_DOWN時(shí)如果坐標(biāo)為5.6.7.8四個(gè)區(qū)域,則分別對(duì)上/右/下/左四個(gè)方向進(jìn)行拉伸;
當(dāng)ACTION_DOWN時(shí)如果坐標(biāo)為9這個(gè)區(qū)域,則對(duì)View進(jìn)行移動(dòng);
雙指觸摸:
先計(jì)算出觸摸時(shí)雙指的距離,float oriDist=distance(event);
再得到雙指離開屏幕的距離,float newDist =distance(event);
得到兩者之間的比例 float scale = newDist / oriDist;
計(jì)算雙指間距離的方法
/** * 計(jì)算兩個(gè)手指間的距離 * @param event 觸摸事件 * @return 放回兩個(gè)手指之間的距離 */ private float distance(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y);//兩點(diǎn)間距離公式 }
自定義的控件
onTouch(View v, MotionEvent event)的觸摸事件中代碼塊
image.png
其他的關(guān)鍵地方,代碼中都有比較詳細(xì)的注釋了。思路就是
觸摸監(jiān)聽
判斷不同情況---getDirection(v, (int) event.getX(), (int) event.getY())
計(jì)算得到新的oriLeft, oriTop, oriRight, oriBottom
重新繪制---v.layout(oriLeft, oriTop, oriRight, oriBottom)
3 DragScaleView 的完整代碼
public class DragScaleView extends android.support.v7.widget.AppCompatImageView implements View.OnTouchListener { protected int screenWidth; protected int screenHeight; protected int lastX; protected int lastY; private int oriLeft; private int oriRight; private int oriTop; private int oriBottom; private int dragDirection; private static final int TOP = 0x15; private static final int LEFT = 0x16; private static final int BOTTOM = 0x17; private static final int RIGHT = 0x18; private static final int LEFT_TOP = 0x11; private static final int RIGHT_TOP = 0x12; private static final int LEFT_BOTTOM = 0x13; private static final int RIGHT_BOTTOM = 0x14; private static final int TOUCH_TWO = 0x21; private static final int CENTER = 0x19; private int offset = 0; //可超出其父控件的偏移量 protected Paint paint = new Paint(); private static final int touchDistance = 80; //觸摸邊界的有效距離 // 初始的兩個(gè)手指按下的觸摸點(diǎn)的距離 private float oriDis = 1f; /** * 初始化獲取屏幕寬高 */ protected void initScreenW_H() { screenHeight = getResources().getDisplayMetrics().heightPixels - 40; screenWidth = getResources().getDisplayMetrics().widthPixels; } public DragScaleView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setOnTouchListener(this); initScreenW_H(); } public DragScaleView(Context context, AttributeSet attrs) { super(context, attrs); setOnTouchListener(this); initScreenW_H(); } public DragScaleView(Context context) { super(context); setOnTouchListener(this); initScreenW_H(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setColor(Color.GRAY); paint.setStrokeWidth(4.0f); paint.setStyle(Paint.Style.STROKE); } @Override public boolean onTouch(View v, MotionEvent event) { setBackgroundResource(R.drawable.bg_dashgap); int action = event.getAction()& MotionEvent.ACTION_MASK; if (action == MotionEvent.ACTION_DOWN) { oriLeft = v.getLeft(); oriRight = v.getRight(); oriTop = v.getTop(); oriBottom = v.getBottom(); lastY = (int) event.getRawY(); lastX = (int) event.getRawX(); dragDirection = getDirection(v, (int) event.getX(), (int) event.getY()); } if (action == MotionEvent.ACTION_POINTER_DOWN){ oriLeft = v.getLeft(); oriRight = v.getRight(); oriTop = v.getTop(); oriBottom = v.getBottom(); lastY = (int) event.getRawY(); lastX = (int) event.getRawX(); dragDirection = TOUCH_TWO; oriDis = distance(event); } // 處理拖動(dòng)事件 delDrag(v, event, action); invalidate(); return false; } /** * 處理拖動(dòng)事件 * * @param v * @param event * @param action */ protected void delDrag(View v, MotionEvent event, int action) { switch (action) { case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; switch (dragDirection) { case LEFT: // 左邊緣 left(v, dx); break; case RIGHT: // 右邊緣 right(v, dx); break; case BOTTOM: // 下邊緣 bottom(v, dy); break; case TOP: // 上邊緣 top(v, dy); break; case CENTER: // 點(diǎn)擊中心-->>移動(dòng) center(v, dx, dy); break; case LEFT_BOTTOM: // 左下 left(v, dx); bottom(v, dy); break; case LEFT_TOP: // 左上 left(v, dx); top(v, dy); break; case RIGHT_BOTTOM: // 右下 right(v, dx); bottom(v, dy); break; case RIGHT_TOP: // 右上 right(v, dx); top(v, dy); break; case TOUCH_TWO: //雙指操控 float newDist =distance(event); float scale = newDist / oriDis; //控制雙指縮放的敏感度 int distX = (int) (scale*(oriRight-oriLeft)-(oriRight-oriLeft))/50; int distY = (int) (scale*(oriBottom-oriTop)-(oriBottom-oriTop))/50; if (newDist>10f){//當(dāng)雙指的距離大于10時(shí),開始相應(yīng)處理 left(v, -distX); top(v, -distY); right(v, distX); bottom(v, distY); } break; } if (dragDirection != CENTER) { v.layout(oriLeft, oriTop, oriRight, oriBottom); } lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: dragDirection = 0; break; } } /** * 觸摸點(diǎn)為中心->>移動(dòng) * * @param v * @param dx * @param dy */ private void center(View v, int dx, int dy) { int left = v.getLeft() + dx; int top = v.getTop() + dy; int right = v.getRight() + dx; int bottom = v.getBottom() + dy; if (left < -offset) { left = -offset; right = left + v.getWidth(); } if (right > screenWidth + offset) { right = screenWidth + offset; left = right - v.getWidth(); } if (top < -offset) { top = -offset; bottom = top + v.getHeight(); } if (bottom > screenHeight + offset) { bottom = screenHeight + offset; top = bottom - v.getHeight(); } Log.d("raydrag", left+" "+top+" "+right+" "+bottom+" "+dx); v.layout(left, top, right, bottom); } /** * 觸摸點(diǎn)為上邊緣 * * @param v * @param dy */ private void top(View v, int dy) { oriTop += dy; if (oriTop < -offset) { //對(duì)view邊界的處理,如果子view達(dá)到父控件的邊界,offset代表允許超出父控件多少 oriTop = -offset; } if (oriBottom - oriTop - 2 * offset < 200) { oriTop = oriBottom - 2 * offset - 200; } } /** * 觸摸點(diǎn)為下邊緣 * * @param v * @param dy */ private void bottom(View v, int dy) { oriBottom += dy; if (oriBottom > screenHeight + offset) { oriBottom = screenHeight + offset; } if (oriBottom - oriTop - 2 * offset < 200) { oriBottom = 200 + oriTop + 2 * offset; } } /** * 觸摸點(diǎn)為右邊緣 * * @param v * @param dx */ private void right(View v, int dx) { oriRight += dx; if (oriRight > screenWidth + offset) { oriRight = screenWidth + offset; } if (oriRight - oriLeft - 2 * offset < 200) { oriRight = oriLeft + 2 * offset + 200; } } /** * 觸摸點(diǎn)為左邊緣 * * @param v * @param dx */ private void left(View v, int dx) { oriLeft += dx; if (oriLeft < -offset) { oriLeft = -offset; } if (oriRight - oriLeft - 2 * offset < 200) { oriLeft = oriRight - 2 * offset - 200; } } /** * 獲取觸摸點(diǎn)flag * * @param v * @param x * @param y * @return */ protected int getDirection(View v, int x, int y) { int left = v.getLeft(); int right = v.getRight(); int bottom = v.getBottom(); int top = v.getTop(); if (x < touchDistance && y < touchDistance) { return LEFT_TOP; } if (y < touchDistance && right - left - x < touchDistance) { return RIGHT_TOP; } if (x < touchDistance && bottom - top - y < touchDistance) { return LEFT_BOTTOM; } if (right - left - x < touchDistance && bottom - top - y < touchDistance) { return RIGHT_BOTTOM; } if (x < touchDistance) { return LEFT; } if (y < touchDistance) { return TOP; } if (right - left - x < touchDistance) { return RIGHT; } if (bottom - top - y < touchDistance) { return BOTTOM; } return CENTER; } /** * 計(jì)算兩個(gè)手指間的距離 * * @param event 觸摸事件 * @return 放回兩個(gè)手指之間的距離 */ private float distance(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y);//兩點(diǎn)間距離公式 } }
總結(jié)
以上所述是小編給大家介紹的Android控件的縮放移動(dòng)功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android通過(guò)ImageView設(shè)置手指滑動(dòng)控件縮放
- 學(xué)習(xí)使用Material Design控件(四)Android實(shí)現(xiàn)標(biāo)題欄自動(dòng)縮放、放大效果
- Android通過(guò)自定義ImageView控件實(shí)現(xiàn)圖片的縮放和拖動(dòng)的實(shí)現(xiàn)代碼
- Android控件系列之相冊(cè)Gallery&Adapter適配器入門&控件縮放動(dòng)畫入門
- Android基于widget組件實(shí)現(xiàn)物體移動(dòng)/控件拖動(dòng)功能示例
- Android自定義控件實(shí)現(xiàn)隨手指移動(dòng)的小球
相關(guān)文章
利用smsmanager實(shí)現(xiàn)后臺(tái)發(fā)送短信示例
這篇文章主要介紹了android利用SmsManager可以實(shí)現(xiàn)后臺(tái)發(fā)送短信的方法,最近有使用說(shuō)明,大家可以參考使用2014-01-01Android 中使用RadioGroup和Fragment實(shí)現(xiàn)底部導(dǎo)航欄的功能
這篇文章主要介紹了Android 中使用RadioGroup+Fragment實(shí)現(xiàn)底部導(dǎo)航欄的功能,整體文章大概分為兩部分介紹,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-06-06Android Handler內(nèi)存泄漏詳解及其解決方案
在android開發(fā)過(guò)程中,我們可能會(huì)遇到過(guò)令人奔潰的OOM異常,這篇文章主要介紹了Android Handler內(nèi)存泄漏詳解及其解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08詳解Retrofit 動(dòng)態(tài)參數(shù)(非固定參數(shù)、非必須參數(shù))(Get、Post請(qǐng)求)
這篇文章主要介紹了詳解Retrofit 動(dòng)態(tài)參數(shù)(非固定參數(shù)、非必須參數(shù))(Get、Post請(qǐng)求),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Android中RecyclerView點(diǎn)擊Item設(shè)置事件
這篇文章主要介紹了Android中RecyclerView點(diǎn)擊Item設(shè)置事件的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07Android開發(fā)Jetpack組件Room使用講解
Room是一個(gè)數(shù)據(jù)庫(kù)訪問(wèn)組件; 對(duì)SqLite數(shù)據(jù)庫(kù)做了友好的封裝,使我們?cè)诰幋a的時(shí)候,只需要注重邏輯的部分即可,數(shù)據(jù)庫(kù)就交給Room去流暢的訪問(wèn)即可2022-08-08Android九宮格手勢(shì)密碼代碼設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了Android九宮格手勢(shì)密碼的代碼設(shè)計(jì)思路,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android RecycleView使用(CheckBox全選、反選、單選)
這篇文章主要為大家詳細(xì)介紹了Android RecycleView使用,CheckBox全選、反選、單選效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android使用AudioRecord實(shí)現(xiàn)暫停錄音功能實(shí)例代碼
本篇文章主要介紹了Android使用AudioRecord實(shí)現(xiàn)暫停錄音功能實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06MPAndroidChart自定義圖表Chart的Attribute及Render繪制邏輯
這篇文章主要為大家介紹了MPAndroidChart自定義圖表Chart的Attribute及Render繪制邏輯,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12