1. 程式人生 > >縮放系列(三):一個可以手勢縮放、拖拽、旋轉的layout

縮放系列(三):一個可以手勢縮放、拖拽、旋轉的layout

弄了一個下午,終於搞出來了,PowerfulLayout

下面是一個功能強大的改造的例子:

可以實現以下需求:

1.兩個手指進行縮放佈局

2.所有子控制元件也隨著縮放,

3.子控制元件該有的功能不能丟失(像button有可被點選的功能,縮放後不能丟失該功能)

相對上個例子,多了一個功能---

4.拖拽(平移)layout

圖片太大就不貼出來了。

佈局檔案test.xml、超級簡單的

<?xml version="1.0" encoding="utf-8"?>
<com.example.testbitmapscale.PowerfulLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

<FrameLayout
    android:background="@drawable/home_tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

        <ImageButton
            android:id="@+id/imageButton2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/selector_button1" />
</FrameLayout>


</com.example.testbitmapscale.PowerfulLayout>

java程式碼:

MainActivity也是超級簡單

public class MainActivity extends ActionBarActivity {


    private View view;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // setContentView(R.layout.test);
        view = View.inflate(this, R.layout.test, null);
        setContentView(view);

        
    }

//    @Override
//    public boolean onTouchEvent(MotionEvent event) {
//        if (event.getPointerCount() > 1) {
//            // 多點觸控
//            // 返回給ScaleGestureDetector來處理
//            return mScaleGestureDetector.onTouchEvent(event);
//        } else {
//            // 單點觸控
//            switch (event.getAction()) {
//            case MotionEvent.ACTION_DOWN:
//                downX = (int) event.getX();
//                downY = (int) event.getY();
//                newHeight = view.getLayoutParams().height;
//                newWidth = view.getLayoutParams().width;
////                 int widthMeasureSpec =
////                 View.MeasureSpec.makeMeasureSpec(2000,View.MeasureSpec.AT_MOST);
////                
////                 int heightMeasureSpec
////                 =View.MeasureSpec.makeMeasureSpec(2000,View.MeasureSpec.AT_MOST);
////                
////                 view.measure(widthMeasureSpec,heightMeasureSpec);
////                
////                 newHeight = view.getMeasuredHeight();
////                
////                 newWidth = view.getMeasuredWidth();
//                break;
//            case MotionEvent.ACTION_MOVE:
//                long currentTimeMillis = System.currentTimeMillis();
//                if (currentTimeMillis - lastMultiTouchTime > 200) {
//                    // 雙指觸控後要等待200毫秒才能執行單指觸控的操作,避免雙指觸控後出現顫抖的情況
//                    int moveX = (int) event.getX();// 移動手指的時候手指的x
//                    int moveY = (int) event.getY();// 移動手指的時候手指的y
//                    int deltaX = (int) (moveX - downX);
//                    int deltaY = (int) (moveY - downY);
//                    int newLeft = left + deltaX;// view的新left
//                    int newTop = top + deltaY;// view的新top
//                    int newRight = right + deltaX;// view的新right
//                    int newBottom = bottom + deltaY;// view的新bottom
////                    int newWidth = (int) (preScale * originalWidth);
////                    int newHeight = (int) (preScale * originalHeight);
////                    if (deltaX>(newWidth-originalHeight)/2||deltaY>(newHeight-originalHeight)/2) {
////                        return false;
////                    }
//                    // int newWidth = view.getWidth();
//                    // int newHeight = view.getHeight();
//                    System.out.println("newWidth:" + newWidth + "newHeight:"
//                            + newHeight);
////                    System.out.println(preScale);
//                    if (newLeft < originalWidth - newWidth) {
//                        newLeft = originalWidth - newWidth;
//                        newRight = newLeft + newWidth;
//                    }
//                    // if (newTop < originalHeight - newHeight){
//                    // newTop = originalHeight - newHeight;
//                    // newBottom= newTop+newHeight;
//                    // }
//                    // if(newRight>originalWidth){
//                    // newRight=originalWidth;
//                    // newLeft=newRight-newWidth;
//                    // }
//                    // if(newBottom>originalHeight){
//                    // newBottom=originalHeight;
//                    // newTop=newBottom-newHeight;
//                    // }
//                    view.layout(newLeft, newTop, newRight, newBottom);// 重新擺放view的位置
//                } else {
//                    return false;
//                }
//
//                break;
//            case MotionEvent.ACTION_UP:
//                // 更新位置資訊
//                left = view.getLeft();
//                top = view.getTop();
//                right = view.getRight();
//                bottom = view.getBottom();
//                break;
//
//            default:
//                break;
//            }
//            return true;// 代表消費了事件
//        }
//    }


}

