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来设置不同的平铺模式,这样可以达到动态变化的演示效果。
布局代码:
<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); } } }
自定义属性:
<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>
布局代码:
"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" />
默认样式:
-- Base application theme. -->