当前位置: 代码迷 >> Android >> Android 自定义View (5)
  详细解决方案

Android 自定义View (5)

热度:41   发布时间:2016-04-28 01:57:57.0
Android 自定义View (五)

在必要时候需要弯一弯,转一转,因为太坚强容易折断,我们需要更多的柔软,才能战胜挫折。


本讲内容:PorterDuffXfermode图形混合模式类只有一个含参的构造方法

<span style="font-size:18px;">PorterDuffXfermode(PorterDuff.Mode mode)</span>

在API中Android为我们提供了18种图形混合模式:


Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色


比上图多了少种ADD和OVERLAY)模式:(上层( src源图 )、下层(dis目标图) 

1、PorterDuff.Mode.CLEAR                所绘制不会提交到画布上。

2、PorterDuff.Mode.SRC                    显示上层绘制图片
3、PorterDuff.Mode.DST                    显示下层绘制图片
4、PorterDuff.Mode.SRC_OVER        正常绘制显示,上下层绘制叠盖。
5、PorterDuff.Mode.DST_OVER        上下层都显示。下层居上显示。
6、PorterDuff.Mode.SRC_IN               取两层绘制交集。显示上层。
7、PorterDuff.Mode.DST_IN               取两层绘制交集。显示下层。
8、PorterDuff.Mode.SRC_OUT          取上层绘制非交集部分。
9、PorterDuff.Mode.DST_OUT          取下层绘制非交集部分。
10、PorterDuff.Mode.SRC_ATOP      取下层非交集部分与上层交集部分
11、PorterDuff.Mode.DST_ATOP      取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR                    取两层绘制非交集。两层绘制非交集。
13、PorterDuff.Mode.DARKEN          上下层都显示。变暗
14、PorterDuff.Mode.LIGHTEN         上下层都显示。变量
15、PorterDuff.Mode.MULTIPLY      取两层绘制交集
16、PorterDuff.Mode.SCREEN         上下层都显示。

17、PorterDuff.Mode.ADD                上下层饱和度相加

18、PorterDuff.Mode.OVERLAY      上下层叠加


示例一:DST_IN  取两层绘制交集。显示下层。

          

     dis目标图                                src源图                        混合模式效果图(文字不见了)

下面是自定义CustomTitleView.java文件:

public class CustomView extends View {	private Paint mPaint;// 画笔	private Bitmap bitmapDis, bitmapSrc;// 位图	private PorterDuffXfermode porterDuffXfermode;// 图形混合模式	private int x, y;// 位图绘制时左上角的起点坐标	private int screenW, screenH;// 屏幕尺寸	public CustomView(Context context) {		super(context, null);	}	public CustomView(Context context, AttributeSet attrs) {		super(context, attrs);		// 实例化混合模式		porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);		// 初始化画笔		initPaint();		// 初始化资源		initRes(context);	}	// 初始化画笔	private void initPaint() {		// 实例化画笔并打开抗锯齿		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);	}	// 初始化资源	private void initRes(Context context) {		// 获取位图		bitmapDis = BitmapFactory.decodeResource(context.getResources(), R.drawable.a1);		bitmapSrc = BitmapFactory.decodeResource(context.getResources(), R.drawable.a2);		// 获取包含屏幕尺寸的数组		int[] screenSize = MeasureUtil.getScreenSize((Activity) context);		// 获取屏幕尺寸		screenW = screenSize[0];		screenH = screenSize[1];		/*		* 计算位图绘制时左上角的坐标使其位于屏幕中心		* 屏幕坐标x轴向左偏移位图一半的宽度		* 屏幕坐标y轴向上偏移位图一半的高度		*/		x = screenW / 2 - bitmapDis.getWidth() / 2;		y = screenH / 2 - bitmapDis.getHeight() / 2;	}	protected void onDraw(Canvas canvas) {		super.onDraw(canvas);		canvas.drawColor(Color.WHITE);		// 将绘制操作保存到新的图层(离屏缓存)		int sc = canvas.saveLayer(0, 0, screenW, screenH, null,				Canvas.ALL_SAVE_FLAG);		// 先绘制dis目标图		canvas.drawBitmap(bitmapDis, x, y, mPaint);		// 设置混合模式		mPaint.setXfermode(porterDuffXfermode);		// 再绘制src源图		canvas.drawBitmap(bitmapSrc, x, y, mPaint);		// 还原混合模式		mPaint.setXfermode(null);		// 还原画布		canvas.restoreToCount(sc);	}}

示例二:DST_OUT     取下层绘制非交集部分。

           

            src源图                              效果图

下面是自定义CustomTitleView.java文件:

public class CustomView extends View {	private Paint mPaint;// 画笔	private Bitmap bitmapSrc;// 位图	private PorterDuffXfermode porterDuffXfermode;// 图形混合模式	private int x, y;// 位图绘制时左上角的起点坐标	private int screenW, screenH;// 屏幕尺寸	public CustomView(Context context) {		super(context, null);	}	public CustomView(Context context, AttributeSet attrs) {		super(context, attrs);		// 实例化混合模式		porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);		// 初始化画笔		initPaint();		// 初始化资源		initRes(context);	}	// 初始化画笔	private void initPaint() {		// 实例化画笔并打开抗锯齿		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);	}	// 初始化资源	private void initRes(Context context) {		// 获取位图		bitmapSrc = BitmapFactory.decodeResource(context.getResources(),R.drawable.c5);		// 获取包含屏幕尺寸的数组		int[] screenSize = MeasureUtil.getScreenSize((Activity) context);		// 获取屏幕尺寸		screenW = screenSize[0];		screenH = screenSize[1];		/*		 * 计算位图绘制时左上角的坐标使其位于屏幕中心		 * 屏幕坐标x轴向左偏移位图一半的宽度		 * 屏幕坐标y轴向上偏移位图一半的高度		 */		x = screenW / 2 - bitmapSrc.getWidth() / 2;		y = screenH / 2 - bitmapSrc.getHeight() / 2;	}	protected void onDraw(Canvas canvas) {		super.onDraw(canvas);		canvas.drawColor(Color.WHITE);		// 将绘制操作保存到新的图层(离屏缓存)		int sc = canvas.saveLayer(0, 0, screenW, screenH, null,				Canvas.ALL_SAVE_FLAG);		// 先绘制一层颜色(先绘制dis目标图)		canvas.drawColor(0xFFFF04DA);		// 设置混合模式		mPaint.setXfermode(porterDuffXfermode);		// 再绘制src源图		canvas.drawBitmap(bitmapSrc, x, y, mPaint);		// 还原混合模式		mPaint.setXfermode(null);		// 还原画布		canvas.restoreToCount(sc);	}}

示例三:橡皮檫效果

我们可以通过手指不断地触摸屏幕绘制Path,再以Path作遮罩遮掉填充的色块显示下层的图像:



public class CustomView extends View {	// 最小的移动距离:如果我们手指在屏幕上的移动距离小于此值则不会绘制	private static final int MIN_MOVE_DIS = 5;	private Bitmap fgBitmap, bgBitmap;// 前景橡皮擦的Bitmap和背景我们底图的Bitmap	private Canvas mCanvas;// 绘制橡皮擦路径的画布	private Paint mPaint;// 橡皮檫路径画笔	private Path mPath;// 橡皮擦绘制路径	private int screenW, screenH;// 屏幕宽高	private float preX, preY;// 记录上一个触摸事件的位置坐标	public CustomView(Context context) {		super(context, null);	}	public CustomView(Context context, AttributeSet attrs) {		super(context, attrs);		// 计算参数		cal(context);		// 初始化对象		init(context);	}	// 计算参数	private void cal(Context context) {		// 获取屏幕尺寸数组		int[] screenSize = MeasureUtil.getScreenSize((Activity) context);		// 获取屏幕宽高		screenW = screenSize[0];		screenH = screenSize[1];	}	// 初始化对象	private void init(Context context) {		// 实例化路径对象		mPath = new Path();		// 实例化画笔并开启其抗锯齿和抗抖动		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);		// 设置画笔透明度为0是关键!我们要让绘制的路径是透明的,然后让该路径与前景的底色混合“抠”出绘制路径		mPaint.setARGB(128, 255, 0, 0);		// 设置混合模式为DST_IN		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));		// 设置画笔风格为描边		mPaint.setStyle(Paint.Style.STROKE);		// 设置路径结合处样式		mPaint.setStrokeJoin(Paint.Join.ROUND);		// 设置笔触类型		mPaint.setStrokeCap(Paint.Cap.ROUND);		// 设置描边宽度		mPaint.setStrokeWidth(50);		// 生成前景图Bitmap		fgBitmap = Bitmap.createBitmap(screenW, screenH, Config.ARGB_8888);		// 将其注入画布		mCanvas = new Canvas(fgBitmap);		// 绘制画布背景为中性灰		mCanvas.drawColor(0xFF808080);		// 获取背景底图Bitmap		bgBitmap = BitmapFactory.decodeResource(context.getResources(),				R.drawable.a4);		// 缩放背景底图Bitmap至屏幕大小		bgBitmap = Bitmap.createScaledBitmap(bgBitmap, screenW, screenH, true);	}	protected void onDraw(Canvas canvas) {		// 绘制背景		canvas.drawBitmap(bgBitmap, 0, 0, null);		// 绘制前景		canvas.drawBitmap(fgBitmap, 0, 0, null);		/*		 * 这里要注意canvas和mCanvas是两个不同的画布对象		 * 当我们在屏幕上移动手指绘制路径时会把路径通过mCanvas绘制到fgBitmap上		 * 每当我们手指移动一次均会将路径mPath作为目标图像绘制到mCanvas上,而在上面我们                   先在mCanvas上绘制了中性灰色		 * 两者会因为DST_IN模式的计算只显示中性灰,但是因为mPath的透明,计算生成的混合                    图像也会是透明的 所以我们会得到“橡皮擦”的效果		 */		mCanvas.drawPath(mPath, mPaint);	}		//View的事件	public boolean onTouchEvent(MotionEvent event) {          //获取当前事件位置坐标          float x = event.getX();          float y = event.getY();            switch (event.getAction()) {          case MotionEvent.ACTION_DOWN:// 手指接触屏幕重置路径              mPath.reset();              mPath.moveTo(x, y);              preX = x;              preY = y;              break;          case MotionEvent.ACTION_MOVE:// 手指移动时连接路径              float dx = Math.abs(x - preX);              float dy = Math.abs(y - preY);              if (dx >= MIN_MOVE_DIS || dy >= MIN_MOVE_DIS) {                  mPath.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);                  preX = x;                  preY = y;              }              break;          }            // 重绘视图          invalidate();          return true;      }  }

Take your time and enjoy it 路过的、学习过的请留个言,顶个呗~~

  相关解决方案