PowerfulLayout.java:
package com.example.testbitmapscale;

import android.content.Context;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.FrameLayout;

import com.nineoldandroids.view.ViewHelper;

public class PowerfulLayout extends FrameLayout {
    // 螢幕寬高
    private int screenHeight;
    private int screenWidth;
    private ViewDragHelper mDragHelper;
    private long lastMultiTouchTime;// 記錄多點觸控縮放後的時間
    private int originalWidth;// view寬度
    private int originalHeight;// view高度
    private ScaleGestureDetector mScaleGestureDetector = null;
    // private View view;
    private int downX;// 手指按下的x座標值
    private int downY;// 手指按下的y座標值
    private int left;// view的左座標值
    private int top;// view的上座標值
    private int right;// view的右座標值
    private int bottom;// view的下座標值
    private int newHeight;
    private int newWidth;

    private float scale;
    private float preScale = 1;// 預設前一次縮放比例為1

    public PowerfulLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public PowerfulLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public PowerfulLayout(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        mDragHelper = ViewDragHelper.create(this, callback);
        mScaleGestureDetector = new ScaleGestureDetector(context,
                new ScaleGestureListener());

        // view.post(new Runnable() {
        //
        // @Override
        // public void run() {
        // left = view.getLeft();
        // top = view.getTop();
        // right = view.getRight();
        // bottom = view.getBottom();
        // originalWidth = view.getWidth();
        // originalHeight = view.getHeight();
        // }
        // });
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        screenWidth = getMeasuredWidth();
        screenHeight = getMeasuredHeight();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        super.onInterceptTouchEvent(ev);
        boolean b = mDragHelper.shouldInterceptTouchEvent(ev);// 由mDragHelper決定是否攔截事件,並傳遞給onTouchEvent
        return b;
    }

