转自 http://wuxiaolong.me/2016/08/20/Paint/
了解Android Paint,一篇就够。引用Aige《自定义控件其实很简单》系列博客的话“很多时候你压根不需要了解太多原理,只需站在巨人的丁丁上即可”,所谓前人种树后人好乘凉,这里记录下我的实践结果。
我们可以通过Paint中setter方法来为画笔设置属性:



浩浩荡荡来将这些方法一一过一遍:
set
为当前画笔copy一个画笔
setARGB
- 1
|
- void setARGB(int a, int r, int g, int b)
|
设置Paint对象颜色,a代表透明度,r,g,b代表颜色值
插播:RGB与十六进制区别
一般在xml里定义颜色可以直接写:
- 1
|
- android:textColor="#FF6281"
|
但是在code代码中就必须写成这样:
- 1
|
- text.setTextColor(0xffff6281);
|
xml中透明度写不写无所谓,默认是ff不透明,但是代码中用十六进制0x来表示,就必须跟上ff透明度,不然会默认00全透明。
setAlpha
设置alpha透明度,范围为0~255
setAntiAlias
- 1
|
- void setAntiAlias(boolean aa)
|
是否抗锯齿
setColor
- 1
|
- void setColor(int color)
|
设置paint颜色
setColorFilter
- 1
|
- ColorFilter setColorFilter (ColorFilter filter)
|
设置颜色过滤,ColorFilter有三个子类去实现ColorMatrixColorFilter、LightingColorFilter和PorterDuffColorFilter
ColorMatrixColorFilter
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
- public class PaintCanvas extends View {
private Paint mPaint; //省略构造方法 private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); // 生成色彩矩阵 ColorMatrix colorMatrix = new ColorMatrix(new float[]{ 0.5F, 0, 0, 0, 0, 0, 0.5F, 0, 0, 0, 0, 0, 0.5F, 0, 0, 0, 0, 0, 1, 0, }); mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); } -
- @Override
protected void onDraw(Canvas canvas) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.logo); canvas.drawBitmap(bitmap, 0, 0, mPaint); } -
- }
|
第一行表示的R(红色)的向量,第二行表示的G(绿色)的向量,第三行表示的B(蓝色)的向量,最后一行表示A(透明度)的向量,这一顺序必须要正确不能混淆!这个矩阵不同的位置表示的RGBA值,其范围在0.0F至2.0F之间,1为保持原图的RGB值。每一行的第五列数字表示偏移值。

这是原图效果,增加ColorMatrix,效果如下:

LightingColorFilter
只有一个构造方法,LightingColorFilter (int mul, int add)
,参数1:mul全称是colorMultiply意为色彩倍增;参数2:add全称是colorAdd意为色彩添加,这两个值都是16进制的色彩值0xAARRGGBB。
- 1
2
|
- // 设置颜色过滤,去掉绿色
mPaint.setColorFilter(new LightingColorFilter(0xFFFF00FF, 0x00000000));
|
效果如下:

PorterDuffColorFilter
也只有一个构造方法,PorterDuffColorFilter (int color, PorterDuff.Mode mode)
,参数1:16进制表示的颜色值;参数2:PorterDuff内部类Mode中的一个常量值,这个值表示混合模式。
- 1
2
|
- // 设置颜色过滤,Color的值设为红色,模式PorterDuff.Mode.DARKEN变暗
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN));
|
效果如下:

混合模式还有很多,不仅是应用于图像色彩混合,还应用于图形混合。
setDither
- 1
|
- void setDither(boolean dither)
|
设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
setFakeBoldText
- 1
|
- void setFakeBoldText(boolean fakeBoldText)
|
设置伪粗体文本
setFilterBitmap
- 1
|
- void setFilterBitmap(boolean filter)
|
设置位图进行滤波处理
setHinting
- 1
|
- void setHinting (int mode)
|
Added in API level 14,设置暗示模式,HINTING_OFF 或 HINTING_ON
setLetterSpacing
- 1
|
- void setLetterSpacing (float letterSpacing)
|
Added in API level 21,设置文本字母间距,默认0,负值收紧文本
setLinearText
- 1
|
- void setLinearText(boolean linearText)
|
设置线性文本
setMaskFilter
- 1
|
- MaskFilter setMaskFilter (MaskFilter maskfilter)
|
设置滤镜的效果,MaskFilter有两个子类实现BlurMaskFilter, EmbossMaskFilter
BlurMaskFilter
设置画笔模糊阴影效果
- 1
|
- mPaint.setMaskFilter(new BlurMaskFilter(20f, BlurMaskFilter.Blur.SOLID));
|
参数1:模糊延伸半径,必须>0;
参数2:有四种枚举
NORMAL,同时绘制图形本身内容+内阴影+外阴影,正常阴影效果
INNER,绘制图形内容本身+内阴影,不绘制外阴影
OUTER,不绘制图形内容以及内阴影,只绘制外阴影
SOLID,只绘制外阴影和图形内容本身,不绘制内阴影
BlurMaskFilter绘制的Bitmap基本完全不受影响
四种枚举效果如下:




