之所以做了这么一个Demo,是由于近期项目中有一个奇葩的需求:用户拍摄照片后,分享到微信的同一时候加入备注,想获取用户在微信的弹出框输入的内容。保存在自己的server上。而其实,这个内容程序是无法获取的,因此採取了一个折衷方案,将文字直接写在图片上。

首先上Demo效果图:

功能:

1.用户自由输入内容,可手动换行,而且行满也会自己主动换行。

2.可拖动改变图片中文本位置(文字不会超出图片区域)。

3.点击“生成图片”button之后,生成一张带有文字的图片文件。

代码不多,直接所有贴上了:

Activity:

/**
* 将文字写在图片中(截图方式),支持拖动文字。 <br/>
* 说明:非常明显,截图方式会减少图片的质量。假设须要保持图片质量能够使用canvas的方式。将文字直接绘制在图片之上(只是,使用此方式要实现文字拖动较为复杂)。
*/
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(); //设置文本大小
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;
}
});
} //确认,生成图片
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显示的内容(相似于截图)
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为负。向左移动时。distanceX为正
//向下移动时,distanceY为负。向上移动时。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>

<完>

Android:图片中叠加文字,支持拖动改变位置的更多相关文章

  1. android控件跟随手势滑动改变位置

    要求:1.通过手指移动来拖动图片   2.控制图片不能超出屏幕显示区域 技术点:1.MotionEvent处理2.对View进行动态定位(layout) activity_main.xml: < ...

  2. ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多

    ListView实现Item上下拖动交换位置  并且实现下拉刷新  上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...

  3. 给Jquery添加alert,prompt方法,类似系统的Alert,Prompt,可以响应键盘,支持拖动

    我们在调用系统的Alert,prompt的弹出提示时,不同的系统会有不同的提示框,视觉效果不统一,而且不好看,功能单一,现在我们通过Jquery模拟Alert,prompt,现实统一视觉效果,而且内容 ...

  4. Android技术分享-文字转语音并朗读

    Android技术分享-文字转语音并朗读 最近在做一个项目,其中有一个功能是需要将文本转换成语音并播放出来.下面我将我的做法分享一下. 非常令人开心的是,Android系统目前已经集成了TTS,提供了 ...

  5. 使用Python进行OCR -- 识别图片中的文字

    工具 Tesseract pytesseract tesserocr 朋友需要一个工具,将图片中的文字提取出来.我帮他在网上找了一些OCR的应用,都不好用.所以准备自己研究,写一个Web APP供他使 ...

  6. 如何用ABBYY FineReader提取图片中的文字

    作为OCR文字识别软件中的佼佼者,可能大家对于ABBYY FineReader的使用还不熟练,没关系,今天小编就为大家演示,如何用ABBYY FineReader这款文字识别软件,将一张截图中的文字识 ...

  7. C# 扫描并读取图片中的文字

    本文介绍如何通过C# 程序来扫描并读取图片中的文字,这里以创建一个.Net Core程序为例.下面是具体步骤,供参考. 程序测试环境: Visual Studio版本要求不低于2017 图片扫描工具: ...

  8. C# 扫描识别图片中的文字(.NET Framework)

    环境配置 本文以C#及VB.NET代码为例,介绍如何扫描并读取图片中的文字. 本次程序环境如下: Visual Studio版本要求不低于2017 图片扫描工具:Spire.OCR for .NET ...

  9. Android:让WebView支持<input type=”file”…>元素

    最近在做一个活动页面:用户上传一张图片进行缩放.旋转后点击下一步填写内容后生成图片! 做好后经过各种测试是没有问题的,基本没有什么明显BUG,流程都能走通,但是嵌入到APP后,问题就来了! 在IOS上 ...

随机推荐

  1. Web框架之Django_01初识(三大主流web框架、Django安装、Django项目创建方式及其相关配置、Django基础三件套:HttpResponse、render、redirect)

    摘要: Web框架概述 Django简介 Django项目创建 Django基础必备三件套(HttpResponse.render.redirect) 一.Web框架概述: Python三大主流Web ...

  2. php expat+DOM+SimpleXML XML读取

    XML 文件 将在我们的例子中使用下面的 XML 文件: <?xml version="1.0" encoding="ISO-8859-1"?> & ...

  3. Knockout v3.4.0 中文版教程-9-计算监控-API参考

    5.参考 下面的内容描述了如何构建和使用计算监控. 1. 构建一个计算监控 可以用如下的形式构建一个计算监控: ko.computed( evaluator [, targetObject, opti ...

  4. cf950d A Leapfrog in the Array

    考虑在位置 \(p\) 的青蛙. 如果 \(p\) 是奇数,答案显然是 \((p+1)/2\). 否则,由于未跳时 \(p\) 左边有 \(p/2\) 只,则 \(p\) 右边有 \(n-p/2\) ...

  5. wp8 longlistselector 动态加载datatemplate

    在做一个windows phone 8 即时通讯应用的时候,聊天界面的对话气泡. 需要根据不同的消息类型,加载对应的DataTemplate, 比如发送,接受,图片,语音,等气泡. 如下图所示 会话界 ...

  6. Eclipse截取android报错log

    Eclipse截取android报错log: 1.前提条件:已安装eclipse 2. LogCat界面设置: Logcat是Android 编程中一个命令行工具,可以用于得到程序的 log 信息,可 ...

  7. python第三方库之openpyxl(1)

    python第三方库之openpyxl(1) 简介 Openpyxl是一个用于读写Excel 2010 xlsx/xlsm/xltx/xltm文件的Python库,其功能非常强大.Excel表格可以理 ...

  8. 【原】缓存之 HttpRuntime.Cache

    1.HttpRuntime.Cache HttpRuntime.Cache 相当于就是一个缓存具体实现类,这个类虽然被放在了 System.Web 命名空间下了.但是非 Web 应用也是可以拿来用的. ...

  9. 九度oj题目1008:最短路径问题

    题目描述: 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. 输入:                 ...

  10. 【Luogu】P1312Mayan游戏(暴搜)

    题目链接 由于是暴搜题,所以这篇博客只讲怎么优化剪枝,以及一些细节. 模拟消除思路:因为消除可以拆分成小的横条或竖条,而这些条的长度至少为三,所以一块可消除的区域至少会有一个中心点.这里的中心点可以不 ...