项目地址:https://github.com/coolstar1204/MakePoster

今天主要讲一下项目主要控件,文字排版控件组,实现类似QQ音乐歌词海报效果。

控件主要功能点

  • 可设置背景图片
  • 可设置标题文字,并支持标题文字自动居中、超长自动…
  • 可设置图片颜色效果,实现黑白、旧照片、变暗、变亮等效果(有些效果还不太理想)
  • 可增加多行自定义文字、支持文字设置阴影、颜色、大小、居中居右居左等对齐通用设置,支持文字特效
    排版(横向、竖向、不同行等宽不同字体大小效果等)

控件设计结构

核心类

IPoster:字义了排版功能,直接子类有DiffSizePoster、HorizontalPoster、VerticalPoster三个类。
IBmpDrawer:定义了背景图绘制功能,实现类目前只有BmpDrawer,支持ColorMatrix设置
TextDrawer:控件基类,继承于View控件,内部管理上二个接口实现类、增加了标题栏和Logo的绘制功能、导出海报图片功能。

TextDrawer

核心函数有:

 @Overrideprotected void onDraw(Canvas canvas) {onDrawBackBmp(canvas);//画文字,文字位置使用scroll变量偏移,达到拖动效果canvas.save();canvas.translate(mScrollX,mScrollY);if(poster!=null){poster.onPostDraw(canvas);}canvas.restore();//画标题文字与酷我logodrawLogoAndTitle(canvas);  //画标题和logo}
 private float drawTitle(float logoTop, Canvas canvas) {if(!TextUtils.isEmpty(kuwoMusicInfo)){float titleTargetWidth = bmpDrawer.getBmpScaleRect().width()-mTitleMarginValue-mTitleMarginValue; //有时有缩放显示的情况,文字也要同步变窄,默认是和控件一样宽float txtHeight = titlePaint.getFontMetrics().bottom-titlePaint.getFontMetrics().top;float txtTop = logoTop-txtHeight-V_SAPCE*2; //文字高度是在图标上方,文字高度上下各留20间距的区域中,居中显示RectF txtRect = new RectF(mTitleMarginValue,txtTop,getWidth()-mTitleMarginValue,logoTop);Paint.FontMetricsInt fontMetrics = titlePaint.getFontMetricsInt();//判断字符串长度是不是超长,超长转为...String drawTxt = kuwoMusicInfo;float txtWidth = titlePaint.measureText(kuwoMusicInfo);if(txtWidth>titleTargetWidth){float tailWidth = titlePaint.measureText("...");float[] wordWidths = new float[kuwoMusicInfo.length()];titlePaint.getTextWidths(kuwoMusicInfo,wordWidths);float tmpWidth = tailWidth;int wordIdx = kuwoMusicInfo.length()-1;  //默认是显示全部字符for(int i=0;i<wordWidths.length;i++){if((tmpWidth+wordWidths[i])>titleTargetWidth){wordIdx = i;break;}else{tmpWidth += wordWidths[i];}}drawTxt = kuwoMusicInfo.substring(0,wordIdx)+"...";  //截取一部分标题内容}float baseline = (txtRect.bottom + txtRect.top - fontMetrics.bottom - fontMetrics.top) / 2;// 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()titlePaint.setTextAlign(Paint.Align.CENTER);canvas.drawText(drawTxt, txtRect.centerX(), baseline, titlePaint);
//            canvas.drawRect(txtRect,titlePaint);return getHeight() - txtRect.top; //高度减去文字的上边坐标,得到文字与logo的总高度}return 0f;}
 private Bitmap innerBuildPost(String filePath, int outWidth, int outHeight, boolean saveFile){if(saveFile&& FileUtils.isExist(filePath)){if(false == FileUtils.deleteFile(filePath)){return null;}}try {Bitmap outBmp = Bitmap.createBitmap(outWidth,outHeight, Bitmap.Config.ARGB_8888);LogMgr.d("PostBmp","width:"+outWidth+",Height:"+outWidth);Canvas canvas = new Canvas(outBmp);ImageView.ScaleType oldScaleType =  bmpDrawer.getBmpScaleType();bmpDrawer.outputDraw(canvas,outWidth,outHeight);float scaleX = outWidth*1.0f/getWidth();  //获取屏幕与真实图片的比例float scaleY = outHeight*1.0f/getHeight();  //获取屏幕与真实图片的比例LogMgr.d("PostBmp","scaleX:"+scaleX+",scaleY:"+scaleY);Matrix matrix = canvas.getMatrix();canvas.save();if(Math.abs(scaleX-1.0f)>0.01|| Math.abs(scaleY-1.0f)>0.01){matrix.setScale(scaleX,scaleY,0,0); //缩放处理显示与原图的位置关系canvas.setMatrix(matrix);}if(poster!=null){canvas.save();canvas.translate(mScrollX,mScrollY);poster.onPostDraw(canvas);canvas.restore();}drawLogoAndTitle(canvas);canvas.restore();if(saveFile){  //如果设置要保存文件,则保存到本地sd卡中,如果不要,则直接返回bitmap对象File bmpFile = new File(filePath);FileOutputStream fout = new FileOutputStream(bmpFile);outBmp.compress(Bitmap.CompressFormat.JPEG,100,fout);fout.flush();fout.close();}return outBmp;} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (Throwable e){System.gc();e.printStackTrace();}return null;}

HorizontalPoster

本类实现文字的横向排版、运行多行的三种对齐方式,同时其子类实现了跳动文字效果、下划线效果、带符号线条装饰效果等。其核心函数是各类中的

 public void updateTextRect(int parentWidth, int parentHeight) {Log.d("Poster","------------updateTextRect-----------");if(textList!=null&&textList.length>0){if(drawTextList==null){drawTextList = new ArrayList<TxtRowInfo>(textList.length*2); //默认设置是歌词行数2倍,不能每个都换行吧!}drawTextList.clear();Paint.FontMetrics pfm = textPaint.getFontMetrics();txtHeight = pfm.descent-pfm.ascent;   //保存现有字号下单个字的高度maxWidth = 0;     //保存最大宽度float totalHeight=-pfm.top;    //保存总高度totalHeight+=margin_Top;  //加上上面的空隙float rawWidth;     //保存每一行的宽度for(int i=0;i<textList.length;i++){rawWidth = textPaint.measureText(textList[i]);if(rawWidth>(parentWidth-margin_Left-margin_Right)){float[] chayWidths = new float[textList[i].length()];textPaint.getTextWidths(textList[i],0,textList[i].length(),chayWidths);float rowTmpWidth = 0f;int startIdx = 0;for(int j=0;j<chayWidths.length;j++){if(rowTmpWidth+chayWidths[j]>(parentWidth-margin_Left-margin_Right)){ //如果加上当前的字符,超过父控件宽度了。则不加当前控件,换行TxtRowInfo item = new TxtRowInfo();item.rowText = textList[i].substring(startIdx,j);item.rowWidth = rowTmpWidth;item.startTop = totalHeight;drawTextList.add(item);maxWidth = Math.max(maxWidth,rowTmpWidth); //得到最大宽度totalHeight+=(txtHeight+ROW_SPACE); //增加行高度rowTmpWidth = chayWidths[j]; //重新计算新行宽度startIdx = j;//保存换行的起始字符}else{rowTmpWidth+=chayWidths[j];}}TxtRowInfo item = new TxtRowInfo();item.rowText = textList[i].substring(startIdx);item.rowWidth = rowTmpWidth;item.startTop = totalHeight;drawTextList.add(item);maxWidth = Math.max(maxWidth,rowTmpWidth); //得到最大宽度totalHeight+=(txtHeight+ROW_SPACE); //增加行高度}else{TxtRowInfo info = new TxtRowInfo();info.rowText = textList[i];info.rowWidth = rawWidth;info.startTop = totalHeight;drawTextList.add(info);totalHeight +=(txtHeight+ROW_SPACE);maxWidth = Math.max(maxWidth,rawWidth); //得到最大宽度}}totalHeight = totalHeight+pfm.ascent-ROW_SPACE; //此处要减去开始直接设置的文字top值与ascent的差值,保持上面文字边缘空白相同totalHeight += margin_Bottom;  //加上下面要保留的空隙updateDrawTextLeft(maxWidth);textRect.set(0,0,margin_Left+maxWidth+margin_Right,totalHeight);  //保存文字显示区域}else{if(drawTextList!=null){drawTextList.clear();drawTextList = null;}}}

VerticalPoster

本类实现了文字的竖向排版,其子类也是实现了装饰线效果、跳动文字效果等。核心函数

 public void updateTextRect(int parentWidth, int parentHeight) {Log.d("Poster","------------updateTextRect-----------");if(textList!=null&&textList.length>0){if(drawTextList==null){drawTextList = new ArrayList<TxtRowInfo>(128); //默认设置是歌词行数2倍,不能每个都换行吧!}drawTextList.clear();Paint.FontMetrics pfm = textPaint.getFontMetrics();txtHeight = pfm.bottom-pfm.top;   //保存现有字号下单个字的高度maxHeight = 0;     //保存最大高度float maxRowWidth = textPaint.measureText("国"); //默认最宽的字符就是中文,数字与字母都比中文窄float startLeft = 0;float startTop =ROW_SPACE+(-pfm.top);int colCount = 1;  //默认肯定有第一列float totalWidth=0;    //保存总高度for(int i=0;i<textList.length;i++){float[] rawWidths = new float[textList[i].length()];     //保存每一行的每个字符的宽度数组textPaint.getTextWidths(textList[i],rawWidths);int rawNo = 0;  //记录换行的个数for(int j=0;j<textList[i].length();j++){startLeft = parentWidth - colCount*(maxRowWidth+COL_SPACE);startTop  = ROW_SPACE+(-pfm.top) + (j-rawNo)*txtHeight;//因为top为负数,则要先取负再加上if((startTop+txtHeight)>parentHeight){ //如果测试发现下一个字符超过边界,则换行rawNo = j;  //保存换行的这个字符indexupdateColHeight(colCount,(startTop+pfm.bottom+ROW_SPACE));colCount++;startTop = ROW_SPACE+(-pfm.top);startLeft = parentWidth - colCount*(maxRowWidth+COL_SPACE); //换列要重新计算一下}maxHeight = Math.max(maxHeight,(startTop+pfm.bottom+ROW_SPACE)); //保存最高的列,用于更新字符RectTxtRowInfo item = new TxtRowInfo();item.rowText = String.valueOf(textList[i].charAt(j));item.startLeft = startLeft+(maxRowWidth-rawWidths[j])/2; //把窄的字符要居中,所以这里要处理起点item.startTop = startTop;item.colIndex = colCount;   //保存此字所在列位置,用于列对齐时更新起点做条件item.rowWidth = rawWidths[j];drawTextList.add(item);}updateColHeight(colCount,(startTop+pfm.bottom+ROW_SPACE));colCount++;}totalWidth = (colCount-1)*(maxRowWidth+COL_SPACE)+COL_SPACE; //此处要加上最左一行左边的空白区域updateDrawTextTop(maxHeight);updateVerTextLeft(parentWidth,totalWidth);textRect.set(0,0,totalWidth,maxHeight);  //保存文字显示区域}else{if(drawTextList!=null){drawTextList.clear();drawTextList = null;}}}

DiffSizePoster

本类实现多行文字时,各行等宽、字体大小不同的混合排版效果。

核心函数:

 public void updateTextRect(int parentWidth, int parentHeight) {if(textList!=null&&textList.length>0){if(drawTextList==null){drawTextList = new ArrayList<DiffRowInfo>();}drawTextList.clear();float totalHeight = 0;for(int i=0;i<textList.length;i++){float curTextSize = textPaint.getTextSize();float rowTotalWidth = textPaint.measureText(textList[i]);if(rowTotalWidth>parentWidth){//缩小字号,直到小于父控件while (rowTotalWidth>parentWidth){textPaint.setTextSize(--curTextSize);rowTotalWidth = textPaint.measureText(textList[i]);}}else{//扩大字号,直到大于父控件,然后获取前一字号值while (rowTotalWidth<parentWidth){textPaint.setTextSize(++curTextSize);rowTotalWidth = textPaint.measureText(textList[i]);}curTextSize--; //大于时才循环停止,所以这里要再减去最后大于的字号值,还原到小于宽度范围内textPaint.setTextSize(curTextSize);rowTotalWidth = textPaint.measureText(textList[i]);}
//                float maxRowWidth = textPaint.measureText("国"); //保存每个汉字标准的宽度float maxRowHeight = textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top;float totoalWidth = 0;if(totalHeight<0.1f){totalHeight += (maxRowHeight-(textPaint.getFontMetrics().bottom)); //保存上面所有行高,用于本行的top定位,因为文字的baseline在文字中下部,所以这里现减去bottom值,把y定位到baseline上}else{totalHeight += (maxRowHeight-(textPaint.getFontMetrics().bottom)+HEIGHT_SPACE); //保存上面所有行高,用于本行的top定位,因为文字的baseline在文字中下部,所以这里现减去bottom值,把y定位到baseline上}if(totalHeight>parentHeight){break;}float[] rowWidths = new float[textList[i].length()]; //保存每个字符的宽度,textPaint.getTextWidths(textList[i],rowWidths);float leftOffet = (parentWidth-rowTotalWidth)/2; //保存宽度差的一半,做为行首偏移量,实现居中效果for(int j=0;j<textList[i].length();j++){DiffRowInfo item = new DiffRowInfo();item.rowText = ""+textList[i].charAt(j);item.startTop =totalHeight; //item.startLeft = leftOffet + totoalWidth;item.fontSize = curTextSize;drawTextList.add(item);totoalWidth += rowWidths[j];}}totalHeight = Math.min(totalHeight+textPaint.getFontMetrics().bottom+HEIGHT_SPACE,parentHeight);  //最高不能高过父控件高度textRect.set(0,0,parentWidth,totalHeight);}else{if(drawTextList!=null){drawTextList.clear();drawTextList = null;}}}

BmpDrawer

本类主要是用于绘制背景时,可设置ColorMatrix,达到改变图像颜色效果
核心代码是:

bgPaint.setColorFilter(new ColorMatrixColorFilter(bgColorMatrix));

同时模仿系统ImageView,支持了几种ScaleType的绘制

更多细节请去github上查看代码

本控件因为工作项目需要编写,功能比较独立、所以分享出来,希望能抛砖引玉,大牛们要是看到在结构上有不合理的地方,多指点:)

Android图片海报制作-自定义文字排版控件组件相关推荐

  1. Android图片海报制作软件开发实践

    Android图片海报制作软件开发实践 项目地址:https://github.com/coolstar1204/MakePoster 本博客分以下几个文章,从头记录我学习Material Desig ...

  2. android富文本图片自适应,Android 图片混排富文本编辑器控件

    一.一个Android 图片混排富文本编辑器控件(仿兴趣部落) 1.1 图片混排富文本控件 是一种图片和文字混合在一起的控件,文本之间可以插入图片,类似于网页的排版样式. 1.2 该控件主要是仿兴趣部 ...

  3. Android 图片混排富文本编辑器控件

    概述 一个Android 图片混排富文本编辑器控件(仿兴趣部落) 详细 代码下载:http://www.demodashi.com/demo/12032.html 一.一个Android 图片混排富文 ...

  4. Android图片海报制作-MaterialDesign使用

    项目地址:https://github.com/coolstar1204/MakePoster MaterialDesign界面 这个界面控件出来也有好久了,可实际生产项目中,因为兼容老旧手机的需要, ...

  5. android 设置文字大小控件

    自定义设置文字大小控件 1.效果图 2.自定义文字设置控件 public class TextSizeSelector extends View {private int margingLeft;pr ...

  6. Angular19 自定义表单控件

    1 需求 当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件:自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互 2 官方文档 -> 点击前 ...

  7. android自定义xml弹窗,Android自定义弹窗提醒控件使用详解

    Android中原生的Dialog弹窗提醒控件样式单一,有时候并不能满足我们的项目需求,而且一个工程里面有时候会在多处都用到弹窗提醒的功能,代码会出现大量的冗余,工作之余,就自己实现了这么一个弹窗提醒 ...

  8. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  9. 【Android自定义View实战】之自定义评价打分控件RatingBar,可以自定义星星大小和间距...

    [Android自定义View实战]之自定义评价打分控件RatingBar,可以自定义星星大小和间距

最新文章

  1. linux系统photoshop安装教程,在ubuntu中安装photoshop cs6
  2. k-means优化 k-means距离的选择 k-medoids对比
  3. 剑指Offer(Java实现)把字符串转换成整数
  4. php join a.id b.id,mysql,sql_MySQL A left join B on B.cid=A.id 左链接查询失败,求解,mysql,sql - phpStudy...
  5. 华南理工专科计算机随堂联系,华南理工大学网络教育计算机基础随堂练习第三章...
  6. C++(22)--继承和派生
  7. 以串结构存储c语言版,数据结构C语言版 串的块链存储表示和实现
  8. 通俗易懂,先来认识一下Vuex
  9. 关于ArcGIS Mobile回传数据中常遇到的问题整理!
  10. 网上第3方软件教程摘选
  11. flash xml+textArea组件+CSS
  12. 通过反编译深入理解Java String及intern
  13. Tomcat之—— linux/centos 解决Tomcat内存溢出
  14. Linux --配置网络(通过网络访问服务器)
  15. 英语四级——常考语法【不断更新中】
  16. python ip地址处理_Python学习笔记-IP地址处理模块Ipy
  17. 北邮计院数电第五章——VHDL语言
  18. java rgb cmyk_Java CMYK图片转RGB图片(TwelveMonkeys方式)
  19. 到底什么是SDWAN服务?
  20. ElementUI使用表格如何显示图片?

热门文章

  1. 二元置信椭圆r语言_一般加性模型的简介、应用举例及R语言操作
  2. DPL常用激活函数求导
  3. 树莓派配置无线连接与无线AP热点
  4. 【JY】土木工程的我们CAE应该怎么学?
  5. python远程连接ssh_Python实现SSH连接远程服务器
  6. 【Carsim学习】Carsim与Simulink关联问题的解决
  7. 物料描述模板技术解析及10个典型行业实践示例
  8. Elasticsearch基础1——搜索引擎发展史和工作流程、es/es-head/kibana的基础安装
  9. 爬虫之Selenium模块
  10. 领英如何设置好友谁可见