EmbossMaskFilter
- 1
2 3 4 5 6 7 8
|
- //Paint的setMaskFilter不被GPU支持,为了确保画笔的setMaskFilter能供起效,我们需要禁用掉GPU硬件加速或AndroidManifest.xml文件中设置android:hardwareAccelerated为false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { //View从API Level 11才加入setLayerType方法 //设置软件渲染模式绘图 this.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } //设置浮雕滤镜效果,参数1:光源指定方向;参数2:环境光亮度,取值0-1,值越小越暗;参数3:镜面高光反射系数,值越小反射越强;参数4:模糊延伸半径 mPaint.setMaskFilter(new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 8f, 3f));
|
效果如下:

setPathEffect
- 1
|
- PathEffect setPathEffect(PathEffect effect)
|
设置路径效果,PathEffect有6个子类实现ComposePathEffect, CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect
具体代码:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
- public class PaintCanvas extends View {
private Paint mPaint; // 路径对象 private Path mPath; private PathEffect[] pathEffects = new PathEffect[7]; private float mPhase=5; -
- //省略构造方法
-
- private void init() {
mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPaint.setAntiAlias(true); initPath(); -
- }
-
- private void initPath() {
// 实例化路径 mPath = new Path(); // 定义路径的起点 mPath.moveTo(10, 50); -
- // 定义路径的各个点
for (int i = 0; i <= 30; i++) { mPath.lineTo(i * 35, (float) (Math.random() * 100)); } //什么都不处理 pathEffects[0] = null; //参数1:线段之间的圆滑程度 pathEffects[1] = new CornerPathEffect(10); //参数1:间隔线条长度(length>=2),如float[] {20, 10}的偶数参数20定义了我们第一条实线的长度, //而奇数参数10则表示第一条虚线的长度,后面不再有数据则重复第一个数以此往复循环;参数2:虚实线间距 pathEffects[2] = new DashPathEffect(new float[]{20, 10}, mPhase); //参数1:值越小杂点越密集;参数2:杂点突出的大小,值越大突出的距离越大 pathEffects[3] = new DiscretePathEffect(5.0f, 10.0f); Path path = new Path(); path.addRect(0, 0, 8, 8, Path.Direction.CCW); //定义路径虚线的样式,参数1:path;参数2:实线的长度;参数3:虚实线间距 pathEffects[4] = new PathDashPathEffect(path, 20, mPhase, PathDashPathEffect.Style.ROTATE); pathEffects[5] = new ComposePathEffect(pathEffects[2], pathEffects[4]); //ComposePathEffect和SumPathEffect都可以用来组合两种路径效果,具体区别(不知道如何描述)小伙伴们自己试试 pathEffects[6] = new SumPathEffect(pathEffects[4], pathEffects[3]); } -
- @Override
protected void onDraw(Canvas canvas) { /* * 绘制路径 */ for (int i = 0; i < pathEffects.length; i++) { mPaint.setPathEffect(pathEffects[i]); canvas.drawPath(mPath, mPaint); // 每绘制一条将画布向下平移250个像素 canvas.translate(0, 250); } } -
- }
|
效果如下:

setRasterizer
Rasterizer setRasterizer(Rasterizer rasterizer)
设置光栅化,API21已过时
setShader
- 1
|
- Shader setShader(Shader shader)
|
设置着色器,Shader 子类实现有BitmapShader, ComposeShader, LinearGradient, RadialGradient, SweepGradient
BitmapShader
对图形进行渲染,构造方法:
- 1
|
- BitmapShader (Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)
|
tileX、tileY参数Shader.TileMode有三个:
CLAMP 重复最后一个颜色至最后
MIRROR 重复着色的图像水平或垂直方向已镜像方式填充会有翻转效果
REPEAT 重复着色的图像水平或垂直方向
设置tileX、tileY为Shader.TileMode.CLAMP
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
- public class PaintCanvas extends View {
private Paint mPaint; private Context mContext; private Bitmap mBitmap; private BitmapShader mShader; -
- // 省略构造方法
-
- private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo); mShader= new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint = new Paint(); mPaint.setAntiAlias(true); } -
- @Override
protected void onDraw(Canvas canvas) { mPaint.setShader(mBitmapShader); canvas.drawCircle(500, 550, 500, mPaint); } -
- }
|
效果如下:

设置tileX、tileY为Shader.TileMode.MIRROR
效果如下:

设置tileX、tileY为Shader.TileMode.REPEAT
效果如下:

LinearGradient
设置线性渐变效果,有两个构造函数
- 1
2 3
|
- //坐标(x0,y0)渐变直线的起点,坐标(x1,y1)渐变直线的终点,color0和color1分别表示了渐变的起始颜色和终止颜色,TileMode也有CLAMP 、REPEAT 和 MIRROR三个取值
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions,Shader.TileMode tile)
|
例子:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
- public class PaintCanvas extends View {
private Paint mPaint; private Context mContext; private Bitmap mBitmap; private Shader mShader; -
- // 省略构造方法
-
- private void init() {
mShader = new LinearGradient(0, 0, 500, 500, Color.BLUE, Color.GREEN,Shader.TileMode.CLAMP); mPaint = new Paint(); mPaint.setAntiAlias(true); } -
- @Override
protected void onDraw(Canvas canvas) { mPaint.setShader(mBitmapShader); canvas.drawCircle(500, 550, 400, mPaint); } -
- }
|
效果如下:

