1. 程式人生 > >Android DIY之路 (一) 指定區域多圖片合成 放大 縮小 映象 旋轉 等

Android DIY之路 (一) 指定區域多圖片合成 放大 縮小 映象 旋轉 等

最近一直的工作是手機上的DIY操作,功能很多網上資料不多,將最近遇到的功能分解成模組 今天介紹的是圖片(我這裡也可以是任意View 常見的還有Edittext需要這樣的功能 指定區域多圖片合成 放大 縮小 映象 旋轉) 一一分享,當然做法有很多種,我分享其中嘗試後最簡單的一種

有做DIY的同學可以加我一起討論。除了美顏沒有那麼牛B的演算法外 基本功能都設計到了

#

慣例先看效果圖

合成圖片

// 注意做類似這種模板功能時候 方位由後臺資料提供,這裡我們用假資料 4個點 或者xy 加區域來做示例
//一開始我們公司用的是透明蓋住 操作圖片 但發現 侷限性較大。後來直接限定區域。將操作圖片層級移到模板圖上面 隨意疊加
1. 背景圖上繪製操作區域,(操作區域可以不止一個,比如美圖秀秀的模板)
2. 操作區域內加上 編輯素材圖的容器與編輯區域相同
3. 新增可編輯的view(這裡方便觀看用的ImageView)
4. 合成將你想要的View範圍截圖(也可以去掉你不想要的那部分)

合成將你想要的View範圍截圖檔案

知道這個 可以簡化你自己做圖生成圖片的 省略 很多複雜方法 所以先放出來 後面DIY也是用的最多的方法。
flRoot.setClipChildren(false);//是否限制子View在其範圍內*(注意這個屬性必須放在拖動佈局的爺爺佈局才能生效)
還有這個方法。使其顯示View的全域性哪怕被遮擋。方便使用者在編輯圖片的時候更清晰
1.
View view = 你想要合成的當前顯示的view
 Bitmap bitmap = Bitmap.createBitmap(view .getWidth(), view .getHeight(),  Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
view .draw(canvas);
如果本例中我是ivTemps 那麼整個圖片就區域性截圖下來了。就類似區域性截圖。當然這個bitmap可以繼續操作。又或者你可以把4個按鈕隱藏掉後 再區域性截圖
2.
bitmap存檔案
public static String saveBitmap(Context context, Bitmap bitmap) {
File file = null;
try {
file = new File(getCacheDir(), getFileName());/這是你的檔案存的路徑 自己定義吧
FileOutputStream os = new FileOutputStream(file);
bitmap = small(bitmap,0.5f);
mmmmmm = Bitmap.createBitmap(bitmap).copy(Bitmap.Config.ARGB_8888, true);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
os.flush();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

    return file.getPath();
}

背景圖上繪製操作區域

  <com.rex.refreshapp.ImagesTemplates
        android:id="@+id/ivTemps"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        />

自定義的FrameLayout
ImagesTemplates帶的佈局 View view = View.inflate(mContext, R.layout.view_images_templates, null);
addView(view);
簡單形成一個組合控制元件。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/flRoot"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:background="#FFF"
    >

    <ImageView
        android:id="@+id/ivBg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <FrameLayout
        android:id="@+id/flZone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#50654786"
        android:clipChildren="false"
        >

    </FrameLayout>

</FrameLayout>

操作區域內加上 編輯素材圖的容器與編輯區域相同

 initBg(30, 90, 800, 1200);//背景起點 長寬
  private void initBg(int x, int y, int width, int height) {
        ivBg.setX(x);
        ivBg.setY(y);
        ivBg.setScaleType(ImageView.ScaleType.CENTER_CROP);
        ivBg.setImageResource(R.mipmap.yudi);
        ViewGroup.LayoutParams layoutParams = ivBg.getLayoutParams();
        layoutParams.width = width;
        layoutParams.height = height;
        ivBg.setLayoutParams(layoutParams);
    }

//可多個


-----------------------

 initEditZone(30, 30, 600, 800);// 展現編輯區域 虛線 和半透明  可能多個拓展
     /**
     * 通過起點和區域範圍  得到編輯區域
     *
     * @param x
     * @param y
     * @param width
     * @param height
     */
    private void initEditZone(final int x, final int y, final int width, final int height) {

        ViewTreeObserver vto = ivBg.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                                          @Override
                                          public void onGlobalLayout() {

                                              //監聽一次馬上結束

                                              if (Build.VERSION.SDK_INT < 16) {
                                                  ivBg.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                                              } else {
                                                  ivBg.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                              }

                                              Bitmap bitmap = Bitmap.createBitmap(ivBg.getWidth(), ivBg.getHeight(), Bitmap.Config.ARGB_8888);
                                              Canvas canvas = new Canvas(bitmap);
                                              canvas.drawColor(Color.WHITE);
                                              ivBg.draw(canvas);


                                              Paint p = new Paint();
                                              p.setStyle(Paint.Style.STROKE);
                                              //設定虛線效果
                                              p.setPathEffect(new DashPathEffect(new float[]{10, 5}, 0));
                                              p.setStrokeWidth(3);
                                              p.setColor(Color.parseColor("#D0104C"));


                                              Path path = new Path();
                                              path.moveTo(x, y);
                                              path.lineTo(x + width, y);
                                              path.lineTo(x + width, y + height);
                                              path.lineTo(x, y + height);
                                              path.close();
                                              canvas.drawPath(path, p);

                                              ivBg.setImageBitmap(bitmap);


                                              //顯示編輯區域範圍
                                              flZone.setX(x + ivBg.getX());
                                              flZone.setY(y + ivBg.getY());
                                              FrameLayout.LayoutParams layoutParams = (LayoutParams) flZone.getLayoutParams();
                                              layoutParams.width = width;
                                              layoutParams.height = height;

                                              flZone.setLayoutParams(layoutParams);


                                          }


                                      }

        );


    }
    1.

