Android 高仿頭像截取 打造不一樣的自訂控制項,android高仿微
轉載請表明出處:http://blog.csdn.net/lmj623565791/article/details/39761281,本文出自:【張鴻洋的部落格】1、概述
前面已經寫了關於檢測手勢識別的文章,如果不瞭解可以參考:Android 手勢檢測實戰 打造支援縮放平移的圖片預覽效果(下)。首先本篇文章,將對之前部落格的ZoomImageView代碼進行些許的修改與改善,然後用到我們的本篇部落格中去,實現仿的頭像截取功能,當然了,個人覺得的截取頭像功能貌似做得不太好,本篇部落格準備去其糟粕,取其精華;最後還會見識到不一樣的自訂控制項的方式,也是在本人部落格中首次出現,如果有興趣可以讀完本篇部落格,希望可以啟到拋磚引玉的效果。
2、效果分析1、:
我們來看看妹子的項鏈,嗯,妹子項鏈還是不錯的~
2、效果分析
根據上面的效果,我們目測需要自訂兩個控制項,一個就是我們的可自由縮放移動的ImageView,一個就是那個白色的邊框;然後一起放置到一個RelativeLayout中;最後對外公布一個裁剪的方法,返回一個Bitmap;
暫時的分析就這樣,下面我們來寫代碼~
首先是白色框框那個自訂View,我們叫做ClipImageBorderView
3、ClipImageBorderView
分析下這個View,其實就是根據在螢幕中繪製一個正方形,正方形地區以外為半透明,繪製這個正方形需要與螢幕左右邊距有個邊距。
我們準備按如繪製:
按順序在View的onDraw裡面繪製中:1、2、3、4,四個半透明的地區,然後在中間正方形地區繪製一個正方形
下面看下代碼:
package com.zhy.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.util.AttributeSet;import android.util.TypedValue;import android.view.View;/** * @author zhy * */public class ClipImageBorderView extends View{/** * 水平方向與View的邊距 */private int mHorizontalPadding = 20;/** * 垂直方向與View的邊距 */private int mVerticalPadding;/** * 繪製的矩形的寬度 */private int mWidth;/** * 邊框的顏色,預設為白色 */private int mBorderColor = Color.parseColor("#FFFFFF");/** * 邊框的寬度 單位dp */private int mBorderWidth = 1;private Paint mPaint;public ClipImageBorderView(Context context){this(context, null);}public ClipImageBorderView(Context context, AttributeSet attrs){this(context, attrs, 0);}public ClipImageBorderView(Context context, AttributeSet attrs, int defStyle){super(context, attrs, defStyle);// 計算padding的pxmHorizontalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding, getResources().getDisplayMetrics());mBorderWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mBorderWidth, getResources().getDisplayMetrics());mPaint = new Paint();mPaint.setAntiAlias(true);}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);//計算矩形地區的寬度mWidth = getWidth() - 2 * mHorizontalPadding;//計算距離螢幕垂直邊界 的邊距mVerticalPadding = (getHeight() - mWidth) / 2;mPaint.setColor(Color.parseColor("#aa000000"));mPaint.setStyle(Style.FILL);// 繪製左邊1canvas.drawRect(0, 0, mHorizontalPadding, getHeight(), mPaint);// 繪製右邊2canvas.drawRect(getWidth() - mHorizontalPadding, 0, getWidth(),getHeight(), mPaint);// 繪製上邊3canvas.drawRect(mHorizontalPadding, 0, getWidth() - mHorizontalPadding,mVerticalPadding, mPaint);// 繪製下邊4canvas.drawRect(mHorizontalPadding, getHeight() - mVerticalPadding,getWidth() - mHorizontalPadding, getHeight(), mPaint);// 繪製外邊框mPaint.setColor(mBorderColor);mPaint.setStrokeWidth(mBorderWidth);mPaint.setStyle(Style.STROKE);canvas.drawRect(mHorizontalPadding, mVerticalPadding, getWidth()- mHorizontalPadding, getHeight() - mVerticalPadding, mPaint);}}我們直接預設了一個水平方向的邊距,根據邊距計算出正方形的邊長,接下來就是按照分別會1、2、3、4四個地區,最後就是繪製我們的正方形~~
代碼還是很簡單的~~我們的ClipImageBorderView就搞定了,我們決定來測試一下:
布局檔案:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/a" > <com.zhy.view.ClipImageBorderView android:id="@+id/id_clipImageLayout" android:layout_width="fill_parent" android:layout_height="fill_parent" /></RelativeLayout>
:
故意放了個背景,沒撒用,就是為了能看出效果,可以看到我們的框框繪製的還是蠻不錯的~~嗯,這個框框距離螢幕左右兩側的距離應該抽取出來,嗯,後面再說~
4、ClipZoomImageView
我們準備對我們原先的ZoomImageView進行簡單的修改,修改的地方:
1、在onGlobalLayout方法中,如果圖片的寬或者高只要一個小於我們的正方形的邊長,我們會直接把較小的尺寸放大至正方形的邊長;如果圖片的寬和高都大於我們的正方形的邊長,我們僅僅把圖片移動到我們螢幕的中央,不做縮放處理;
2、根據步驟1,我們會獲得初始的縮放比例(預設為1.0f),然後SCALE_MID , 與 SCALE_MAX 分別為2倍和4倍的初始化縮放比例。
3、圖片在移動過程中的邊界檢測完全根據正方形的地區,圖片不會在移動過程中與正方形地區產生內邊距
4、對外公布一個裁切的方法
部分代碼:
/** * 水平方向與View的邊距 */private int mHorizontalPadding = 20;/** * 垂直方向與View的邊距 */private int mVerticalPadding;@Overridepublic void onGlobalLayout(){if (once){Drawable d = getDrawable();if (d == null)return;Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());// 計算padding的pxmHorizontalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding,getResources().getDisplayMetrics());// 垂直方向的邊距mVerticalPadding = (getHeight() - (getWidth() - 2 * mHorizontalPadding)) / 2;int width = getWidth();int height = getHeight();// 拿到圖片的寬和高int dw = d.getIntrinsicWidth();int dh = d.getIntrinsicHeight();float scale = 1.0f;if (dw < getWidth() - mHorizontalPadding * 2&& dh > getHeight() - mVerticalPadding * 2){scale = (getWidth() * 1.0f - mHorizontalPadding * 2) / dw;}if (dh < getHeight() - mVerticalPadding * 2&& dw > getWidth() - mHorizontalPadding * 2){scale = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;}if (dw < getWidth() - mHorizontalPadding * 2&& dh < getHeight() - mVerticalPadding * 2){float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2)/ dw;float scaleH = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;scale = Math.max(scaleW, scaleH);}initScale = scale;SCALE_MID = initScale * 2;SCALE_MAX = initScale * 4;Log.e(TAG, "initScale = " + initScale);mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);mScaleMatrix.postScale(scale, scale, getWidth() / 2,getHeight() / 2);// 圖片移動至螢幕中心setImageMatrix(mScaleMatrix);once = false;}}/** * 剪下圖片,返回剪下後的bitmap對象 * * @return */public Bitmap clip(){Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);draw(canvas);return Bitmap.createBitmap(bitmap, mHorizontalPadding,mVerticalPadding, getWidth() - 2 * mHorizontalPadding,getWidth() - 2 * mHorizontalPadding);}/** * 邊界檢測 */private void checkBorder(){RectF rect = getMatrixRectF();float deltaX = 0;float deltaY = 0;int width = getWidth();int height = getHeight();// 如果寬或高大於螢幕,則控制範圍if (rect.width() >= width - 2 * mHorizontalPadding){if (rect.left > mHorizontalPadding){deltaX = -rect.left + mHorizontalPadding;}if (rect.right < width - mHorizontalPadding){deltaX = width - mHorizontalPadding - rect.right;}}if (rect.height() >= height - 2 * mVerticalPadding){if (rect.top > mVerticalPadding){deltaY = -rect.top + mVerticalPadding;}if (rect.bottom < height - mVerticalPadding){deltaY = height - mVerticalPadding - rect.bottom;}}mScaleMatrix.postTranslate(deltaX, deltaY);}
這裡貼出了改變的代碼,完整的代碼就不貼了,太長了,如果大家學習過前面的部落格應該也會比較熟悉,若沒有也沒事,後面會提供源碼。
貼代碼的目的,第一讓大家看下我們改變了哪些;第二,我想暴露出我們代碼中的問題,我們設定了一個這樣的變數:mHorizontalPadding = 20;這個是手動和ClipImageBorderView裡面的成員變數mHorizontalPadding 寫的一致,也就是說這個變數,兩個自訂的View都需要使用且需要相同的值,目前我們的做法,寫死且每個View各自訂一個。這種做法不用說,肯定不好,即使抽取成自訂屬性,兩個View都需要進行抽取,且使用者在使用的時候,還需要設定為一樣的值,總覺得有點強人所難~~
5、不一樣的自訂控制項
現在我們考慮下:易用性。目前為止,其實我們的效果已經實現了,但是需要使用者這麼寫布局檔案:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#aaaaaa" > <com.zhy.view.ZoomImageView android:id="@+id/id_zoomImageView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="matrix" android:src="@drawable/a" /> <com.zhy.view.ClipImageView android:layout_width="fill_parent" android:layout_height="fill_parent" /></RelativeLayout>
然後這兩個類中都有一個mHorizontalPadding變數,且值一樣,上面也說過,即使抽取成自訂變數,也需要在布局檔案中每個View中各寫一次。so, we need change . 這樣的耦合度太誇張了,且使用起來蹩腳。
於是乎,我決定把這兩個控制項想辦法整到一起,使用者使用時只需要聲明一個控制項:
怎麼做呢,我們使用組合的思想來自訂控制項,我們再聲明一個控制項,繼承子RelativeLayout,然後在這個自訂RelativeLayout中通過代碼添加這兩個自訂的布局,並且設定一些公用的屬性,具體我們就開始行動。
1、ClipImageLayout
我們自訂一個RelativeLayout叫做ClipImageLayout,用於放置我們的兩個自訂View,並且由ClipImageLayout進行設定邊距,然後傳給它內部的兩個View,這樣的話,跟使用者互動的就一個ClipImageLayout,使用者只需要設定一次邊距即可。
完整的ClipImageLayout代碼:
package com.zhy.view;import android.content.Context;import android.graphics.Bitmap;import android.util.AttributeSet;import android.util.TypedValue;import android.widget.RelativeLayout;import com.zhy.clippic.R;/** * zhy * @author zhy * */public class ClipImageLayout extends RelativeLayout{private ClipZoomImageView mZoomImageView;private ClipImageBorderView mClipImageView;/** * 這裡測試,直接寫死了大小,真正使用過程中,可以提取為自訂屬性 */private int mHorizontalPadding = 20;public ClipImageLayout(Context context, AttributeSet attrs){super(context, attrs);mZoomImageView = new ClipZoomImageView(context);mClipImageView = new ClipImageBorderView(context);android.view.ViewGroup.LayoutParams lp = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT,android.view.ViewGroup.LayoutParams.MATCH_PARENT);/** * 這裡測試,直接寫死了圖片,真正使用過程中,可以提取為自訂屬性 */mZoomImageView.setImageDrawable(getResources().getDrawable(R.drawable.a));this.addView(mZoomImageView, lp);this.addView(mClipImageView, lp);// 計算padding的pxmHorizontalPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHorizontalPadding, getResources().getDisplayMetrics());mZoomImageView.setHorizontalPadding(mHorizontalPadding);mClipImageView.setHorizontalPadding(mHorizontalPadding);}/** * 對外公布設定邊距的方法,單位為dp * * @param mHorizontalPadding */public void setHorizontalPadding(int mHorizontalPadding){this.mHorizontalPadding = mHorizontalPadding;}/** * 裁切圖片 * * @return */public Bitmap clip(){return mZoomImageView.clip();}}
可以看到,現在使用者需要使用頭像裁切功能只需要聲明下ClipImageLayout即可,完全避免了上述我們描述的問題,我們對使用者屏蔽了兩個真正實現的類。這個也是自訂控制項的一種方式,希望可以藉此拋磚引玉,大家能夠更加合理的設計出自己的控制項~~
好了,我們的ClipImageLayout搞定以後,下面看下如何使用~
6、用法1、布局檔案
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#aaaaaa" > <com.zhy.view.ClipImageLayout android:id="@+id/id_clipImageLayout" android:layout_width="fill_parent" android:layout_height="fill_parent" /></RelativeLayout>
2、MainActivity
package com.zhy.clippic;import java.io.ByteArrayOutputStream;import android.app.Activity;import android.content.Intent;import android.graphics.Bitmap;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import com.zhy.view.ClipImageLayout;public class MainActivity extends Activity{private ClipImageLayout mClipImageLayout;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mClipImageLayout = (ClipImageLayout) findViewById(R.id.id_clipImageLayout);}@Overridepublic boolean onCreateOptionsMenu(Menu menu){getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item){switch (item.getItemId()){case R.id.id_action_clip:Bitmap bitmap = mClipImageLayout.clip();ByteArrayOutputStream baos = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);byte[] datas = baos.toByteArray();Intent intent = new Intent(this, ShowImageActivity.class);intent.putExtra("bitmap", datas);startActivity(intent);break;}return super.onOptionsItemSelected(item);}}
我們在menu裡面體檢了一個裁切的按鈕,點擊後把裁切好的圖片傳遞給我們的ShowImageActivity
看一下眼menu的xml
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/id_action_clip" android:icon="@drawable/actionbar_clip_icon" android:showAsAction="always|withText" android:title="裁切"/></menu>
3、ShowImageActivity
package com.zhy.clippic;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.widget.ImageView;public class ShowImageActivity extends Activity{private ImageView mImageView;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.show);mImageView = (ImageView) findViewById(R.id.id_showImage);byte[] b = getIntent().getByteArrayExtra("bitmap");Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);if (bitmap != null){mImageView.setImageBitmap(bitmap);}}}
layout/show.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" > <ImageView android:id="@+id/id_showImage" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/tbug" /></RelativeLayout>
好了,到此我們的 高仿頭像截取功能 就已經結束了~~希望大家可以從本篇部落格中可以領悟到something~
最後我們把ClipImageLayout裡面的mHorizontalPadding設定為50,貼個靜態~
ok ~~
源碼點擊下載
---------------------------------------------------------------------------------------------------------
我建了一個QQ群,方便大家交流。群號:55032675
買了一個高仿蘋果手機 讀不出來頭像 說我記憶卡拔出
儲存空間不足或者記憶卡壞掉了。