设置REPEAT 和 MIRROR就不贴图片了,小伙伴们可以自己试试看看效果。
RadialGrdient
设置光束从中心向四周发散的辐射渐变效果,构造方法:
- 1
2 3
|
- //坐标(centerX,centerY)中心点坐标,radius圆的半径,centerColor中心颜色,edgeColor圆的轮廓颜色,颜色逐渐从centerColor渐变到edgeColor,TileMode也有CLAMP 、REPEAT 和 MIRROR三个取值
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode) RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
|
例子:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
- public class PaintCanvas extends View {
private Paint mPaint; private Context mContext; private Shader mShader; -
- // 省略构造方法
-
- private void init() {
mShader = new RadialGradient(500, 500, 400, Color.BLUE, Color.GREEN, Shader.TileMode.CLAMP); mPaint = new Paint(); mPaint.setAntiAlias(true); } -
- @Override
protected void onDraw(Canvas canvas) { mPaint.setShader(mBitmapShader); canvas.drawCircle(500, 550, 400, mPaint); } -
- }
|
效果如下:

设置REPEAT 和 MIRROR也不贴图片了。
SweepGradient
设置绕着某中心点进行360度旋转渐变效果,构造方法:
- 1
2 3 4
|
- //坐标(cx,cy)决定了中心点的位置,会绕着该中心点进行360度旋转。color0表示的是起点的颜色,color1表示的是终点的颜色
SweepGradient(float cx, float cy, int color0, int color1) //坐标(cx,cy)决定了中心点的位置,colors颜色数组,position取值范围为[0,1],0和1都表示3点钟位置,0.25表示6点钟位置,0.5表示9点钟位置,0.75表示12点钟位置,诸如此类 SweepGradient(float cx, float cy, int[] colors, float[] positions)
|
例子:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
- public class PaintCanvas extends View {
private Paint mPaint; private Context mContext; private Shader mShader; -
- // 省略构造方法
-
- private void init() {
mShader = new SweepGradient(500, 500, Color.BLUE, Color.GREEN); mPaint = new Paint(); mPaint.setAntiAlias(true); } -
- @Override
protected void onDraw(Canvas canvas) { mPaint.setShader(mBitmapShader); canvas.drawCircle(500, 550, 400, mPaint); } -
- }
|
效果如下:

