Android 自定义View消除锯齿实现图片旋转,添加边框及文字说明
先看看图片的效果,左边是原图,右边是旋转之后的图;
 
之所以把这个写出来是因为在一个项目中需要用到这样的效果,我试过用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消除锯齿实现图片旋转,添加边框及文字说明的更多相关文章
- 【朝花夕拾】Android自定义View篇之(三)Canvas绘制文字
		
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10968358.html],谢谢! 前面的文章中在介绍Canvas的时候,提到过后续单独讲Can ...
 - Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解
		
Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...
 - Android 自定义 view(三)—— onDraw 方法理解
		
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
 - [原] Android 自定义View步骤
		
例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...
 - Android自定义View
		
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...
 - android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
		
我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...
 - Android自定义View和控件之一-定制属于自己的UI
		
照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...
 - android 自定义view 前的基础知识
		
本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...
 - 【Android - 自定义View】之自定义View浅析
		
1.概述 Android自定义View / ViewGroup的步骤大致如下: 1) 自定义属性: 2) 选择和设置构造方法: 3) 重写onMeasure()方法: 4) 重写onDraw()方法: ...
 
随机推荐
- spring源码-增强容器xml解析-3.1
			
一.ApplicationContext的xml解析工作是通过ClassPathXmlApplicationContext来实现的,其实看过ClassPathXmlApplicationContext ...
 - js中call()方法和apply方法的使用
			
1. 方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call ...
 - MySQL高级-性能分析Explain
			
1.使用Explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的.分析你的查询语句或是表结构的性能瓶颈 . 2.执行方法:Explain + SQL语句 解释 ...
 - 基于Docker的UI自动化初探
			
本文来自网易云社区 前言 一直以来,项目迭代的时间都是比较紧张的,开发加班加点coding,测试加班加点提bug.都说"时间像海绵里的水,挤挤总会有的"(当然这里的"挤挤 ...
 - TraceHelper
			
public class TraceHelper { private static TraceHelper _traceHelper; private TraceHelper() { } public ...
 - vim分屏功能总结
			
vim的分屏功能 总结起来,基本都是ctrl+w然后加上某一个按键字母,触发一个功能.(1)在shell里打开几个文件并且分屏: vim -On file1 file2 ... vim -on fil ...
 - appium -- 页面出现弹窗,关闭后,无法识别页面元素
			
1. 问题:如图所示:在修改手势密码的过程中,点击了返回按钮后,弹出该弹窗:点击继续设置后,就发现 driver.getPageSource()获取不到页面元素.在找了一圈无用的资料后,没有什么好的处 ...
 - 前端开发工程师 - 03.DOM编程艺术 - 第1章.基础篇(上)
			
第1章.基础篇(上) Abstract:文档树.节点操作.属性操作.样式操作.事件 DOM (Document Object Model) - 文档对象模型 以对象的方式来表示对应的html,它有一系 ...
 - Python3获取新浪微博内容乱码问题
			
用python获取新浪微博最近发布内容的时候调用 public_timeline()函数的返回值是个jsonDict对象,首先需要将该对象通过json.dumps函数转换成字符串,然后对该字符串用GB ...
 - 头文件#ifndef #define #endif使用
			
想必很多人都看过“头文件中的 #ifndef #define #endif 防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?是不能在不同的两个文件中使用include来包含这个头文件 ...