Android編程實現(xiàn)支持拖動改變位置的圖片中疊加文字功能示例
本文實例講述了Android編程實現(xiàn)支持拖動改變位置的圖片中疊加文字功能。分享給大家供大家參考,具體如下:
之所以做了這么一個Demo,是因為最近項目中有一個奇葩的需求:用戶拍攝照片后,分享到微信的同時添加備注,想獲取用戶在微信的彈出框輸入的內(nèi)容,保存在自己的服務(wù)器上。而事實上,這個內(nèi)容程序是無法獲取的,因此采取了一個折衷方案,將文字直接寫在圖片上。
首先上Demo效果圖:

功能:
1.用戶自由輸入內(nèi)容,可手動換行,并且行滿也會自動換行。
2.可拖動改變圖片中文本位置(文字不會超出圖片區(qū)域)。
3.點擊“生成圖片”按鈕之后,生成一張帶有文字的圖片文件。
代碼不多,直接全部貼上了:
Activity:
/**
* 將文字寫在圖片中(截圖方式),支持拖動文字。<br/>
* 說明:很明顯,截圖方式會降低圖片的質(zhì)量。如果需要保持圖片質(zhì)量可以使用canvas的方式,將文字直接繪制在圖片之上(不過,使用此方式要實現(xiàn)文字拖動較為復(fù)雜)。
*/
public class MainActivity extends AppCompatActivity {
//圖片組件
private ImageView imageView;
//位于圖片中的文本組件
private TextView tvInImage;
//圖片和文本的父組件
private View containerView;
//父組件的尺寸
private float imageWidth, imageHeight, imagePositionX, imagePositionY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_with_text);
imageView = (ImageView) findViewById(R.id.writeText_img);
EditText editText = (EditText) findViewById(R.id.writeText_et);
tvInImage = (TextView) findViewById(R.id.writeText_image_tv);
containerView = findViewById(R.id.writeText_img_rl);
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
imageView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
imagePositionX = imageView.getX();
imagePositionY = imageView.getY();
imageWidth = imageView.getWidth();
imageHeight = imageView.getHeight();
//設(shè)置文本大小
tvInImage.setMaxWidth((int) imageWidth);
}
});
imageView.setImageBitmap(getScaledBitmap(R.mipmap.test_img));
//輸入框
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().equals("")) {
tvInImage.setVisibility(View.INVISIBLE);
} else {
tvInImage.setVisibility(View.VISIBLE);
tvInImage.setText(s);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
final GestureDetector gestureDetector = new GestureDetector(this, new SimpleGestureListenerImpl());
//移動
tvInImage.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
return true;
}
});
}
//確認(rèn),生成圖片
public void confirm(View view) {
Bitmap bm = loadBitmapFromView(containerView);
String filePath = Environment.getExternalStorageDirectory() + File.separator + "image_with_text.jpg";
try {
bm.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
Toast.makeText(this, "圖片已保存至:SD卡根目錄/image_with_text.jpg", Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//以圖片形式獲取View顯示的內(nèi)容(類似于截圖)
public static Bitmap loadBitmapFromView(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
private int count = 0;
//tvInImage的x方向和y方向移動量
private float mDx, mDy;
//移動
private class SimpleGestureListenerImpl extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//向右移動時,distanceX為負(fù);向左移動時,distanceX為正
//向下移動時,distanceY為負(fù);向上移動時,distanceY為正
count++;
mDx -= distanceX;
mDy -= distanceY;
//邊界檢查
mDx = calPosition(imagePositionX - tvInImage.getX(), imagePositionX + imageWidth - (tvInImage.getX() + tvInImage.getWidth()), mDx);
mDy = calPosition(imagePositionY - tvInImage.getY(), imagePositionY + imageHeight - (tvInImage.getY() + tvInImage.getHeight()), mDy);
//控制刷新頻率
if (count % 5 == 0) {
tvInImage.setX(tvInImage.getX() + mDx);
tvInImage.setY(tvInImage.getY() + mDy);
}
return true;
}
}
//計算正確的顯示位置(不能超出邊界)
private float calPosition(float min, float max, float current) {
if (current < min) {
return min;
}
if (current > max) {
return max;
}
return current;
}
//獲取壓縮后的bitmap
private Bitmap getScaledBitmap(int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), resId, opt);
opt.inSampleSize = Utility.calculateInSampleSize(opt, 600, 800);
opt.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), resId, opt);
}
}
一個工具類:
public class Utility {
//計算 inSampleSize 值,壓縮圖片
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<RelativeLayout
android:id="@+id/writeText_img_rl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<ImageView
android:id="@+id/writeText_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxHeight="360dp"
android:adjustViewBounds="true"
android:contentDescription="@null"/>
<TextView
android:id="@+id/writeText_image_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="#79652a"
android:clickable="true"
android:padding="4dp"
android:textColor="@android:color/white"
android:textSize="15sp" />
</RelativeLayout>
<EditText
android:id="@+id/writeText_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="添加備注" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="confirm"
android:text="生成圖片" />
</LinearLayout>
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android圖形與圖像處理技巧總結(jié)》、《Android開發(fā)入門與進(jìn)階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對大家Android程序設(shè)計有所幫助。
相關(guān)文章
Android EventBus 3.0.0 使用總結(jié)(必看篇)
下面小編就為大家?guī)硪黄狝ndroid EventBus 3.0.0 使用總結(jié)(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
關(guān)于android studio通過命令行運行g(shù)radle編譯命令的問題
這篇文章主要介紹了關(guān)于android studio通過命令行運行g(shù)radle編譯命令的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11
Android開發(fā)實現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法
這篇文章主要介紹了Android開發(fā)實現(xiàn)判斷通知欄是否打開及前往設(shè)置頁面的方法,涉及Android通知欄的打開、判斷、設(shè)置等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01
android 鍵盤事件和屏幕事件的運行原理及交互實現(xiàn)
當(dāng)在自定義View或者做游戲的時候,我們常常會用到鍵盤觸發(fā)事件和屏幕觸發(fā)事件!在自定義的View里的鍵盤觸發(fā)事件和屏幕觸發(fā)事件和activity里的鍵盤觸發(fā)事件和屏幕觸發(fā)事件是怎么樣交互的呢,接下來為您詳細(xì)介紹,感興趣的朋友可以了解下哦2013-01-01
Android實現(xiàn)購物車及其他功能的角標(biāo)
本文主要介紹了Android實現(xiàn)購物車及其他功能的角標(biāo)的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04

