編輯:關於Android編程
最近一直的工作是手機上的DIY操作,功能很多網上資料不多,將最近遇到的功能分解成模塊 今天介紹的是圖片(我這裡也可以是任意View 常見的還有Edittext需要這樣的功能 指定區域多圖片合成 放大 縮小 鏡像 旋轉) 一一分享,當然做法有很多種,我分享其中嘗試後最簡單的一種有做DIY的同學可以加我一起討論。除了美顏沒有那麼牛B的算法外 基本功能都設計到了
慣例先看效果圖

// 注意做類似這種模板功能時候 方位由後台數據提供,這裡我們用假數據 4個點 或者xy 加區域來做示例
//一開始我們公司用的是透明蓋住 操作圖片 但發現 局限性較大。後來直接限定區域。將操作圖片層級移到模板圖上面 隨意疊加
1. 背景圖上繪制操作區域,(操作區域可以不止一個,比如美圖秀秀的模板)
2. 操作區域內加上 編輯素材圖的容器與編輯區域相同
3. 添加可編輯的view(這裡方便觀看用的ImageView)
4. 合成將你想要的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();
}
自定義的FrameLayout
ImagesTemplates帶的布局 View view = View.inflate(mContext, R.layout.view_images_templates, null);
addView(view);
簡單形成一個組合控件。
<framelayout android:background="#FFF" android:id="@+id/flRoot" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<framelayout android:background="#50654786" android:clipchildren="false" android:id="@+id/flZone" android:layout_height="wrap_content" android:layout_width="wrap_content">
</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.
當然你可以一個ImageView畫4個bitmap搞定 但是後面我否定了這個方法 重寫了這種比較直觀的4個ImageView 作為按鈕蓋在主圖View方法,中間的View可以替換。也方便理解,簡化了後面的步驟。
R.layout.add_img_item
<framelayout android:id="@+id/fl" android:layout_height="wrap_content" android:layout_width="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android">
</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;
}
RecyclerView的通用適配器的高級使用
前言博主由於項目中頻繁的使用了V7包中的RecyclerView來代替ListView的列表展示,所以抽空基於ListView的通用適配器的原理,給RecyclerVie
Android給自定義按鍵添加廣播和通過廣播給當前焦點輸入框賦值
一、給自定義按鍵添加廣播修改PhoneWindowManager.java中的interceptKeyBeforeDispatching方法/frameworks/bas
android拍照圖片選取與圖片剪裁
最近從以前的項目中扒下來一個常用的模塊,在這裡有必要記錄一下的,就是android上獲取圖片以及裁剪圖片,怎麼樣?這個功能是不是很常用啊,你隨便打開一個Ap
移動一卡多號業務開通/價錢/APP使用指南
中國移動推出的一卡多號業務可以在已有移動手機號上增加1-3個副號,不用換機、換卡。特別適合想擁有多個手機號碼的用戶或需要保護隱私的用戶服務。在不增加手機、不