Android实现遮罩层(蒙板)效果

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的,下面大的是从代码中添加的

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义Dialog内部透明、外部遮罩效果

    本文实例为大家分享了Android自定义Dialog遮罩效果的具体代码,供大家参考,具体内容如下 图例: 代码 1.自定义dialog:引入样式和代码指定样式 package com.gxjl.pe.gxjlpesdk.view; import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import

  • Android页面中引导蒙层的使用方法详解

    蒙层是什么,蒙层是一层透明的呈灰色的视图,是在用户使用App时让用户快速学会使用的一些指导.类似于一些引导页面,只不过比引导页面更加生动形象而已.在GitHub上有具体的demo. 地址为   github源码地址,需要的可以去上面下载源码看看 使用引导蒙层非常简单,只要在你的项目中导入一个GuideView类即可,当然,别忘了在values的资源文件下加上相应的一些数值. 下面是GuideView的原码 public class GuideView extends RelativeLayout

  • Android使用popUpWindow带遮罩层的弹出框

    上次项目中实现了新功能,就一直想添加到博客里来着,惰性发作起来简直太可怕,不说了,跟着一起写吧,三步即可实现简单的弹出框功能,首先看效果-- 首先:主页面布局,触发控件一定要有,再有就是给根标签设置id <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:

  • Android实现新手引导半透明蒙层效果

    本文实例为大家分享了Android实现新手引导半透明蒙层效果的具体代码,供大家参考,具体内容如下 效果图: 其中的文字和我知道啦是ui切得两张透明图片 自定义View: package com.cymobi.library.view.widget; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas;

  • Android自定义ViewGroup实现竖向引导界面

    一般进入APP都有欢迎界面,基本都是水平滚动的,今天和大家分享一个垂直滚动的例子. 先来看看效果把: 1.首先是布局文件: <com.example.verticallinearlayout.VerticalLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:i

  • 360浏览器文本框获得焦点后被android软键盘遮罩该怎么办

    场景是这样的,站点上筛选按钮点击后弹出层(fixed),当输入框获取焦点以后弹出系统自带的软键盘,在android上十款浏览器挨个测试比对,发现在360浏览器弹出键盘以后获取焦点的文本框被软键盘覆盖了. 截图如下 (未获取软键盘焦点的情况) (chrome浏览器调起软键盘的情况) (360浏览器调起软键盘情况) 那么问题来了,浏览器的软键盘显示出来又哪几种情况呢?英文   中文(网上找的) 经过简单的了解,大概分析了一下软键盘在浏览器上弹出应该包含软键盘占用主activity空间,让主activ

  • Android GuideView实现首次登陆引导

    简介:最最轻量级的新手引导库,能够快速为任何一个 View 创建一个遮罩层,支持单个页面,多个引导提示,支持为高亮区域设置不同的图形,支持引导动画,方便扩展 项目地址:binIoter/GuideView GuideView 本系统能够快速的为一个 Activity 里的任何一个 View 控件创建一个遮罩式的导航页. 工作原理 首先它需要一个目标 View 或者它的 id,我们通过 findViewById 来得到这个 View,计算它在屏幕上的区域 targetRect,通过这个区域,开始绘

  • Android PopupWindow实现遮罩层效果

    此篇博客实现的功能是:点击界面中的图片,跳出一个PopupWindow,PopupWindow中含有相应的文字和图标,并且在显示PopupWindow的时候,背景为半透明. 看图描述:点击加号,跳出PopupWindow,其中包含三个图片,点击叉号PopupWindow消失:当PopupWindow显示的时候,背景为半透明 显示PopupWindow的代码 private void showPopupWindow() { View view = (LinearLayout) getLayoutI

  • Android之淘宝商品列表长按遮罩效果的实现

    先来看看淘宝.唯品会长按商品的效果,以及简单Demo的效果: 首先分析一下场景: 长按条目时,弹出遮罩的效果遮挡在原来的条目布局上: 页面滑动或点击其他的条目,上一个正在遮罩的条目遮罩消失. 长按其他条目时,上一个遮罩的条目撤销遮罩,当前长按的显示遮罩: 条目添加遮罩的时添加动画: 1. 遮罩的效果,我们会很容易的想到Android布局控件FrameLayout布局,是基于叠加在上方的布局.所以在列表条目布局的时候,可以使用FrameLayout布局,在长按列表条目时,用条目的根布局添加一个遮罩

  • 一分钟实现Android遮罩引导视图

    一分钟实现Android遮罩引导视图,供大家参考,具体内容如下 先看一下效果图 主角GuideView登场! GuideView是一种基于DialogFragment实现的引导遮罩浮层视图的轻量级解决方案,它具备以下的特性: 响应导航按钮的动作(因为引导浮层本质是一个dialog): 链式引导层,支持设定一组的引导遮罩视图,通过点击切换下一个试图,快读与业务进行解藕: 自动绘制半透明浮层.透明核心区以及确保目标视图和引导视图的位置. 实现说明 页面的结构如下图所示: 核心类 GuideViewB

随机推荐