Android實(shí)現(xiàn)遮罩層(蒙板)效果
Android的遮罩效果就是把一張圖片蓋在另一張圖片的上面,通過控制任意一張圖片的顯示百分比實(shí)現(xiàn)遮罩效果。下面我使用兩張一樣的圖片來實(shí)現(xiàn)一個(gè)類似于 Android 的progressbar 的填充效果。使用遮罩效果來實(shí)現(xiàn)progressbar的效果的好處是,我們可以只改變圖片就可以更改progress的進(jìn)度填充效果,并且我們可以實(shí)現(xiàn)任意形式的填充效果,就比如橫豎填充,扇形逆/順時(shí)填充針等。
網(wǎng)上有很多介紹Android 遮罩效果的列子,但是都是橫豎的填充效果,下面我來實(shí)現(xiàn)一個(gè)扇形填充效果,如下圖:
我現(xiàn)在要做的就是用這兩種圖去實(shí)現(xiàn)一個(gè)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){ //設(shè)置最大值 maskProgress.setMax(300); //初始填充量為一半 //初始化填充progress時(shí)的填充動(dòng)畫時(shí)間,越大越慢 maskProgress.setTotaltime(3); //progress背景圖 maskProgress.setBackgroundResId(R.drawable.untitled1); //progress填充內(nèi)容圖片 maskProgress.setContentResId(R.drawable.untitled2); //Progress開始的填充的位置360和0為圓最右、90圓最下、180為圓最右、270為圓最上(順時(shí)針方向?yàn)檎? maskProgress.setStartAngle(0); maskProgress.setAnimateListener(animateListener); //初始化時(shí)必須在setMax設(shè)置之后再設(shè)置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){//隨機(jī)繪制效果 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的實(shí)現(xiàn)效果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時(shí)進(jìn)度條前進(jìn)或者回退到所設(shè)的值時(shí)都會(huì)有一段動(dòng)畫。 * 該接口用于監(jiān)聽動(dòng)畫的完成,你應(yīng)該設(shè)置監(jiān)聽器監(jiān)聽到動(dòng)畫完成后,才再一次調(diào)用 * 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設(shè)值時(shí),從沒有填充到,填充到給定的值時(shí) * 有一段動(dòng)畫 * */ 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的,下面大的是從代碼中添加的
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android自定義Dialog內(nèi)部透明、外部遮罩效果
- Android PopupWindow實(shí)現(xiàn)遮罩層效果
- Android使用popUpWindow帶遮罩層的彈出框
- Android之淘寶商品列表長(zhǎng)按遮罩效果的實(shí)現(xiàn)
- 360瀏覽器文本框獲得焦點(diǎn)后被android軟鍵盤遮罩該怎么辦
- Android頁(yè)面中引導(dǎo)蒙層的使用方法詳解
- Android實(shí)現(xiàn)新手引導(dǎo)半透明蒙層效果
- Android自定義ViewGroup實(shí)現(xiàn)豎向引導(dǎo)界面
- Android GuideView實(shí)現(xiàn)首次登陸引導(dǎo)
- 一分鐘實(shí)現(xiàn)Android遮罩引導(dǎo)視圖
相關(guān)文章
Android自定義LinearLayout實(shí)現(xiàn)淘寶詳情頁(yè)
這篇文章主要為大家詳細(xì)介紹了Android自定義LinearLayout實(shí)現(xiàn)淘寶詳情頁(yè)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android開發(fā)中滑動(dòng)分頁(yè)功能實(shí)例詳解
這篇文章主要介紹了Android開發(fā)中滑動(dòng)分頁(yè)功能,結(jié)合實(shí)例形式詳細(xì)分析了Android滑動(dòng)分頁(yè)功能的具體步驟與相關(guān)實(shí)現(xiàn)技巧,代碼中備有詳盡的注釋便于理解,需要的朋友可以參考下2017-10-10Android實(shí)現(xiàn)文字垂直滾動(dòng)、縱向走馬燈效果的實(shí)現(xiàn)方式匯總
本文給大家分享了三種方式實(shí)現(xiàn)Android文字垂直滾動(dòng)、縱向走馬燈效果,文中給大家介紹了相關(guān)屬性及注意事項(xiàng),需要的朋友參考下吧2017-12-12Android編程實(shí)現(xiàn)基于局域網(wǎng)udp廣播自動(dòng)建立socket連接的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)基于局域網(wǎng)udp廣播自動(dòng)建立socket連接的方法,涉及Android使用udp廣播實(shí)現(xiàn)socket通訊的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11解決android studio引用遠(yuǎn)程倉(cāng)庫(kù)下載慢(JCenter下載慢)
這篇文章主要介紹了解決android studio引用遠(yuǎn)程倉(cāng)庫(kù)下載慢(JCenter下載慢),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03詳解Android 進(jìn)程間通信的幾種實(shí)現(xiàn)方式
在Android SDK中提供了4種用于跨進(jìn)程通訊的方式。這篇文章主要介紹了詳解Android 進(jìn)程間通信的幾種實(shí)現(xiàn)方式,有興趣的可以了解一下。2017-01-01android車牌識(shí)別系統(tǒng)EasyPR使用詳解
這篇文章主要為大家詳細(xì)介紹了android車牌識(shí)別系統(tǒng)EasyPR使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android 獲取手機(jī)聯(lián)系人實(shí)例代碼詳解
最近做了個(gè)項(xiàng)目,其中有項(xiàng)目需求是這樣的,需要獲取手機(jī)聯(lián)系人,下面小編把代碼分享給大家,供大家參考2015-12-12