Public Bitmapshader (Bitmap bitmap,shader.tilemode tilex,shader.tilemode Tiley)
Call this class to produce a renderer (Shader) that has a bitmap painted.
Bitmap: Bitmaps used within the renderer
(1) tilex:the tiling mode for x to draw the bitmap in. X-direction tile mode on bitmap
(2) tiley:the tiling mode for y-draw the bitmap in. Y-Direction tile mode on bitmap
Tilemode: (a total of three)
(1) Clamp: If the renderer exceeds the original boundary range, it will replicate the range of edge staining.
(2) REPEAT: Horizontal and vertical Repeat renderer picture, tiled.
(3) MIRROR: Horizontal and vertical Repeat renderer picture, this and repeat repeat the way is different, he is tiled in a mirrored way.
Still don't get it? Then look at the effect chart!
For our rounded corners and circles, we set the pattern to be clamp, but would you have a question:
Is the view wider or taller than the width or height of our bitmap stretching?
Well, we'll set up a matrix for bitmapshader to zoom in or out of the picture, not to make "view wider or taller than our bitmap wide or high" condition.
The principle of our basic introduction finished, get drawable into bitmap, and then directly initialize Bitmapshader, brush set shader, finally in the OnDraw inside to draw a circle on the line.
Basic usage and examples
Let's take a look at the rounded or rounded corners that are implemented using Bitmapshader.
We are here to inherit ImageView directly, so that the code to set up the picture will be more familiar; but we need to support two modes, then we need custom attributes:
1, custom properties
Values/attr.xml
<?xml version= "1.0" encoding= "Utf-8"?>
<resources> <attr name= "Borderradius" format= "
Dimension "/>
<attr name=" type ">
<enum name=" Circle "value=" 0 "/> <enum
" Name= " value= "1"/>
</attr>
<declare-styleable name= "Roundimageview" >
<attr name= " Borderradius "/>
<attr name=" type "/>
</declare-styleable>
</resources>
We define an enumeration and a rounded corner size Borderradius.
2. Get Custom properties
public class Roundimageview extends ImageView {/** * picture type, round or rounded/private int type;
private static final int type_circle = 0;
private static final int type_round = 1;
/** * The default value for fillet size * * private static final int boder_radius_default = 10;
/** * The size of the rounded angle * * private int mborderradius;
/** * Drawing Paint * * Private Paint mbitmappaint;
/** * The radius of the rounded corner * * private int mradius;
/** * 3x3 matrix, mainly used to reduce amplification * * Private matrix Mmatrix;
/** * Rendering images, using images for drawing graphics coloring * * Private bitmapshader Mbitmapshader;
/** * View Width * * private int mwidth;
Private RECTF Mroundrect;
Public Roundimageview (context, AttributeSet attrs) {Super (context, attrs);
Mmatrix = new Matrix ();
Mbitmappaint = new Paint ();
Mbitmappaint.setantialias (TRUE);
TypedArray a = Context.obtainstyledattributes (Attrs, R.styleable.roundimageview); Mborderradius = A.getdimensionpixelsize (R.styleable.roundimageview_borderradius, (int) Typedvalue applydimension (Typedvalue.complex_unit_dip, Boder_radius_default, Getresources ()
. Getdisplaymetrics ());//default 10DP type = A.getint (R.styleable.roundimageview_type, type_circle);//default to CIRCLE
A.recycle ();
}
We can see some of our member variables, basically annotated, and then we get our custom attributes in the constructor and the initialization of some of the variables.
3, Onmeasure
@Override
protected void onmeasure (int widthmeasurespec, int heightmeasurespec)
{
log.e ("TAG", " Onmeasure ");
Super.onmeasure (Widthmeasurespec, heightmeasurespec);
/**
* If the type is circular, it forces the view to change the width-height consistency, with a decimal value of quasi-
*
/if (type = = type_circle)
{
mwidth = math.min ( Getmeasuredwidth (), Getmeasuredheight ());
Mradius = MWIDTH/2;
Setmeasureddimension (Mwidth, mwidth);
}
We have made a onmeasure method, which is mainly used when setting the type to circular, we force the view width and height to be consistent.
The next step is to set the Bitmapshader and draw the
4, set Bitmapshader
/**
* Initialize Bitmapshader/
private void Setupshader ()
{
drawable drawable = getdrawable ();
if (drawable = = null)
{return
;
}
Bitmap bmp = Drawabletobitamp (drawable);
BMP as a shader, is in the specified area to draw bmp
Mbitmapshader = new Bitmapshader (BMP, Tilemode.clamp, tilemode.clamp);
float scale = 1.0f;
if (type = = type_circle)
{
//Get bitmap width or high small value
int bsize = Math.min (Bmp.getwidth (), Bmp.getheight ());
Scale = Mwidth * 1.0f/bsize;
} else if (type = = Type_round)
{
//if the width or height of the picture does not match the width of the view, calculate the scale that needs to be scaled; the width of the scaled picture must be greater than the width of our view; so we take the big value here;
scale = Math.max (getwidth () * 1.0f/bmp.getwidth (), getheight ()
* 1.0f/bmp.getheight ());
Shader transformation matrix, where we are mainly used to enlarge or shrink
mmatrix.setscale (scale, scale);
Setting the transformation matrix
Mbitmapshader.setlocalmatrix (Mmatrix);
Set shader
Mbitmappaint.setshader (mbitmapshader);
In the Setupshader, first to drawable into our bitmap;
Then initialize
Mbitmapshader = new Bitmapshader (BMP, Tilemode.clamp, Tilemode.clamp);
Next, compute the scale according to the type and the width of the bitmap and view;
About the calculation of scale:
(1) When the circle: Take the bitmap width or high value as the benchmark, if the use of large value, scaling must not fill our rounded area. Then, the view of the mwidth/bsize; Get is scale.
(2) Fillet: Because the design to the wide/high proportion, we respectively getwidth () * 1.0f/bmp.getwidth () and GetHeight () * 1.0f/bmp.getheight (); The final large value, because we want to make the final zoom complete the picture must be The area larger than our view, somewhat similar to the Centercrop;
(3) For example: view of the width of 10*20; the width of the picture is 5*100; In the end, we should enlarge it by a wide scale, not by a high proportion, because we need to make the scaled picture, the custom larger than our view wide, and guarantee the original ratio.
With the scale, you can set the matrix to us;
Then use Mbitmapshader.setlocalmatrix (Mmatrix);
Finally, set the Bitmapshader to paint.
About drawable to bitmap code:
/**
* drawable Bitmap *
*
* @param drawable
* @return
/private Bitmap Drawabletobitamp ( Drawable drawable)
{
if (drawable instanceof bitmapdrawable)
{
bitmapdrawable bd = (bitmapdrawable) drawable;
return Bd.getbitmap ();
}
int w = drawable.getintrinsicwidth ();
int h = drawable.getintrinsicheight ();
Bitmap Bitmap = Bitmap.createbitmap (W, H, Bitmap.Config.ARGB_8888);
Canvas Canvas = new Canvas (bitmap);
Drawable.setbounds (0, 0, W, h);
Drawable.draw (canvas);
return bitmap;
}
Finally we will call Setupshader () in the OnDraw and then draw.
5. Draw
here, the last step is drawn, because our range, as well as the scaling, is done, so it's really just a draw.
@Override
protected void OnDraw (Canvas Canvas)
{
if (getdrawable () = null)
{return
;
}
Setupshader ();
if (type = = Type_round)
{
canvas.drawroundrect (mroundrect, Mborderradius, Mborderradius,
mbitmappaint );
} else
{
canvas.drawcircle (Mradius, Mradius, Mradius, mbitmappaint);
Drawsomething (canvas);
}
@Override
protected void onsizechanged (int w, int h, int oldw, int oldh)
{
super.onsizechanged (W, H, OLDW, OLDH);
Fillet picture Range
if (type = = Type_round)
mroundrect = new RECTF (0, 0, getwidth (), getheight ());
Drawing is very simple, draw a circle, rounded rectangle or something. The bounded range of rounded rectangles is mroundrect in the onsizechanged.
6, the state of storage and recovery
of course, if there is not enough memory and it happens that our activity is in the background, it is unfortunately restarted, or the user spins the screen causing the activity to restart, our view should also be able to save its own attributes as much as possible.
What is the use of State preservation? For example, now one of the fillet size is 10DP, the user clicks into 50DP, after the user rotated, or after a long time in the background, return to our activity should still be 50dp;
We simply store the current type as well as the Mborderradius
private static final String state_instance =" State_instance ";
private static final String State_type = "State_type";
private static final String State_border_radius = "State_border_radius";
@Override protected parcelable onsaveinstancestate () {Bundle Bundle = new Bundle ();
Bundle.putparcelable (State_instance, Super.onsaveinstancestate ());
Bundle.putint (State_type, TYPE);
Bundle.putint (State_border_radius, Mborderradius);
return bundle; } @Override protected void Onrestoreinstancestate (parcelable state) {if (state instanceof Bundle) {Bun
Dle bundle = (bundle) state;
Super.onrestoreinstancestate ((Bundle) state). Getparcelable (state_instance));
This.type = Bundle.getint (State_type);
This.mborderradius = Bundle.getint (State_border_radius);
else {super.onrestoreinstancestate (state); }
}
The code is relatively simple. The demo in our article, the first one, the fourth one can be clicked, click will change, you can click, then rotate the screen to test.
At the same time, we have published two methods for dynamically modifying the fillet size and type
public void Setborderradius (int borderradius)
{
int pxval = dp2px (Borderradius);
if (This.mborderradius!= pxval)
{
This.mborderradius = pxval;
Invalidate ();
}
public void SetType (int type)
{
if (this.type!= type)
{
this.type = type;
if (this.type!= type_round && this.type!= type_circle)
{
this.type = type_circle;
}
Requestlayout ();
}
public int dp2px (int dpval)
{return
(int) typedvalue.applydimension (Typedvalue.complex_unit_dip,
Dpval, Getresources (). Getdisplaymetrics ());
Finally, stick to our layout files and mainactivity.
6. Call
Layout file:
<scrollview xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http:// Schemas.android.com/tools "xmlns:zhy=" Http://schemas.android.com/apk/res/com.zhy.variousshapeimageview "Android: Layout_width= "Match_parent" android:layout_height= "wrap_content" > <linearlayout android:layout_width= "MATC" H_parent "android:layout_height=" match_parent "android:orientation=" vertical "> <com.zhy.view.roundimagev Iew android:id= "@+id/id_qiqiu" android:layout_width= "wrap_content" android:layout_height= "Wrap_content" an Droid:layout_margin= "10DP" android:src= "@drawable/qiqiu" > </com.zhy.view.RoundImageView> <com.zhy . View. Roundimageview android:layout_width= "200DP" android:layout_height= "200DP" android:layout_margin= "10DP" and roid:src= "@drawable/aa" > </com.zhy.view.RoundImageView> <com.zhy.view.roundimageview Android:layo Ut_width= "Wrap_content" android:layout_height= "Wrap_content" android:layout_margin= "10DP" android:src= "@drawable/icon" > </com.zhy.view.roundimageview > <com.zhy.view.roundimageview android:id= "@+id/id_meinv" android:layout_width= "Wrap_content" Andro id:layout_height= "Wrap_content" android:layout_margin= "10DP" android:src= "@drawable/aa" zhy:borderradius= "20DP "Zhy:type=" Round "> </com.zhy.view.RoundImageView> <com.zhy.view.roundimageview android:layou T_width= "Wrap_content" android:layout_height= "wrap_content" android:layout_margin= "10DP" android:src= "@drawabl E/icon "zhy:borderradius=" 40DP "zhy:type=" Round "> </com.zhy.view.RoundImageView> <com.zhy.vie W.roundimageview android:layout_width= "wrap_content" android:layout_height= "Wrap_content" Android:layout_margi N= "10DP" android:src= "@drawable/qiqiu" zhy:borderradius= "60DP" zhy:type= "Round" > </com.zhy.view.roun Dimageview> </liNearlayout> </ScrollView>
No, scrollview inside a linear layout, inside a bunch of roundimageview.
Mainactivity
package com.zhy.variousshapeimageview;
Import android.app.Activity;
Import Android.os.Bundle;
Import Android.view.View;
Import Android.view.View.OnClickListener;
Import Com.zhy.view.RoundImageView;
public class Mainactivity extends activity {private Roundimageview Mqiqiu;
Private Roundimageview MMEINV;
@Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
Setcontentview (R.layout.activity_main);
Mqiqiu = (Roundimageview) Findviewbyid (R.id.id_qiqiu);
MMEINV = (Roundimageview) Findviewbyid (R.ID.ID_MEINV); Mqiqiu.setonclicklistener (New Onclicklistener () {@Override public void OnClick (View v) {Mqiqiu.setty
PE (ROUNDIMAGEVIEW.TYPE_ROUND);
}
}); Mmeinv.setonclicklistener (New Onclicklistener () {@Override public void OnClick (View v) {Mmeinv.
Setborderradius (90);
}
}); }
}
The final effect chart: