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

作者:白鹭飞 时间:2023-04-26 18:43:03 

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

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

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

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

来源:https://blog.csdn.net/u010949962/article/details/41908837?utm_source=blogxgwz7

标签:Android,遮罩层
0
投稿

猜你喜欢

  • 浅谈Java中向上造型向下造型和接口回调中的问题

    2023-11-09 13:51:46
  • Android自定义密码输入框和数字键盘

    2022-02-04 11:14:00
  • Unity制作小地图和方向导航

    2023-02-07 16:51:02
  • Android 实现滑动的六种方式

    2021-12-05 08:27:48
  • JAVA实现的CrazyArcade泡泡堂游戏

    2022-12-25 11:55:23
  • java实现酒店管理系统

    2023-06-09 23:59:50
  • C#中添加窗口的步骤详解

    2021-12-19 16:30:29
  • Java自定义注解用法实例小结

    2023-03-26 09:13:51
  • Android开发之SD卡文件操作分析

    2022-01-09 04:55:16
  • Java中finally和return的关系实例解析

    2023-05-31 04:21:31
  • 解决fcitx输入法在IDEA中输入法候选框无法跟随光标移动的问题

    2021-09-21 14:48:25
  • Android实现系统级悬浮按钮

    2022-12-15 00:44:26
  • slf4j与jul、log4j1、log4j2、logback的集成原理

    2023-01-31 18:01:47
  • Java安全之Tomcat6 Filter内存马问题

    2022-11-20 07:29:23
  • C#模拟window操作鼠标的方法

    2021-07-17 01:50:22
  • 浅析JAVA中过滤器、监听器、拦截器的区别

    2023-10-18 17:47:24
  • Java中的HashSet详解和使用示例_动力节点Java学院整理

    2021-10-21 05:58:33
  • Android Activity之间的数据传递方法总结

    2023-05-26 03:43:21
  • Java I/O 操作及优化详细介绍

    2022-07-30 14:46:42
  • 利用unity代码C#封装为dll的步骤分享

    2023-12-02 00:31:00
  • asp之家 软件编程 m.aspxhome.com