新增可編輯的view(這裡方便觀看用的ImageView)

當然你可以一個ImageView畫4個bitmap搞定 但是後面我否定了這個方法 重寫了這種比較直觀的4個ImageView 作為按鈕蓋在主圖View方法,中間的View可以替換。也方便理解,簡化了後面的步驟。
R.layout.add_img_item

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fl"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    >

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="15dp"
        android:scaleType="centerCrop"/>

    <ImageView
        android:id="@+id/ivLeft"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="top|left"
        android:src="@mipmap/r_close"
        />

    <ImageView
        android:id="@+id/ivRight"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="top|right"
        android:src="@mipmap/r_rotate"
        />

    <ImageView
        android:id="@+id/ivBLeft"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="bottom|left"
        android:src="@mipmap/r_symmetric"
        />

    <ImageView
        android:id="@+id/ivBRight"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="bottom|right"
        android:src="@mipmap/r_zoom"
        />
</FrameLayout>
  private void initEditImage() {

        final View flEditImage = View.inflate(mContext, R.layout.add_img_item, null);
        flZone.addView(flEditImage);

        final ImageView iv = (ImageView) flEditImage.findViewById(R.id.iv);
        final ImageView ivLeft = (ImageView) flEditImage.findViewById(R.id.ivLeft);
        final ImageView ivRight = (ImageView) flEditImage.findViewById(R.id.ivRight);
        final ImageView ivBLeft = (ImageView) flEditImage.findViewById(R.id.ivBLeft);
        final ImageView ivBRight = (ImageView) flEditImage.findViewById(R.id.ivBRight);
        iv.setImageResource(R.mipmap.shaosiming);
        flEditImage.setTag(iv);


        ViewTreeObserver vto = flEditImage.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                                          @Override
                                          public void onGlobalLayout() {

                                              //監聽一次馬上結束

                                              if (Build.VERSION.SDK_INT < 16) {
                                                  flEditImage.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                                              } else {
                                                  flEditImage.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                              }
                                              int[] location = new int[2];
                                              flEditImage.getLocationOnScreen(location);//獲取在整個螢幕內的絕對座標
                                              centerX0 = location[0] + flEditImage.getWidth() / 2;
                                              centerY0 = location[1] + flEditImage.getHeight() / 2;
                                          }
                                      }

        );

        //平移
        flEditImage.setOnTouchListener(new OnTouchListener() {

            private boolean isMove;
            private int downY;
            private int downX;

            private ImageView iv;

            @Override
            public boolean onTouch(View view, MotionEvent event) {
                FrameLayout flview = (FrameLayout) view;
                iv = (ImageView) view.getTag();
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    downX = (int) event.getRawX();
                    downY = (int) event.getRawY();
                    isMove = false;
                }
                if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    isMove = true;
                    int moveX = (int) event.getRawX();
                    int moveY = (int) event.getRawY();
//                Log.d(TAG,"moveX   "+moveX+"  moveY  "+moveY);
                    int dx = moveX - downX;
                    int dy = moveY - downY;
                    int i1 = dx + (int) ViewHelper.getTranslationX(flview);
                    int i2 = dy + (int) ViewHelper.getTranslationY(flview);
                    ViewHelper.setTranslationX(flview, i1);
                    ViewHelper.setTranslationY(flview, i2);
                    downX = moveX;
                    downY = moveY;
                    Log.i("rex", "ACTION_MOVE");
                    flRoot.setClipChildren(false);//是否限制子View在其範圍內*(注意這個屬性必須放在拖動佈局的爺爺佈局才能生效)

                }
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    Log.i("rex", "ACTION_UP");
                    centerX = centerX0 + flEditImage.getTranslationX();
                    centerY = centerY0 + flEditImage.getTranslationY();
                    flRoot.setClipChildren(true);
                    flRoot.invalidate();

                    if (CheckIsOut(iv, flview)) {
                        ViewGroup parent = (ViewGroup) flview.getParent();
                        parent.removeView(flview);

//                    flZone.removeView(flview);
                        Toast.makeText(mContext, "超出範圍已移除", Toast.LENGTH_SHORT).show();
                    }

                    if (!isMove) {
//                    Log.i("rex", "i 點選事件 ");
                    }
//                    centerX = (flEditImage.getLeft() + flEditImage.getWidth()) / 2;
//                    centerY = (flEditImage.getTop() + flEditImage.getHeight()) / 2;

                }

                return true;
            }
        });

        //刪除
        ivLeft.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {

                flZone.removeView(flEditImage);
            }
        });

        //映象
        ivBLeft.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {

                Bitmap bitmap = Bitmap.createBitmap(iv.getWidth(), iv.getHeight(), Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                canvas.drawColor(Color.WHITE);
                iv.draw(canvas);
                iv.setImageBitmap(flip(bitmap, false));

            }
        });
        //旋轉
        ivRight.setOnTouchListener(new OnTouchListener() {

            private float mDownY;
            private float mDownX;


//
//            float centerX = flEditImage.getWidth() / 2;
//            float centerY = flEditImage.getHeight() / 2;

            @Override
            public boolean onTouch(View view, MotionEvent event) {

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:


                        mDownX = event.getRawX();
                        mDownY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        float moveX = event.getRawX();
                        float moveY = event.getRawY();
                        float rotate = flEditImage.getRotation();
                        float angle = getDegress(moveX, moveY) - getDegress(mDownX, mDownY);
//                        float angle = getDegress(mDownX, mDownY) - getDegress(moveX, moveY);


                        Log.d("name", "degress -> " + (angle + rotate));
                        flEditImage.setRotation((rotate + angle) % 360);

                        mDownX = moveX;
                        mDownY = moveY;
                        break;
                    case MotionEvent.ACTION_UP:

                        break;
                }
                return true;

            }

            private float getDegress(float newX, float newY) {
                float x = newX - centerX;
                float y = newY - centerY;
                return (float) Math.toDegrees(Math.atan2(y, x));
            }
        });

        //縮放
        ivBRight.setOnTouchListener(new OnTouchListener() {


            @Override
            public boolean onTouch(View view, MotionEvent event) {

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
//                        mDownX = event.getRawX();
//                        mDownY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        float moveX = event.getRawX();
                        float moveY = event.getRawY();

                        float bili = getBili(moveX, moveY);
                        Log.d("rex", "bili -> " + bili);
                        flEditImage.setScaleX(bili);
                        flEditImage.setScaleY(bili);
                        correct4ButtonSize(1 / bili);

                        Log.d("rex", " ivBRight.getScaleX(); " + ivBRight.getScaleX());
//                        mDownX = moveX;
//                        mDownY = moveY;
                        break;
                    case MotionEvent.ACTION_UP:

                        break;
                }
                return true;

            }

            /**
             *
             * @param bili
             */
            private void correct4ButtonSize(float bili) {

                ivLeft.setScaleX(bili);
                ivLeft.setScaleY(bili);

                ivRight.setScaleX(bili);
                ivRight.setScaleY(bili);

                ivBLeft.setScaleX(bili);
                ivBLeft.setScaleY(bili);

                ivBRight.setScaleX(bili);
                ivBRight.setScaleY(bili);
                ;
            }

            private float getBili(float moveX, float moveY) {

                int[] location = new int[2];
//                flEditImage.getLocationInWindow(location); //獲取在當前視窗內的絕對座標
                flEditImage.getLocationOnScreen(location);//獲取在整個螢幕內的絕對座標
                float radius = ivBLeft.getWidth();//不精確

                float oldLine = (float) Math.sqrt(flEditImage.getWidth() * flEditImage.getWidth() + flEditImage.getHeight() * flEditImage.getHeight());
                float newLine = (float) Math.sqrt((moveX - location[0] + radius) * (moveX - location[0] + radius) + (moveY - location[1] + radius) * (moveY - location[1] + radius));

                float scale = newLine / oldLine;//此處有微量誤差
                return scale;
            }
        });


    }
 /**
     * 判斷是否超出
     * @param iv
     * @param fl
     * @return
     */
    private boolean CheckIsOut(ImageView iv, FrameLayout fl) {
        int maxX = flZone.getWidth();
        int maxY = flZone.getHeight();

        int a = fl.getWidth();
        int a2 = (int) fl.getX();
        int a3 = iv.getLeft();
        int a4 = iv.getRight();
        int x1 = (int) (fl.getX() + iv.getLeft());
        int x2 = (int) (fl.getX() + fl.getWidth() - iv.getLeft());
        int y1 = (int) (fl.getY() + iv.getTop());
        int y2 = (int) (fl.getY() + fl.getHeight() - iv.getTop());


        if (x1 > maxX || x2 < 0 || y1 > maxY || y2 < 0) {
            return true;
        }
        return false;
    }


    public static Bitmap flip(Bitmap bitmap, boolean isVer) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        Matrix matrix = new Matrix();
        if (isVer)
            matrix.postScale(1, -1);//映象垂直翻轉
        else
            matrix.postScale(-1, 1); //映象水平翻轉
        Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
        bitmap.recycle();
        return newBitmap;
    }

相關推薦

Android DIY 指定區域圖片合成 放大 縮小 映象 旋轉

最近一直的工作是手機上的DIY操作,功能很多網上資料不多,將最近遇到的功能分解成模組 今天介紹的是圖片(我這裡也可以是任意View 常見的還有Edittext需要這樣的功能 指定區域多圖片合成 放大 縮小 映象 旋轉) 一一分享,當然做法有很多種,我分享其中嘗試

Android程式設計師的救贖

最近在整理一些Android的筆記,越來越覺得自己在遠離Android開發。Android程式設計師的就業情況也不如以前,包括我自己雖然是以移動端工程師的身份進入新公司的,但做的事情從前到後,也不在區分移動端和非移動端。 十年之前,你不認識我,我不認識你。 2008年Android剛剛

Android進階 -- AS 3.0NDK環境搭建及hello world

通過一段找實習的經歷,以及快應用、小程式、web app等移動開發模式的衝擊,深深為安卓原生開發的未來感到擔憂,希望接入人工智慧API和物聯網井噴能給安卓開發帶來新的生機吧。當然,作為小白我是不用擔心這麼多的,做自己喜歡的事就好了,於是拋開雜念,繼續學習安卓。NDK開發環境的

Android外掛化學習動態載入綜述

前段時間,公司專案完成了外掛化的開發,自己也因此學習了很多Android外掛化的知識,於是想把這些內容記錄下來,本次帶來Android外掛化的第一篇:動態載入綜述 背景知識 1.什麼是動態載入? 動態載入技術應由以下幾個部分組成: 1) 應用在執行

