Android 绘图Shader之BitmapShader

来源:互联网 发布:java链表反序 编辑:程序博客网 时间:2024/05/25 05:37

Shader

Shader在计算机图形领域叫做着色器,是一组提供给GPU的绘图指令,用于告诉GPU在绘图时应该怎么绘制并对绘制的物体进行色彩渲染。

Android中定义了几种Shader给Paint使用,在Paint绘制图像时对其设置不同的Shader,绘制出来的物体就会使用Shader提供的信息进行着色。

Shader的子类有:BitmapShader、LinearGradient、ComposeShader、RadialGradient、SweepGradient

BitmapShader

BitmapShader是使用一张指定的图片给Paint进行着色,在绘制的时候会根据设置的TileMode(平铺模式)和图像来形成不同的效果,其中TileMode有

如下三种:

  • CLAMP 这种模式在绘制的时候如果绘制区域超出提供的图片的尺寸,超出区域会使用超出部分的边缘颜色进行着色

  • REPEAT 这种模式在绘制的时候如果绘制区域超出提供的图片的尺寸,超出区域会重新使用完整的图片进行着色

  • MIRROR 这种模式在绘制的时候如果绘制区域超出提供的图片的尺寸,超出区域会重新使用完整的图片进行着色,并且呈镜面反转效果

BitmapShader构造函数:

/** * 唯一的一个构造函数,调用这个构造函数构造一个新的BitmapShader * * @param bitmap            用于着色的bitmap对象 * @param tileX             水平方向的平铺模式. * @param tileY             垂直方向的平铺模式. */public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY) {

BitmapShader实例

public class BitmapShaderView extends View {    private Paint mPaint;    private Shader mShader;    /**     * 用于给Paint着色的图片     */    private Bitmap mBitmap;    public BitmapShaderView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        initial();    }    private void initial(){        mPaint = new Paint();        mPaint.setAntiAlias(true);        mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.sunwukong);        resetShader(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int width = getWidth();        int height = getHeight();        //使用设置了Shader的Paint进行绘制        canvas.drawRect(0,0,width,height,mPaint);    }    /**     * 重新设置BitmapShader     * @param tileX     * @param tileY     */    public void resetShader(Shader.TileMode tileX,Shader.TileMode tileY){        mShader = new BitmapShader(mBitmap, tileX, tileY);        //调用Paint的setShader(Shader shader)方法设置BitmapShader        mPaint.setShader(mShader);        invalidate();    }}

在代码中提供了一个public方法resetShader来设置不同的平铺模式,这样可以达到动态变化的演示效果。

布局代码:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical"    ><com.example.debugm.BitmapShaderView    android:id="@+id/shader_view"    android:layout_width="match_parent"    android:layout_height="100dp" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="clamp"        android:text="@string/bitmap_shader_clamp"        />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="mirror"        android:text="@string/bitmap_shader_mirror"        />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:onClick="repeat"        android:text="@string/bitmap_shader_repeat"        /></LinearLayout>

Activity代码:

public class MainActivity extends AppCompatActivity {    private BitmapShaderView mShaderView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mShaderView = (BitmapShaderView)findViewById(R.id.shader_view);    }    public void clamp(View view){        mShaderView.resetShader(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);    }    public void repeat(View view){        mShaderView.resetShader(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);    }    public void mirror(View view){        mShaderView.resetShader(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);    }}

通过点击不同的按钮切换不同的平铺模式

CLAMP效果图:

这里写图片描述

REPEAT效果图:

这里写图片描述

MIRROR效果图:

这里写图片描述

BitmapShader实际应用

BitmapShader在实际开发中还是会用到的,比如实现圆形ImageView,圆角ImageView

以下效果和代码只做演示用,具体使用到项目中还需要考虑其他详细的因素和条件

BitmapShader实现圆形ImageView和圆角ImageView

效果图:

这里写图片描述

代码:

public class RoundImageView extends ImageView {    private static final int RECTANGLE = 0;    private static final int CIRCULAR = 1;    private static final float DEFAULT_RADIUS = 50f;    private float mRadius = DEFAULT_RADIUS;    private int mRoundType = CIRCULAR;    private Paint mPaint;    private Bitmap mBitmap;    private Shader mShader;    public RoundImageView(Context context) {        this(context,null);    }    public RoundImageView(Context context,AttributeSet attrs) {        this(context, attrs,R.attr.roundImageViewStyle);    }    public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray a = context.obtainStyledAttributes(                attrs, R.styleable.RoundImageView, defStyleAttr, R.style.RoundImageViewStyle_Default);        mRadius = a.getDimension(R.styleable.RoundImageView_android_radius,DEFAULT_RADIUS);        mRoundType = a.getInt(R.styleable.RoundImageView_roundType, CIRCULAR);        a.recycle();        initialShader();    }    private void initialShader(){        mPaint = new Paint();        mPaint.setAntiAlias(true);        mBitmap = BitmapUtils.drawable2bitmap(getDrawable());        if(mBitmap != null){            mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);            mPaint.setShader(mShader);        }    }    private boolean isCircular(){        return  mRoundType == CIRCULAR;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        /*         *如果是圆形的话需要让View的宽高比例一样         */        if(isCircular()){            int width = Math.min(getMeasuredWidth(),getMeasuredHeight());            int height = Math.min(width,getMeasuredHeight());            setMeasuredDimension(width,height);        }    }    @Override    protected void onDraw(Canvas canvas) {        if(getDrawable() == null){            return;        }        int width = getWidth();        int height = getHeight();        float radius;        /*         *如果是圆形则绘制圆形图片,否则绘制圆角矩形         */        if(isCircular()){            radius = width/2;            canvas.drawCircle(width / 2,height / 2,radius,mPaint);        }else{            radius = mRadius;            canvas.drawRoundRect(0f,0f,width*1.0f,height*1.0f,radius,radius,mPaint);        }    }}

自定义属性:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="RoundImageView">        <attr name="android:radius" />        <attr name="roundType" format="enum">            <enum name="rectangle" value="0"/>            <enum name="circular" value="1"/>        </attr>    </declare-styleable>    <attr name="roundImageViewStyle" format="reference" /></resources>

布局代码:

<LinearLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical"    android:showDividers="middle"    android:divider="@drawable/divider_vertical"    >    <com.example.debugm.RoundImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@drawable/sunwukong" />    <com.example.debugm.RoundImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        app:roundType="rectangle"        android:radius="5dp"        android:src="@drawable/sunwukong2" /></LinearLayout>

默认样式:

<!-- Base application theme. --><style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">    <!-- Customize your theme here. -->    <item name="colorPrimary">@color/colorPrimary</item>    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>    <item name="colorAccent">@color/colorAccent</item>    <item name="roundImageViewStyle">@style/RoundImageViewStyle.Default</item></style><style name="RoundImageViewStyle.Default" parent="@android:style/Widget">    <item name="roundType">circular</item></style>
原创粉丝点击