Android實(shí)現(xiàn)遮罩層(蒙板)效果
Android的遮罩效果就是把一張圖片蓋在另一張圖片的上面,通過(guò)控制任意一張圖片的顯示百分比實(shí)現(xiàn)遮罩效果。下面我使用兩張一樣的圖片來(lái)實(shí)現(xiàn)一個(gè)類(lèi)似于 Android 的progressbar 的填充效果。使用遮罩效果來(lái)實(shí)現(xiàn)progressbar的效果的好處是,我們可以只改變圖片就可以更改progress的進(jìn)度填充效果,并且我們可以實(shí)現(xiàn)任意形式的填充效果,就比如橫豎填充,扇形逆/順時(shí)填充針等。
網(wǎng)上有很多介紹Android 遮罩效果的列子,但是都是橫豎的填充效果,下面我來(lái)實(shí)現(xiàn)一個(gè)扇形填充效果,如下圖:

我現(xiàn)在要做的就是用這兩種圖去實(shí)現(xiàn)一個(gè)progressbar效果.好了原來(lái)不解釋了直接上代碼吧:
一.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)畫(huà)時(shí)間,越大越慢
maskProgress.setTotaltime(3);
//progress背景圖
maskProgress.setBackgroundResId(R.drawable.untitled1);
//progress填充內(nèi)容圖片
maskProgress.setContentResId(R.drawable.untitled2);
//Progress開(kāi)始的填充的位置360和0為圓最右、90圓最下、180為圓最右、270為圓最上(順時(shí)針?lè)较驗(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)畫(huà)。
* 該接口用于監(jiān)聽(tīng)動(dòng)畫(huà)的完成,你應(yīng)該設(shè)置監(jiān)聽(tīng)器監(jiān)聽(tīng)到動(dòng)畫(huà)完成后,才再一次調(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í),從沒(méi)有填充到,填充到給定的值時(shí)
* 有一段動(dòng)畫(huà)
* */
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軟鍵盤(pán)遮罩該怎么辦
- 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 USB如何修改VID具體實(shí)現(xiàn)
在android 設(shè)備的Linux 內(nèi)核中把 USB 驅(qū)動(dòng)的 PID VID 修改以后,也許之前的adb工具就不能識(shí)別設(shè)備了,會(huì)打印出"device not found"的提示2013-06-06
基于Socket.IO實(shí)現(xiàn)Android聊天功能代碼示例
本篇文章主要介紹了基于Socket.IO實(shí)現(xiàn)Android聊天功能代碼示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
Android okhttputils現(xiàn)在進(jìn)度顯示實(shí)例代碼
本文通過(guò)實(shí)例代碼給大家詳細(xì)介紹了Android okhttputils現(xiàn)在進(jìn)度顯示,代碼簡(jiǎn)答易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2016-12-12
Android中自定義加載樣式圖片的具體實(shí)現(xiàn)
想實(shí)現(xiàn)下面這張圖中的自定義加載樣式,其實(shí)很簡(jiǎn)單,首先我們需要的布局組件有ProcessBar和TextView,下面是布局文件的代碼2014-04-04
Flutter利用Canvas模擬實(shí)現(xiàn)微信紅包領(lǐng)取效果
這篇文章主要為大家詳細(xì)介紹了如何利用Flutter中的Canvas模擬實(shí)現(xiàn)微信紅包領(lǐng)取的效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03
android 傳感器(OnSensorChanged)使用介紹
當(dāng)傳感器的值發(fā)生變化時(shí),例如磁阻傳感器方向改變時(shí)會(huì)調(diào)用OnSensorChanged(). 當(dāng)傳感器的精度發(fā)生變化時(shí)會(huì)調(diào)用OnAccuracyChanged()方法2014-11-11
Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
Android應(yīng)用退出登錄的實(shí)現(xiàn)方法
每一個(gè)app都會(huì)有一個(gè)”退出登陸”的功能,當(dāng)點(diǎn)擊退出之后需要將所有的Activity都finish掉,開(kāi)始是想將棧中的所有Activity清除掉,但是沒(méi)有找到方法,后來(lái)用廣播實(shí)現(xiàn)了。下面小編給大家分享android應(yīng)用退出登錄的實(shí)現(xiàn)方法,需要的朋友參考下2017-04-04

