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

 

之所以把这个写出来是因为在一个项目中需要用到这样的效果,我试过用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. SpringCloud Eureka 服务注册与服务发现

    一.Eureka简介 spring Cloud Netflix技术栈中,Eureka作为服务注册中心对整个微服务架构起着最核心的整合作用.有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只 ...

  2. oracle分区表按时间自动创建

    表分区是一种思想,分区表示一种技术实现.当表的大小过G的时候可以考虑进行表分区,提高查询效率,均衡IO.oracle分区表是oracle数据库提供的一种表分区的实现形式.表进行分区后,逻辑上仍然是一张 ...

  3. L010 linux命令及基础手把手实战总结

    一转眼都快两周没更新了,最近实在太忙了,这两周的时间断断续续的把L010学完了,短短的15节课,确是把前10节的课程全部的运用一遍,从笔记到整理,再到重新理解,最后发布到微博,也确实提升了一些综合性能 ...

  4. 微信小程序—day02

    全局配置 在app.json中,对小程序进行全局配置.官方文档 tabBar是对底部/顶部导航栏的配置,图片的icon 大小限制为40kb,建议尺寸为 81px * 81px 去阿里矢量图网站,找到图 ...

  5. fiddler不经意的功能

    捕获指定客户端的请求,直接食用 窗口分离,直接食用 Hide this column  隐藏此列Ensure all columns are visible   显示默认所有列Customize co ...

  6. Educational Codeforces Round 32 Problem 888C - K-Dominant Character

    1) Link to the problem: http://codeforces.com/contest/888/problem/C 2) Description: You are given a ...

  7. Docker: 如何修改 Docker 的镜像存储位置

    我用的阿里云的服务器, 但是系统盘只有20G, 默认 Docker 的镜像文件是安装在/var/lib 目录下的, 这样的话我根本装不了太多的镜像... 这个必须得改改... 搜了下, 解决方案如下: ...

  8. 关于ES6-{块级作用域 let const 解构赋值 数组 字符串 函数的扩展 箭头函数}

    关于ES6 块级作用域 任何一对花括号({})中的语句集都属于一个块,在块中声明的变量在代码块外都是不可访问的,称之为块级作用域,ES5以前没有块级作用域 let let 是ES6新增的声明变量的一种 ...

  9. Android 开发 之 JNI入门 - NDK从入门到精通

    NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub - https://github.com/han1202012/NDKHelloworld.git -- Java传递参数给C ...

  10. iOS-Masonry用法

    __weak typeof(self) weakSelf = self; UIView * tempView = [[UIView alloc]init]; NSInteger count = ;// ...