ComposeShader
混合,有两个构造函数
- 1
2 3
|
- //shaderA对应下层图形,shaderB对应上层图形
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
|
例子:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
- public class PaintCanvas extends View {
private Paint mPaint; private Context mContext; private Bitmap mBitmap; private Shader bitmapShader, linearGradient, composeShader; -
- // 省略构造方法
-
- private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo); bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); linearGradient = new LinearGradient(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), Color.BLUE, Color.GREEN, Shader.TileMode.CLAMP); //bitmapShader对应下层图形,linearGradient对应上层图形,像素颜色混合采用MULTIPLY模式 composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY); mPaint = new Paint(); mPaint.setAntiAlias(true); } -
- @Override
protected void onDraw(Canvas canvas) { mPaint.setShader(mBitmapShader); canvas.drawCircle(500, 550, 400, mPaint); } }
|
效果如下:

setShadowLayer
- 1
|
- void setShadowLayer(float radius, float dx, float dy, int shadowColor)
|
图形添加一个阴影层效果
setStrikeThruText
- 1
|
- void setStrikeThruText (boolean strikeThruText)
|
设置删除线
setStrokeCap
- 1
|
- void setStrokeCap (Paint.Cap cap)
|
当设置setStyle是Stroke或StrokeAndFill,设置笔刷的图形样式,如圆形样式Cap.ROUND或方形样式Cap.SQUARE
setStrokeJoin
- 1
|
- void setStrokeJoin (Paint.Join join)
|
当设置setStyle是Stroke或StrokeAndFill,设置绘制时各图形的结合方式,如影响矩形角的外轮廓
setStrokeMiter
- 1
|
- void setStrokeMiter (float miter)
|
当设置setStyle是Stroke或StrokeAndFill,设置斜切
setStrokeWidth
- 1
|
- void setStrokeWidth (float width)
|
当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
setStyle
- 1
|
- void setStyle (Paint.Style style)
|
设置画笔样式,画笔样式分三种:
Paint.Style.STROKE:描边
Paint.Style.FILL_AND_STROKE:描边并填充
Paint.Style.FILL:填充
setSubpixelText
- 1
|
- void setSubpixelText (boolean subpixelText)
|
有助于文本在LCD屏幕上的显示效果
setTextAlign
- 1
|
- void setTextAlign(Paint.Align align)
|
设置文本对齐
setTextScaleX
- 1
|
- void setTextScaleX(float scaleX)
|
设置文本缩放倍数,1.0f为原始
setTextSize
- 1
|
- void setTextSize(float textSize)
|
设置字体大小
setTextSkewX
- 1
|
- void setTextSkewX (float skewX)
|
设置斜体文字,skewX为倾斜弧度,默认值0,大于0,向左斜,小于0,向右斜
setTypeface
- 1
|
- Typeface setTypeface(Typeface typeface)
|
设置字体,Typeface包含了字体的类型,粗细,还有倾斜、颜色等。
- 1
|
- mPaint.setTypeface(Typeface.SANS_SERIF);
|
setUnderlineText
- 1
|
- void setUnderlineText(boolean underlineText)
|
设置下划线
setXfermode
- 1
|
- Xfermode setXfermode (Xfermode xfermode)
|
设置图像混合模式,Xfermode 有个子类去实现PorterDuffXfermode
PorterDuffXfermode
构造方法PorterDuffXfermode(PorterDuff.Mode mode)
,参数就是上面的提到的,图形混合模式如图:

Dst:先画(下层)的图形;Src:后画(上层)的图形,然而被网上这张图片误导了,解释见孙群博客,他也给了最终运行效果:

我一一运行确实是如此,这里贴出Mode 为Screen代码:
- 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
- public class PaintCanvas extends View {
private Paint mPaint; private PorterDuffXfermode porterDuffXfermode;// 图形混合模式 private Context mContext; private Bitmap mBitmap; //省略构造方法 -
- private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo); mPaint = new Paint(); mPaint.setAntiAlias(true); // 实例化混合模式 porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SCREEN); } -
- @Override
protected void onDraw(Canvas canvas) { int canvasWidth = canvas.getWidth(); int canvasHeight = canvas.getHeight(); //新建一个layer,放置在canvas默认layer的上部,产生的layer初始时是完全透明的 int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG); //dst是先画的图形 canvas.drawBitmap(mBitmap, 0, 0, mPaint); //设置混合模式 mPaint.setXfermode(porterDuffXfermode); //src是后画的图形 mPaint.setColor(0xFFFFCC44); canvas.drawCircle(600, 600, 200, mPaint); //还原混合模式 mPaint.setXfermode(null); //或canvas.restore()把这个layer绘制到canvas默认的layer上去 canvas.restoreToCount(layerId); } -
- }
|

