Android實(shí)現(xiàn)掃一掃功能之繪制指定區(qū)域透明區(qū)域
一、概述
在實(shí)現(xiàn)掃一掃的功能的時(shí)候,我們需要繪制一個(gè)中間為透明的掃碼框,其余部分為半透明。通常情況下,例如微信或者支付寶的掃碼框都是矩形的,如果中間的掃碼框是一個(gè)矩形,那么布局是很簡(jiǎn)單的,可是如果掃碼框是一個(gè)圓角矩形,或者圓形等情況怎么辦呢?這篇文章主要是記錄繪制一個(gè)中間透明帶圓角的矩形。
按照慣例,我們先來(lái)看看效果圖 :

二、按照流程我們就開(kāi)始來(lái)看看代碼啦
1、CustomDrawable,支持中間出現(xiàn)透明區(qū)域的drawable
package per.juan.scandome;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* 支持中間出現(xiàn)透明區(qū)域的drawable
* 通過(guò){@link #setSrcPath(Path)}設(shè)定透明區(qū)域的形狀
* Created by juan on 2018/07/20.
*/
public class CustomDrawable extends Drawable {
private Paint srcPaint;
private Path srcPath = new Path();
private Drawable innerDrawable;
public CustomDrawable(Drawable innerDrawable) {
this.innerDrawable = innerDrawable;
srcPath.addRect(100, 100, 200, 200, Path.Direction.CW);
srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
srcPaint.setColor(0xffffffff);
}
/**
* 設(shè)置內(nèi)部透明的部分
*
* @param srcPath
*/
public void setSrcPath(Path srcPath) {
this.srcPath = srcPath;
}
@Override
public void draw(@NonNull Canvas canvas) {
innerDrawable.setBounds(getBounds());
if (srcPath == null || srcPath.isEmpty()) {
innerDrawable.draw(canvas);
} else {
//將繪制操作保存到新的圖層,因?yàn)閳D像合成是很昂貴的操作,將用到硬件加速,這里將圖像合成的處理放到離屏緩存中進(jìn)行
int saveCount = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), srcPaint, Canvas.ALL_SAVE_FLAG);
//dst 繪制目標(biāo)圖
innerDrawable.draw(canvas);
//設(shè)置混合模式
srcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
//src 繪制源圖
canvas.drawPath(srcPath, srcPaint);
//清除混合模式
srcPaint.setXfermode(null);
//還原畫(huà)布
canvas.restoreToCount(saveCount);
}
}
@Override
public void setAlpha(int alpha) {
innerDrawable.setAlpha(alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
innerDrawable.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return innerDrawable.getOpacity();
}
}
(1)主要用到的技術(shù)是PorterDuffXfermode的PorterDuff.Mode.XOR模式
(2)核心思想是先正常繪制出整個(gè)drawable,然后將指定的區(qū)域混合成透明色
2、CustomLayout
package per.juan.scandome;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
/**
* 根據(jù)layout中子View的位置,確定局部透明區(qū)域
* Created by juan on 2018/07/20.
*/
public class CustomLayout extends FrameLayout {
private Context mContext;
private CustomDrawable background;
public CustomLayout(@NonNull Context context) {
super(context);
initView(context, null, 0);
}
public CustomLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.mContext=context;
initView(context, attrs, 0);
}
public CustomLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs, defStyleAttr);
}
@SuppressLint("NewApi")
private void initView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
background = new CustomDrawable(getBackground());
setBackground(background);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
resetBackgroundHoleArea();
}
@SuppressLint("NewApi")
private void resetBackgroundHoleArea() {
Path path = null;
// 以子View為范圍構(gòu)造需要透明顯示的區(qū)域
View view = findViewById(R.id.iv_scan);
if (view != null) {
path = new Path();
// 矩形透明區(qū)域
path.addRoundRect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), dp2Px(mContext,10), dp2Px(mContext,10),Path.Direction.CW);
}
if (path != null) {
background.setSrcPath(path);
}
}
public int dp2Px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
3、然后在XML布局中聲明我們的自定義View
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/frame_layout" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/bg_image" /> <per.juan.scandome.CustomLayout android:layout_width="match_parent" android:id="@+id/layout" android:background="#8c565658" android:layout_height="match_parent"> <!-- 根據(jù)這個(gè)子View所在的位置,計(jì)算出透明矩形的位置 --> <FrameLayout android:id="@+id/iv_scan" android:layout_width="200dp" android引用塊內(nèi)容center" /> </per.juan.scandome.CustomLayout> </FrameLayout>
好了,本篇文章就這樣了,存在不足的地方還望指導(dǎo),感謝^_^
附錄:
自定義Drawable之:在Drawable中部指定透明區(qū)域
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android Studio中導(dǎo)入module的方法(簡(jiǎn)單版)
這篇文章主要介紹了AndroidStudio中導(dǎo)入module的方法,本文是一篇簡(jiǎn)易版的教程,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01
舉例講解Android中ViewPager中的PagerTitleStrip子控件
這篇文章主要介紹了Android中ViewPager中的PagerTitleStrip子控件使用例子,講解了PagerTitleStrip子控件的嵌入與設(shè)置標(biāo)題的用法,需要的朋友可以參考下2016-03-03
android kotlin集成WorkManager實(shí)現(xiàn)定時(shí)獲取數(shù)據(jù)的步驟
在A(yíng)ndroid中使用Kotlin集成WorkManager來(lái)實(shí)現(xiàn)定時(shí)獲取數(shù)據(jù)是一個(gè)很常見(jiàn)的需求,下面給大家分享android kotlin集成WorkManager實(shí)現(xiàn)定時(shí)獲取數(shù)據(jù)的步驟,感興趣的朋友跟隨小編一起看看吧2024-08-08
安卓APP測(cè)試之使用Burp Suite實(shí)現(xiàn)HTTPS抓包方法
這篇文章主要介紹了安卓APP測(cè)試之使用Burp Suite實(shí)現(xiàn)HTTPS抓包方法,本文詳解講解了測(cè)試環(huán)境和各個(gè)軟件的配置方法,需要的朋友可以參考下2015-04-04
Android原生側(cè)滑控件DrawerLayout使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android原生側(cè)滑控件DrawerLayout的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android項(xiàng)目開(kāi)發(fā) 教你實(shí)現(xiàn)Periscope點(diǎn)贊效果
這篇文章主要為大家分享了Android項(xiàng)目開(kāi)發(fā),一步一步教你實(shí)現(xiàn)Periscope點(diǎn)贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-12-12

