Android实现遮罩层(蒙板)效果
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的,下面大的是从代码中添加的
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
以上是 Android实现遮罩层(蒙板)效果 的全部内容, 来源链接: utcz.com/p/241286.html