本文主要是介绍Android的遮罩层(蒙板)效果 setXfermode,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Android的遮罩效果就是把一张图片盖在另一张图片的上面,通过控制任意一张图片的显示百分比实现遮罩效果。下面我使用两张一样的图片来实现一个类似于 Android 的progressbar 的填充效果。使用遮罩效果来实现progressbar的效果的好处是,我们可以只改变图片就可以更改progress的进度填充效果,并且我们可以实现任意形式的填充效果,就比如横竖填充,扇形逆/顺时填充针等。
网上有很多介绍Android 遮罩效果的列子,但是都是横竖的填充效果,下面我来实现一个扇形填充效果,如下图:
我现在要做的就是用这两种图去实现一个progressbar效果.好了原来不解释了直接上代码吧:
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;@Overrideprotected 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设置之后再设置setProgressmaskProgress.setProgress(175);}Handler handler = new Handler(){@Overridepublic 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() {@Overridepublic 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" ><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="vertical" ><com.gplus.mask.widget.MaskProgressandroid: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><RelativeLayoutandroid: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;//sprivate final static int REFRESH = 10;//millsprivate 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();}@Overrideprotected 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;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected 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{@Overridepublic 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的遮罩层(蒙板)效果 setXfermode的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!