Android的遮罩层(蒙板)效果 setXfermode

       Android的遮罩效果就是把一张图片盖在另一张图片的上面,通过控制任意一张图片的显示百分比实现遮罩效果。下面我使用两张一样的图片来实现一个类似于 Android 的progressbar 的填充效果。使用遮罩效果来实现progressbar的效果的好处是,我们可以只改变图片就可以更改progress的进度填充效果,并且我们可以实现任意形式的填充效果,就比如横竖填充,扇形逆/顺时填充针等。

      网上有很多介绍Android 遮罩效果的列子,但是都是横竖的填充效果,下面我来实现一个扇形填充效果,如下图:


    我现在要做的就是用这两种图去实现一个progressbar效果.好了原来不解释了直接上代码吧:

    一.Activity代码
package com.gplus.mask.test;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.gplus.mask.widget.MaskProgress;
import com.gplus.mask.widget.MaskProgress.AnimateListener;


public class GplusMask extends Activity{
	
	float progressFromCode  = 150;
	float progressFromXml  = 150;
	
	MaskProgress maskProgressFromeCode;
	MaskProgress maskProgressFromeXml;
	
	private boolean isAnimateFinish = true;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		RelativeLayout parent = (RelativeLayout) findViewById(R.id.parent);
		maskProgressFromeCode = new MaskProgress(this);
		initialProgress(maskProgressFromeCode);
		RelativeLayout.LayoutParams rp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 
				RelativeLayout.LayoutParams.MATCH_PARENT);
		parent.addView(maskProgressFromeCode, rp);
		maskProgressFromeCode.initial();
		
		maskProgressFromeXml = (MaskProgress) findViewById(R.id.maskView);
		
	}
	
	private void initialProgress(MaskProgress maskProgress){
		//设置最大值
		maskProgress.setMax(300);
		//初始填充量为一半
		//初始化填充progress时的填充动画时间,越大越慢
		maskProgress.setTotaltime(3);
		//progress背景图
		maskProgress.setBackgroundResId(R.drawable.untitled1);
		//progress填充内容图片
		maskProgress.setContentResId(R.drawable.untitled2);
		//Progress开始的填充的位置360和0为圆最右、90圆最下、180为圆最右、270为圆最上(顺时针方向为正)
		maskProgress.setStartAngle(0);
		maskProgress.setAnimateListener(animateListener);
		//初始化时必须在setMax设置之后再设置setProgress
		maskProgress.setProgress(175);
	}
	
	Handler handler = new Handler(){

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);

			float newProgress = maskProgressFromeCode.getProgress() - 4;
			if(newProgress <= 0){//随机绘制效果
				
				float max = (float) (Math.random() * 900 + 1000);
				float progress = (float) (max * Math.random());
				
				maskProgressFromeCode.setMax(max);
				maskProgressFromeCode.setProgress(progress);
				maskProgressFromeCode.setTotaltime((float) (Math.random()*10));
				maskProgressFromeCode.setStartAngle((float) (Math.random()*360));
				maskProgressFromeCode.initial();
				return;
			}
			maskProgressFromeCode.setProgress(newProgress);
			maskProgressFromeCode.updateProgress();
			
			handler.sendEmptyMessageDelayed(0, 50);
		}
	};
	
	AnimateListener animateListener = new AnimateListener() {
		
		@Override
		public void onAnimateFinish() {
			handler.sendEmptyMessageDelayed(0, 500);
		}
	};
}
   二.activity布局文件main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.gplus.mask.test"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical" >

        <com.gplus.mask.widget.MaskProgress
            android:id="@+id/maskView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            app:anim_time="20"
            app:max="180"
            app:progress="135"
            app:progress_background="@drawable/untitled1"
            app:progress_content="@drawable/untitled2"
            app:start_angle="0" 
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/parent"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical" />

</LinearLayout>
    三.View的实现效果MaskProgress.java
package com.gplus.mask.widget;




import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;

/**
 * @author huangxin
 */
public class MaskProgress extends View{
	
	
	/** 每次setProgress时进度条前进或者回退到所设的值时都会有一段动画。
	 * 该接口用于监听动画的完成,你应该设置监听器监听到动画完成后,才再一次调用 
	 * setProgress方法
	 * */
	public static interface AnimateListener{
		public void onAnimateFinish();
	}
	
