Android实现遮罩层(蒙板)效果
程序员文章站
2022-06-04 12:19:06
android的遮罩效果就是把一张图片盖在另一张图片的上面,通过控制任意一张图片的显示百分比实现遮罩效果。下面我使用两张一样的图片来实现一个类似于 android 的pro...
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的,下面大的是从代码中添加的
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。