前端 —— node.js摸爬打滾

turn lan name resp function oba ack val 括號 安裝: window下的安裝,node.js直接上官網下載:https://nodejs.org/en/ 選擇LTS,也就是版本號比較低的穩定版,下載下來後運行下載的文件進行安裝; 通

我的學習SQL盲註學習篇

網絡安全 dvwa sql盲註 我的學習之路,現在零基礎,是一個小白,請各位大牛批評指正!寫下這篇,是對自己的一個思路的整理,僅供參考。 Dvwa中登錄進入,首先在DVWA Security中設置等級為low,然後進入SQL Injection(blind),隨意輸入一個數字進行抓包,然後找

成長 GridLayout 布局

bsp ren 1.0 androi enc 代碼區 andro XML orien 效果圖 代碼區 <?xml version="1.0" encoding="utf-8"?><GridLayout xmlns:android="htt

react爬坑--報錯output.path不是絕對路徑

bpa file 文件 開始 put pac 這就是 文件頭部 之前   之前,一直在糾結是學習angular好,學習vue好,還是學習react好,網上一搜索,也是各種對比,各種互噴,看過之後更糾結。就跟小時候一樣糾結長大了是上清華好,還是上北大好,最後證明我想多了。總之

初識vue.js,我的學習

自動打開 下一步 鏡像 分享圖片 bpa demo 中間 前端技術 width 在以前做項目時經常是新建一些html、css、等一些文件,但在接觸了vue.js之後我發現我已經有點看不懂前端了,這對於我這麽一個菜鳥來說實在是很苦逼的事情。現在的前端技術都離不開