	private float totalTime = 5;//s
	

	private final static int REFRESH = 10;//mills

	private float step;
	
	private float max = 360;
	
	
	private float currentProgress;

	private float destProgress = 0;
	private float realProgress = 0;
	private float oldRealProgress = 0;
	private int backgroundResId;
	private int contentResId;
	
	private float startAngle = 270;
	
	private Bitmap bg;
	private Bitmap ct;
	
	private Paint paint;
	
	private int radius;
	
	private int beginX;
	private int beginY;
	
	private int centerX;
	private int centerY;
	
	private RectF rectF;
	
	private PorterDuffXfermode srcIn;
	
	private double rate;
	
	boolean initialing = false;
	
	AnimateListener animateListener;
	
	public MaskProgress(Context context) {
		this(context, null);
	}
	
	public MaskProgress(Context context, AttributeSet attrs) {
		this(context, attrs, R.attr.maskProgressStyle);
	}
	
	public MaskProgress(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context, attrs, defStyle);
	}


	public void setAnimateListener(AnimateListener animateListener) {
		this.animateListener = animateListener;
	}

	public void setProgress(float destProgress) {
		if(destProgress > max)
			try {
				throw new Exception("progress can biger than max");
			} catch (Exception e) {
				e.printStackTrace();
			}
		
		this.destProgress = destProgress;
		oldRealProgress = realProgress;
		realProgress = (float) (destProgress * rate);
	}
	
	public float getProgress(){
		return destProgress;
	}
	
	public void setTotaltime(float totalTime) {
		this.totalTime = totalTime;
		step = 360 / (totalTime * 1000 / REFRESH);
	}

	public static int getRefresh() {
		return REFRESH;
	}

	public void setMax(float max) {
		this.max = max;
		rate = 360 / max;
	}

	public void setStartAngle(float startAngle) {
		this.startAngle = startAngle;
	}
	

	public void setBackgroundResId(int backgroundResId) {
		this.backgroundResId = backgroundResId;
		bg = BitmapFactory.decodeResource(getResources(), backgroundResId);
	}

	public void setContentResId(int contentResId) {
		this.contentResId = contentResId;
		ct = BitmapFactory.decodeResource(getResources(), contentResId);
	}
	
	public void updateProgress(){
		invalidate();
	}
	
	/** 初始化,第一次给MaskProgress设值时,从没有填充到,填充到给定的值时
	 * 有一段动画
	 * */
	public void initial(){
		initialing = true;
		new CirculateUpdateThread().start();
	}
	
	public float getMax() {
		return max;
	}
	
	private void init(Context context, AttributeSet attrs, int defStyle){
		
		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.maskProgressBar, defStyle, 0);
		
		if (typedArray != null) {
            try {
                setMax(typedArray.getFloat(R.styleable.maskProgressBar_max, max));
                setProgress(typedArray.getFloat(R.styleable.maskProgressBar_progress, destProgress));
                setTotaltime(typedArray.getFloat(R.styleable.maskProgressBar_anim_time, totalTime));
                setStartAngle(typedArray.getFloat(R.styleable.maskProgressBar_start_angle, startAngle));
                setContentResId(typedArray.getResourceId(R.styleable.maskProgressBar_progress_content, R.drawable.untitled2));
                setBackgroundResId(typedArray.getResourceId(R.styleable.maskProgressBar_progress_background, R.drawable.untitled1));
            } finally {
            	typedArray.recycle();
            }   
        }
				
		paint = new Paint();
		paint.setDither(true);
		paint.setAntiAlias(true);
		
		rate = 360 / max;
		currentProgress = 0;
		realProgress = (float) (destProgress * rate);
		srcIn = new PorterDuffXfermode(Mode.SRC_IN);
		step = 360 / (totalTime * 1000 / REFRESH);
		
		bg = BitmapFactory.decodeResource(getResources(), backgroundResId);
		ct = BitmapFactory.decodeResource(getResources(), contentResId);
		
		Log.w("init", "max: " + max + "\n" + "destProgress: " + destProgress +"\n"+"totalTime: "+ totalTime+"\n"+"startAngle: "+ startAngle);
		
		initialing = true;
		new CirculateUpdateThread().start();
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		canvas.drawBitmap(bg, 0, (getHeight() - bg.getHeight()) / 2, paint);
		int rc = canvas.saveLayer(0, (getHeight() - bg.getHeight()) / 2, bg.getWidth(), (getHeight() + bg.getHeight()) / 2, null, Canvas.ALL_SAVE_FLAG);
		
		paint.setFilterBitmap(false);
		if(initialing){
			canvas.drawArc(rectF, startAngle, currentProgress, true, paint);
		}else{
			canvas.drawArc(rectF, startAngle, realProgress, true, paint);
		}
		paint.setXfermode(srcIn);
		canvas.drawBitmap(ct, 0, (getHeight() - ct.getHeight()) / 2, paint);
		
		paint.setXfermode(null);
		canvas.restoreToCount(rc);
	}
	
	public int[] getRectPosition(int progress){
		int[] rect = new int[4]; 
		
		rect[0] = beginX;
		rect[1] = beginY;
		rect[2] = (int)(centerX + radius * Math.cos(progress * Math.PI /180));
		rect[3] = (int)(centerY + radius * Math.sin(progress * Math.PI /180));
		
		Log.w("getRectPosition", "30: " + Math.sin(30 * Math.PI /180));
		
		Log.w("getRectPosition", "X: " + rect[2] + "  " + "Y: " + rect[3]);
		
		return rect;
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		
		int tmp = w >= h ? h : w;
		
		radius = tmp / 2;
		beginX = w / 2;
		beginY = 0;
		centerX = tmp / 2;
		centerY = tmp / 2;
		
		Bitmap bg_ = resizeBitmap(bg, tmp, tmp);
		Bitmap ct_ = resizeBitmap(ct, tmp, tmp);
		
		rectF = new RectF(0, (getHeight() - bg_.getHeight()) / 2, bg_.getWidth(), (getHeight() + bg_.getHeight()) / 2);
		
		bg.recycle();
		ct.recycle();
		
		bg = bg_;
		ct = ct_;
	}
	
	private Bitmap resizeBitmap(Bitmap src, int w, int h){
		
		int width =  src.getWidth();
		int height = src.getHeight();
		int scaleWidht = w / width;
		int scaleHeight = h / height;
		
		Matrix matrix = new Matrix();
		matrix.postScale(scaleWidht, scaleHeight);
		
		Bitmap result = Bitmap.createScaledBitmap(src, w, h, true);
		src = null;
		
		return result;
	}
	
	class CirculateUpdateThread extends Thread{

		@Override
		public void run() {
			while(initialing){
				postInvalidate();
				if(currentProgress < realProgress){
					currentProgress += step * rate;
					if(currentProgress > realProgress)
						currentProgress = realProgress;
				}else{				
					// new Thread(new Runnable() {
					//
					// @Override
					// public void run() {
					// while (true) {
					// postInvalidate();
					// if (currentProgress > 0) {
					// currentProgress -= step * rate;
					// } else {
					// currentProgress = 0;
					// new CirculateUpdateThread().start();
					// break;
					// }
					// try {
					// Thread.sleep(REFRESH);
					// } catch (Exception e) {
					// e.printStackTrace();
					// }
					// }
					// }
					// }).start();
					currentProgress = 0;
					initialing = false;
					if(animateListener != null)
						animateListener.onAnimateFinish();
				}
				try{
					Thread.sleep(REFRESH);
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
		
	} 
}
   四.该Veiw自定义的属性文件attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="maskProgressBar">
        <attr name="max" format="float" />
        <attr name="progress" format="float" />
        <attr name="start_angle" format="float" />
        <attr name="progress_background" format="reference" />
        <attr name="progress_content" format="reference" />
        <attr name="anim_time" format="float" />
    </declare-styleable>

    
    <attr name="maskProgressStyle" format="reference" />
    
</resources>
    效果图如下,上面小的是定义xml的,下面大的是从代码中添加的




猜你喜欢

转载自blog.csdn.net/u010949962/article/details/41908837