    private boolean needToHandle=true;
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int pointerCount = event.getPointerCount(); // 獲得多少點
        if (pointerCount > 1) {// 多點觸控,
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                needToHandle=false;
                break;
            case MotionEvent.ACTION_MOVE:
                
                break;
            case MotionEvent.ACTION_POINTER_2_UP://第二個手指擡起的時候
                needToHandle=true;
                break;

            default:
                break;
            }
            return mScaleGestureDetector.onTouchEvent(event);//讓mScaleGestureDetector處理觸控事件
        } else {
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - lastMultiTouchTime > 200&&needToHandle) {
//                  多點觸控全部手指擡起後要等待200毫秒才能執行單指觸控的操作,避免多點觸控後出現顫抖的情況
                try {
                    mDragHelper.processTouchEvent(event);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
//            }
        }
        return false;
    }

    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        /**
         * 用於判斷是否捕獲當前child的觸控事件
         * 
         * @param child
         *            當前觸控的子view
         * @param pointerId
         * @return true就捕獲並解析;false不捕獲
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            if (preScale > 1)
                return true;
            return false;
        }

        /**
         * 控制水平方向上的位置
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {

            if (left < (screenWidth - screenWidth * preScale) / 2)
                left = (int) (screenWidth - screenWidth * preScale) / 2;// 限制mainView可向左移動到的位置
            if (left > (screenWidth * preScale - screenWidth) / 2)
                left = (int) (screenWidth * preScale - screenWidth) / 2;// 限制mainView可向右移動到的位置
            return left;
        }

        public int clampViewPositionVertical(View child, int top, int dy) {

            if (top < (screenHeight - screenHeight * preScale) / 2) {
                top = (int) (screenHeight - screenHeight * preScale) / 2;// 限制mainView可向上移動到的位置
            }
            if (top > (screenHeight * preScale - screenHeight) / 2) {
                top = (int) (screenHeight * preScale - screenHeight) / 2;// 限制mainView可向上移動到的位置
            }
            return top;
        }

    };

    public class ScaleGestureListener implements
            ScaleGestureDetector.OnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            float previousSpan = detector.getPreviousSpan();// 前一次雙指間距
            float currentSpan = detector.getCurrentSpan();// 本次雙指間距
            if (currentSpan < previousSpan) {
                // 縮小
                // scale = preScale-detector.getScaleFactor()/3;
                scale = preScale - (previousSpan - currentSpan) / 1000;
            } else {
                // 放大
                // scale = preScale+detector.getScaleFactor()/3;
                scale = preScale + (currentSpan - previousSpan) / 1000;
            }
            // 縮放view
            if (scale > 0.5) {
                ViewHelper.setScaleX(PowerfulLayout.this, scale);// x方向上縮放
                ViewHelper.setScaleY(PowerfulLayout.this, scale);// y方向上縮放
            }
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            // 一定要返回true才會進入onScale()這個函式
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            preScale = scale;// 記錄本次縮放比例
            lastMultiTouchTime = System.currentTimeMillis();// 記錄雙指縮放後的時間
        }
    }

}

旋轉效果跟縮放效果的用法類似的,一個用的是RotateGestureDetector 一個用的是ScaleGustureDetector都是手勢檢測器,在這就不寫了。

相關推薦

系列一個可以手勢旋轉layout

弄了一個下午,終於搞出來了,PowerfulLayout 下面是一個功能強大的改造的例子: 可以實現以下需求: 1.兩個手指進行縮放佈局 2.所有子控制元件也隨著縮放, 3.子控制元件該有的功能不能丟失(像button有可被點選的功能,縮放後不能丟失該功能)

系列所有子控制元件也隨著手勢多點觸控layout

下面是一個功能強大的改造的例子: 可以實現以下需求: 1.兩個手指進行縮放佈局 2.所有子控制元件也隨著縮放, 3.子控制元件該有的功能不能丟失(像button有可被點選的功能,縮放後不能丟失該功能) 執行效果圖: java程式碼如下 MainActi

wifi認證Portal開發系列portal協議

tro spa size http log ron 認證 gin auto 中國移動WLAN業務PORTAL協議規範介紹 wifi認證Portal開發系列(三):portal協議

JavaScript夯實基礎系列this

瀏覽器 系列 中一 對象屬性 轉化 繼續 存儲 www 能夠 ??在JavaScript中,函數的每次調用都會擁有一個執行上下文,通過this關鍵字指向該上下文。函數中的代碼在函數定義時不會執行,只有在函數被調用時才執行。函數調用的方式有四種:作為函數調用、作為方法調用、作

.Net Core 商城微服務項目系列Ocelot網關接入Grafana監控

dap 商城 fig 異常類 uri 部分 中標 timeout doc 使用網關之後我們面臨的一個問題就是監控,我們需要知道網關的實時狀態,比如當前的請求吞吐量、請求耗費的時間、請求峰值甚至需要知道具體哪個服務的哪個方法花費了多少時間。網關作為請求的中轉點是監控品牌的要塞

詳解SVM系列線性可分支援向量機與硬間隔最大化

支援向量機概覽(support vector machines SVM) 支援向量機是一種二類分類模型。它的基本模型是定義在特徵空間上的間隔最大(間隔最大區別於感知機)線性分類器(核函式可以用非線性的分類)。 支援向量機的學習策略是間隔最大化可形式化為一個求解凸二次規劃的問題。 也等

Hadoop系列hadoop基本測試

下面是對hadoop的一些基本測試示例 Hadoop自帶測試類簡單使用 這個測試類名叫做 hadoop-mapreduce-client-jobclient.jar,位置在 hadoop/share/hadoop/mapreduce/ 目錄下 不帶任何引數可以獲取這個jar的幫助資訊 $ yar

Docker系列將.net core api部署到Kubernetes (K8s)中

1.新建一個WebApi專案,並新增Dockerfile檔案: FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1-sdk AS build WOR

STM32開發筆記49STM32F4+DP83848乙太網通訊指南系列中斷向量

本章為系列指南的第三章,這一章將會在正式進入乙太網的配置和使用之前,複習一下STM32的中斷以及中斷向量,因為我們以後要在中斷中響應乙太網收包。 中斷—嵌入式中的多執行緒 從51微控制器到ARM架構的32位微晶片,到樹莓派、Ardunio等單板機,中斷的概念對於這些晶片都非常重要。本人是純軟

eShopOnContainers學習系列RabbitMQ訊息匯流排實踐

今天研究了下eShopOnContainers裡的RabbitMQ的使用,在專案裡是以封裝成訊息匯流排的方式使用的,但是仍然是以其釋出、訂閱兩個方法作為基礎封裝的,我們今天就來實際使用一下。 為了簡單起見,就在同一個API專案裡實現釋出訂閱。 新建API專案 RabbitMQ_Bus_Test

eShopOnContainers學習系列RabbitMQ消息總線實踐

bytes 變量名 fault invalid available 通信 make 中新 down 今天研究了下eShopOnContainers裏的RabbitMQ的使用,在項目裏是以封裝成消息總線的方式使用的,但是仍然是以其發布、訂閱兩個方法作為基礎封裝的,我們今天就來

Web安全系列XSS 攻擊進階挖掘漏洞

前言 目前來說,XSS 的漏洞型別主要分為三類:反射型、儲存型、DOM型,在本篇文章當中會以permeate生態測試系統為例,分析網站功能,引導攻擊思路,幫助讀者能夠快速找出網站可能存在的漏洞。 反射型 XSS 挖掘 現在筆者需要進行手工XSS漏洞挖掘,在手工挖掘之前筆者需要先逛逛網站有哪些功能點,如下圖是

爬蟲入門系列用 requests 構建知乎 API

爬蟲入門系列目錄: 在爬蟲系列文章 優雅的HTTP庫requests 中介紹了 requests 的使用方式,這一次我們用 requests 構建一個知乎 API,功能包括:私信傳送、文章點贊、使用者關注等,因為任何涉及使用者操作的功能都需要登入後才操作,所以在閱讀這篇文章前建議先了解

ElasticSearch實踐系列探索資料

前言 經過前兩篇文章得實踐,我們已經瞭解了ElasticSearch的基礎知識,本篇文章讓我來操作一些更真實的資料集。我們可以利用www.json-generator.com/生成如下的文件結構: { "account_number": 1, "balance": 39225,

DelayQueue系列持久化方案

原文發表於簡書DelayQueue之持久化方案,本次更新主要是對processTask方法做了優化,以及優化了補償執行的額外延遲時間設定,可對比閱讀。 上一篇文章中提到了我們在專案中運用DelayQueue解決了一些需要延遲執行的任務,但是最近我們在生產環境上遇到了一個問題。重啟伺服器後,那些未執行的延遲任

MongoDB入門系列查詢SELECT

一、概述 mongodb是最接近關係型資料庫的NOSQL資料庫,它的儲存方式非常的靈活;以至於你會將它看成是一個經過冗餘過的關係型資料庫的表,這也是Mongodb原子性的一個特徵。由於沒有關係型資料庫的表之間的關聯關係和事務性所以Mongodb插入和更新的效率非常的高,同時也支援索引。我們在查詢的時候不能帶

IntelliJ IDEA設定系列設定Tomcat伺服器

1.選擇Server Edit Configurations 2.選擇Add Tomcat Server 3.部署war 附:web專案可在Project Structurre 的Arti

周志華《機器學習》課後習題解答系列Ch2

本章概要 本章講述了模型評估與選擇(model evaluation and selection)的相關知識: 2.1 經驗誤差與過擬合(empirical error & overfitting) 精度accuracy、訓練誤差(經驗誤差)

Docker學習系列Ubuntu下使用Docker的基本指令記錄及一些注意事項

1.Dockerhub下載映象 有兩種方式可以獲得新的映象 直接從dockerhub下載編譯好的image(該編譯過程在docker hub的雲端完成)(見3.1) 下載docekrfile檔案,在本機進行build 直接在docker

mybatis-generator原始碼解讀系列配置讀取

概述:        配置讀取是程式碼生成的基礎工作,主要就是把xml中的元資料讀取到記憶體中,供後面的程式碼生成邏輯使用相關類1、ConfigurationParser功能        主要用來將xml配置檔案讀取到記憶體,獲取根節點,根據根節點的屬性值,選擇對應的子節點