Hadoop學習理論基礎和邏輯思維

file 工作 puts 範圍 小文件 集合 無效 任務 問題 三個題目 第一題 問題描述 統計出當前這個一行一個IP的文件中,到底哪個IP出現的次數最多 解決思路 //必須要能讀取這個內容 BufferedReader br = n

Vue 爬坑—— 使用 vue-cli 搭建項目 增補

web http class 模板 clas ebp 搭建 com png cd 指定好安裝目錄 vue init webpack 項目名稱 執行 vue vue list 查看可應用模板 vue init webpack +名字

Python學習

Python 基礎 Python基礎學習1(1)變量在Python中用來存儲數據所指向的內存地址叫做變量(2)變量的命名變量的命名由數字,字母,下劃線組成,數字不能開頭;不要使用Python中的關鍵字和函數名稱來命名變量;命名時要簡明,具有描述性;變量名區分大小寫。命名方式遵循駝峰命名法和下劃線命名法

python開發

數字 美團 其中 class 翻譯 電視 ... 硬件 算數運算 一、python基礎入門 1、編程語言 程序員與計算機溝通的語言就叫做編程語言 編程語言發展至今經歷了以下: ①.機器語言:站在計算機(奴隸)的角度,說計算機能聽懂的語言,那就是直接用二進制編程,直接操作

Hive學習 Hive初識

完成 優化 ble 缺點 ase 適合 table vol 利用 Hive 簡介 什麽是Hive 1、Hive 由 Facebook 實現並開源 2、是基於 Hadoop 的一個數據倉庫工具 3、可以將結構化的數據映射為一張數據庫表 4、並提供 HQL(Hive

Scala學習 Spark初識

海量 處理 apache .org 流式 height 計算平臺 提高 結構化數據 一、官網介紹 官網地址:http://spark.apache.org/ Apache Spark™是用於大規模數據處理的統一分析引擎。 從右側最後一條新聞看,Spar

Vue 爬坑—— 使用 vue-cli 搭建項目

wrong run 自己 文件的 ima tca 來安 mage 前綴 vue-cli 是一個官方發布 vue.js 項目腳手架,使用 vue-cli 可以快速創建 vue 項目,GitHub地址是:https://github.com/vuejs/vue-cli 一、

學習淺談:基礎命令及linux工作原理

linux命令 看了視頻,它系統的講解了linux從硬件到操作系統的工作過程,以及常用的基礎命令的詳細參數及用法。 我也在這裏整理之後加強記憶一次基本概念,及linux文件樹目錄的基本知識結構。 硬件——>操作系統OS——>l

JAVA基礎學習

個人理解 整形 之路 boolean mman 類型 布爾 屬性 註釋 JAVA基礎概念: PATHl: path屬於操作系統的屬性,是系統用來搜尋可執行文件的路徑,個人理解是類似於linux中的全局變量 CALSSPATH:java程序解釋類文件時加載文件的路徑 註釋

orcale存儲過程學習

部分 bsp packages ORC har package var IE repl 1.在packages文件夾下新建一個“包”,當新建時會同時出現兩個供編輯的面板:一個為“聲明”的,一個為“body”部分的。 2.編寫兩個空方法: ------------------

緩存架構

常見 none ble 訪問網站 (上) 架構 分布 com content 一、緩存概述 本文主要介紹緩存架構的相關理論,常見的緩存組件及應用場景。二、緩存的分類 1、服務端緩存的分類(由上至下) 2、CDN緩存 3、反向代理緩存 4、集中式