Android实现遮罩层(蒙板)效果

作者:袖梨 2022-06-25

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



 
  
 
    
  
 
  
 

三.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的,下面大的是从代码中添加的

相关文章

精彩推荐