先看看图片的效果,左边是原图,右边是旋转之后的图;

 

之所以把这个写出来是因为在一个项目中需要用到这样的效果,我试过用FrameLayout布局如上的画面,然后旋转FrameLayout,随之而来也就存在了一些问题——锯齿!

在网上搜索之后,有两种方法,一是利用Paint,二是利用Canvas;
(1)、paint.setAntiAlias(true);

   paint.setFlags(Paint.ANTI_ALIAS_FLAG);

(2)、DrawFilter pfdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);

   canvas.setDrawFilter(pfdf);

而如果利用paint,或者canvas,需要从哪获取paint/canvas,这也是一个问题;

在实现的过程中,尝试过自定义FrameLayout下面的单个View{ImageView,TextView},但都以失败告终,失败的主要问题在于右图下边的文字描述无法和相片边框相对齐,而且用Matrix旋转背景之后背景大小改变,位置也不在最下边,所以就采用了单独实现一个View的方法,主要原因还是因为自身对Canvas绘图及Paint画笔不是很熟悉,所以导致的效率不高;

public class RotateTextImageView extends View {
PaintFlagsDrawFilter pfdf;
Paint paint;
Matrix matrix;
Bitmap bitmap;
int index = -1;
private int oriHeight;
private int oriWidth;
private int newHeight;
private int newWidth;
private int angle = 5;
protected Path path = new Path();
private float[] f = new float[8];
private int shawHeight = 20;
private int borderSize = 8;
Bitmap oriBitmap;
private String text = ""; public RotateTextImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initCanvasInfo();
} public RotateTextImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initCanvasInfo();
} public RotateTextImageView(Context context) {
super(context);
initCanvasInfo();
} /**
* 初始化Paint
*/
protected void initCanvasInfo() {
pfdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG);
paint = new Paint();
paint.setAntiAlias(true);
matrix = new Matrix();
matrix.setRotate(5);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.reset();
// 消除锯齿
paint.setAntiAlias(true);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
canvas.setDrawFilter(pfdf);
canvas.drawBitmap(bitmap, 0, 0, paint);
newHeight = bitmap.getHeight();
newWidth = bitmap.getWidth();
calculatePoints();
// 添加阴影
path.reset();
path.moveTo(f[0], f[1]);
path.lineTo(f[2], f[3]);
path.lineTo(f[4], f[5]);
path.lineTo(f[6], f[7]);
path.close();
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setColor(Color.parseColor("#96ffffff"));
canvas.drawPath(path, paint);
// 添加字符
if (text != null && !text.equals("")) {
path.reset();
paint.setTextSize(18);
float width = paint.measureText(text);
path.moveTo((f[0] + f[2]) / 2, (f[1] + f[3]) / 2);
path.lineTo((f[4] + f[6]) / 2, (f[5] + f[7]) / 2);
paint.setColor(Color.parseColor("#2b2b2b"));
canvas.drawTextOnPath(text, path, (oriWidth - width) / 2, 3, paint);
}
layout(0, 0, newWidth, newHeight);
} /**
* 计算坐标值
*/
private void calculatePoints() {
double a = angle * Math.PI / 180;
BigDecimal height = new BigDecimal(oriHeight);
BigDecimal width = new BigDecimal(oriWidth);
BigDecimal cos = new BigDecimal(Math.cos(a));
BigDecimal tan = new BigDecimal(Math.tan(a));
f[0] = 0;
f[1] = height.multiply(cos).floatValue();
f[2] = tan.multiply(new BigDecimal(shawHeight)).floatValue();
f[3] = (new BigDecimal(f[1])).subtract(new BigDecimal(shawHeight))
.floatValue();
f[4] = width.multiply(cos).add(new BigDecimal(f[2])).floatValue();
f[5] = new BigDecimal(newHeight - shawHeight).floatValue();
f[6] = width.multiply(cos).floatValue();
f[7] = new BigDecimal(newHeight).floatValue();
} /**
* 设置图片
*
* @param bmp
*/
public void setBitmap(Bitmap bmp) {
oriBitmap = bmp;
matrix.reset();
matrix.setRotate(angle);
Bitmap bitmapF = addFrame(bmp);
oriHeight = bitmapF.getHeight();
oriWidth = bitmapF.getWidth();
bitmap = Bitmap.createBitmap(bitmapF, 0, 0, bitmapF.getWidth(),
bitmapF.getHeight(), matrix, true);
postInvalidate();
} /**
* 旋转角度
*
* @param angle
*/
public void setAngle(int angle) {
this.angle = angle;
setBitmap(oriBitmap);
} /**
* 设置底部阴影高度
*
* @param shawHeight
*/
public void setShawHeight(int shawHeight) {
this.shawHeight = shawHeight;
postInvalidate();
} /**
* 生成添加了白色边缘的图
*
* @param bmp
* @return
*/
protected Bitmap addFrame(Bitmap bmp) {
Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize
* 2, bmp.getHeight() + borderSize * 2, bmp.getConfig());
Canvas canvas = new Canvas(bmpWithBorder);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bmp, borderSize, borderSize, null);
return bmpWithBorder;
} /**
* 设置字符串
*
* @param text
*/
public void setText(String text) {
this.text = text;
postInvalidate();
} /**
* 获取字体高度
*/
protected int getFontHeight() {
FontMetrics fm = paint.getFontMetrics();
return (int) Math.ceil(fm.descent - fm.top) + 2;
}
}

  代码解释:其实没有什么难的东西,只是一些数学运算,代码中每一个方法都有对应的功能注释。浮点型数组代表阴影层四个坐标点的八个坐标值,分别是左下、左上、右上、右下四个点,阴影层坐标计算也比较简单,但有点繁琐,就是把原图旋转之后再根据几何知识进行求解坐标!

  每次重新设置角度,设置图片,都需要重新绘制图形-->postInvalidate();

