Android的遮罩层(蒙板)效果 setXfermode

2023-10-17 21:20

本文主要是介绍Android的遮罩层(蒙板)效果 setXfermode,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       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;@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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/228005

相关文章

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(