例子源码
https://github.com/WuXiaolong/AndroidSamples/blob/master/app/src/main/java/com/wuxiaolong/androidsamples/paintcanvas/PaintCanvas.java
- 【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用——绘图、涂鸦板app的实现
利用一个简单的绘图app来说明安卓的图形处理类与自己定义View的应用. 例如以下图,有一个供用户自己随意绘图.涂鸦的app. 这里不做那么花俏了,仅提供黑白两色.但能够改变笔尖的粗细. 实质上这里的 ...
- 【转】(转)【Android】Paint的效果研究
转自:http://wpf814533631.iteye.com/blog/1847661 (转)[Android]Paint的效果研究 博客分类: android 在Paint中有很多的属性可以 ...
- 画布Canvas 画笔Paint
package com.example.m_evolution.View; import android.content.Context; import android.graphics.Canvas ...
- 创建android画笔程序的样例(有镜面效果)
先上图: 关键是在检測到手指移动的时候用mPath.quadTo的方法,android sdk解释是: Add a quadratic bezier from the last point, appr ...
- android画笔错位问题的解决
下面的画画板的代码: public class MainActivity extends Activity { private ImageView iv; private Bitmap baseBit ...
- android中paint的setXfermode属性
本文前半部分来自于:http://www.cnblogs.com/rayray/p/3670120.html 1.下面的Xfermode子类可以改变这种行为: AvoidXfermode 指定了一个 ...
- android.graphics.Paint方法setXfermode (Xfermode x...
mPaint = new Paint(); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); 常见的Xfermod ...
- android Canvas 和 Paint用法
自定义view里面的onDraw方法,在这里我们可以绘制各种图形,onDraw里面有两个API我们需要了解清楚他们的用法:Canvas 和 Paint. Canvas翻译成中文就是画布的意思,Canv ...
- Android Paint和Color类绘画实例
要绘图,首先得调整画笔,待画笔调整好之后,再将图像绘制到画布上,这样才可以显示在手机屏幕上.Android 中的画笔是 Paint类,Paint 中包含了很多方法对其属性进行设置,主要方法如下: se ...
随机推荐
- 使用PHP生成分享图片
小程序导航 wq.xmaht.top 假设代码中用到的资源文件夹在当前code_png目录下: /** * 分享图片生成 * @param $gData 商品数据,array * @param $co ...
- C语言结构体指针成员强制类型转换
#include <stdio.h> #include <stdlib.h> typedef struct ListElmt_ { void *data; struct Lis ...
- 解决win10子系统Ubuntu新装的mysql 不能root登陆方法
步骤一:打开终端 $sudo /etc/init.d/mysql stop $sudo mkdir -p /var/run/mysqld $sudo chown mysql:mysql /var/ru ...
- Spring的注解@Qualifier
近期在捯饬spring的注解,现将遇到的问题记录下来,以供遇到同样问题的童鞋解决~ 先说明下场景,代码如下: 有如下接口: public interface EmployeeService { pub ...
- Dropping Balls(小球下落)
紫书P148,例题6-6 Sample Input 4 2 3 4 10 1 2 2 8 128 Sample Output 12 7 512 3 255 这应该不仅仅是一棵完全二叉树,题目中说保证所 ...
- __builtin_popcount() 函数
详解 该函数的主要作用是计算一个数字的二进制中有多少个1,返回值就是其中1的个数. 它使用一张基于表的方法来进行位搜索,因此这个操作的执行效率很高 此处举一题 P1582 倒水 #include &l ...
- The Road to learn React书籍学习笔记(第一章)
react灵活的生态圈 Small Application Boilerplate: create-react-app Utility: JavaScript ES6 and beyond Styli ...
- Python 探测图片文件类型
Table of Contents 1. 探测图片类型 1.1. python magic 1.2. imghdr 1.3. PIL.Image 探测图片类型 今天遇到一个小问题,如何探测图片的文件类 ...
- Mysql 表转换成 Sqlite表
目前的转换仅仅支持对没有外键的Mysql数据表 准备: 下载安装 Sqlite Expert 软件 一 获取Mysql中的.sql文件,获取过程省略可以直接导出sql文件 二 在Sqlite Expe ...
- Struts2---配置文件讲解及简单登录示例
bean 用于创建一个JavaBean实例 constant 用于Struts2默认行为标签 <!-- 配置web默认编码集,相当于HttpServletRequest.setChartacte ...