View的使用

一、xml配置文件

    <com.livingstone.RotateTextImageView
android:id="@+id/myview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="250dip"
android:minWidth="200dip"
android:paddingLeft="5dip" />

二、设置文字说明及角度、图片

        RotateTextImageView myView = (RotateTextImageView) findViewById(R.id.myview);
myView.setShawHeight(50);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.test1);
myView.setBitmap(bmp);
myView.setAngle(10);
myView.setText("这是一个测试");

Ex:获取字体宽度的两种方法
<1>.通过paint获取字体的Rect

Rect rect=newRect();
paint.getTextBounds("你好",0,1, rect);
Log.v("a:","height:"+rect.height()+"width:"+rect.width());

<2>.通过paint直接获取字体宽度

intwidth=(int)paint.measureText("你好",0,1);
Log.v("width:","width:"+width);

Android 自定义View消除锯齿实现图片旋转,添加边框及文字说明的更多相关文章

  1. 【朝花夕拾】Android自定义View篇之(三)Canvas绘制文字

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10968358.html],谢谢! 前面的文章中在介绍Canvas的时候,提到过后续单独讲Can ...

  2. Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解

    Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...

  3. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

  4. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

  5. Android自定义View

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

  6. android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索

    我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...

  7. Android自定义View和控件之一-定制属于自己的UI

    照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...

  8. android 自定义view 前的基础知识

    本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...

  9. 【Android - 自定义View】之自定义View浅析

    1.概述 Android自定义View / ViewGroup的步骤大致如下: 1) 自定义属性: 2) 选择和设置构造方法: 3) 重写onMeasure()方法: 4) 重写onDraw()方法: ...

随机推荐

  1. Ruby学习系列一,基本认识

    安装Ruby后,打开命令行,先来看下Ruby的版本. ruby -v ,如果看到类似 ruby 1.9.3p392.... ,说明我们的Ruby安装成功了. 然后我们输入 irb ,进入Ruby的交互 ...

  2. Spring缓存穿透问题修复

    本文来自网易云社区. 本剧情纯属真实,犹如雷同实乃缘分. 发生 事情的发生在某天早上,天气怎样反正是忘了,只记得当时监控平台大量的数据库错误报警. 作为后端开发,当看到日志中大量的db连接获取失败,心 ...

  3. javaweb(六)——Servlet开发(二)

    一.ServletConfig讲解 1.1.配置Servlet初始化参数 在Servlet的配置文件web.xml中,可以使用一个或多个<init-param>标签为servlet配置一些 ...

  4. ElasticSearch搜索引擎安装配置拼音插件pinyin

    近几篇ElasticSearch系列: 1.阿里云服务器Linux系统安装配置ElasticSearch搜索引擎 2.Linux系统中ElasticSearch搜索引擎安装配置Head插件 3.Ela ...

  5. Jmeter接口测试(二)工具介绍

    一.Jmeter文件目录介绍 ◆ bin:可执行文件目录 Bin 目录文件 jmeter.bat:windows 的启动文件 jmeter.log:日志文件 jmeter.sh:linux 的启动文件 ...

  6. Linux用户及权限

    库:lib 共享库:.so ,shared object, 权限: 用户,获取资源,服务的标识符 组,指派权限,标识符 进程:以某个用户的身份在进行,有属主和属组 安全上下文(security con ...

  7. Java开发工程师(Web方向) - 04.Spring框架 - 第4章.数据访问

    第4章--数据访问 Spring JDBC DAO (Data Access Object) 实现数据访问相关接口(接口和实现分离) ORM (Object Relation Mapping) 对象关 ...

  8. flume-kafka-storm-hdfs-hadoop-hbase

    # bigdata-demo 项目地址: https://github.com/windwant/bigdata-demo.git hadoop: hadoop hdfs操作 log输出到flume ...

  9. spark提交任务的两种的方法

    在学习Spark过程中,资料中介绍的提交Spark Job的方式主要有两种(我所知道的): 第一种: 通过命令行的方式提交Job,使用spark 自带的spark-submit工具提交,官网和大多数参 ...

  10. Error: Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster

    自己搭建了一套伪分布的大数据环境,运行Hadoop包中自带的示例时,出现如下错误: 错误: 找不到或无法加载主类 org.apache.hadoop.mapreduce.v2.app.MRAppMas ...