"Android" 0 lines of code to achieve any shape picture Display--android-anyshape

Source: Internet
Author: User
<span id="Label3"></p>Objective<p><p>In Android development, we often encounter scenes that need to display images in some special shapes, such as rounded rectangles, circles, and so On. There are a number of options for how to draw such shapes, such as custom controls overriding the OnDraw method, drawing through the various draw methods of the canvas, and so On. so, What about more complex graphics? like, a pentagram? Like a combination of graphics? Or a variety of bizarre irregular shapes? Some students say that if you know the exact shape of an irregular shape, we can find the path by connecting vertices, and then draw it out by the DrawPath Method. Well... Very reasonable, but do not say some of the images, perhaps the vertex giant, or zigzag difficult to find the specific vertex, do we have to write a separate control for each special shape, or a set of independent code?<br>Sure it is, but I think it is better not to do So. So I have an idea, <strong>with a picture, to tell the control what shape I want, and then the control automatically follows this shape and helps me to show the picture</strong> . so, with this Project--android-anyshape.</p></p>Show<p><p><br>On the left is the use of the ordinary ImageView display effect, the right is the use of the project Anyshapeimageview Effect. To use Anyshapeimageview to reach the right-hand style, just provide three matte images, provided to the control via the "anyshapemask" parameter (explained below).<br>The three "masks" images are as Follows:<br><br>Unlike the normal matte picture, where the background of the picture is required to be <strong>completely transparent, that is, the alpha channel has a value of 0, and the graphic that needs to be displayed has no requirement for the specific color and is opaque</strong> .</p></p>Use<p><p>The use of the control is simple, because it inherits imageview, so the method is similar to imageview, but with one more important custom parameter: Anyshapemask</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-xml hljs "><span class="hljs-tag"><<span class="hljs-title">cn.lankton.anyshape.AnyshapeImageView</span> <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"150dp"</span> <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"150dp"</span> <span class="hljs-attribute">android:layout_marginTop</span>=<span class="hljs-value">"20dp"</span> <span class="hljs-attribute">android:src</span>=<span class="hljs-value">"@drawable/kumamon"</span> <span class="hljs-attribute">app:anyshapeMask</span>=<span class="hljs-value">"@drawable/singlestar"</span>/></span></code></pre></pre><p><p>Add this XML to the layout file, showing the Xiongben bear in the figure Above.<br>however, just by using this, we will find that the images are loaded very slowly. So it is highly recommended to call the Createpaths method of Pathinfomanager (and Anyshapeimageview in a package) at the beginning of the program, or anywhere before using anyshapeimageview, The parameter is a list of all possible matte picture resource ids.</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">new</span> ArrayList<>();ids.add(R.drawable.singlestar);ids.add(R.drawable.rings);ids.add(R.drawable.text);PathManager.getInstance().createPaths(<span class="hljs-keyword">this</span>, ids);</code></pre></pre><p><p><strong>See here, A friend might say I'm heading the party ... This code, however, does not have anything to do with drawing the graph itself, but rather an optimization for a faster display of the image, as described later in this article. </strong>It takes a lot of developers to do it by Themselves.</p></p>Realize<p><p>The idea of implementing this function is simply to get a path object that contains a collection of all the opaque pixels by scanning the transparency of each pixel of a mask image. Then it is very simple, through the DrawPath method of the canvas object, the image we want to display can be brushed up.</p></p>Extracting path from bitmap<p><p><strong>This is the most important part of the project</strong> . The code is as Follows:<br>Pathinfomanager.getpathfrombitmap:</p></p><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword"><span class="hljs-keyword"></span> public</span>Path<span class="hljs-title"><span class="hljs-title">Getpathfrombitmap</span></span>(Bitmap Mask) {path Path =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span>Path ();<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>Bwidth = Mask.getwidth ();<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>Bheight = Mask.getheight ();<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>[] origin =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span> <span class="hljs-keyword"><span class="hljs-keyword">int</span></span>[bwidth];<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>lasta;<span class="hljs-keyword"><span class="hljs-keyword"></span> for</span>(<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>i =<span class="hljs-number"><span class="hljs-number">0</span></span>; I < bheight; I++) {mask.getpixels (origin,<span class="hljs-number"><span class="hljs-number">0</span></span>, bwidth,<span class="hljs-number"><span class="hljs-number">0</span></span>, i, bwidth,<span class="hljs-number"><span class="hljs-number">1</span></span>); Lasta =<span class="hljs-number"><span class="hljs-number">0</span></span>;<span class="hljs-keyword"><span class="hljs-keyword"></span> for</span>(<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>j =<span class="hljs-number"><span class="hljs-number">0</span></span>; J < bwidth; J + +) {<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>A = Color.alpha (origin[j]);<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(a! =<span class="hljs-number"><span class="hljs-number">0</span></span>&& Lasta = =<span class="hljs-number"><span class="hljs-number">0</span></span>) {path.moveto (j, i); }<span class="hljs-keyword"><span class="hljs-keyword">Else</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(a = =<span class="hljs-number"><span class="hljs-number">0</span></span>&& lasta! =<span class="hljs-number"><span class="hljs-number">0</span></span>) {path.lineto (j-<span class="hljs-number"><span class="hljs-number">1</span></span>, i); }<span class="hljs-keyword"><span class="hljs-keyword">Else</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(a! =<span class="hljs-number"><span class="hljs-number">0</span></span>&& j = bwidth-<span class="hljs-number"><span class="hljs-number">1</span></span>) {path.lineto (j, i); } Lasta = a; } }<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>path;}</code></pre><p><p>The solution I designed was simple, scanning the pixels in the bitmap line by row, using the Getpixels method to get a pixel array for each row, and then traversing the Analysis. The steps are as Follows:<br>1. Encounter an opaque pixel, make a judgment, if its previous pixel is opaque, or it itself is the beginning of the line, then we think of it as an opaque area of the beginning, through the MoveTo method to move the path to this point;<br>2. When a transparent pixel is encountered and judged, if its previous pixel is transparent, then we think of it as the end of an opaque area, connecting it to the previous pixel in LineTo way.<br>3. Repeat steps 1 and 2 until the scan is Complete. It is important to note that if the end of the line is opaque pixels, it is connected directly. Prevent the last opaque area from starting with no end Point.<br>In this way, the result of the connection of each row makes up the scan result of the whole picture ~</p></p>Through path, the image is displayed<p><p>First look at the Anyshapeimageview initialization method:</p></p><pre class="prettyprint"><code class=" hljs avrasm">Public Anyshapeimageview (context context, attributeset attrs, int defstyleattr) {super (context, attrs, defstyleattr)<span class="hljs-comment"><span class="hljs-comment">;</span></span>This<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Context</span></span>= Context<span class="hljs-comment"><span class="hljs-comment">;</span></span>TypedArray A = Context<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Gettheme</span></span>()<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Obtainstyledattributes</span></span>(attrs, R<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Styleable</span></span><span class="hljs-preprocessor"><span class="hljs-preprocessor">. Anyshapeimageview</span></span>, defstyleattr,<span class="hljs-number"><span class="hljs-number">0</span></span>)<span class="hljs-comment"><span class="hljs-comment">;</span></span>int n = A<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetIndexCount</span></span>()<span class="hljs-comment"><span class="hljs-comment">;</span></span>for (int i =<span class="hljs-number"><span class="hljs-number">0</span></span><span class="hljs-comment"><span class="hljs-comment">; i < n; I++)</span></span>{final int attr = a<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetIndex</span></span>(i)<span class="hljs-comment"><span class="hljs-comment">;</span></span>if (attr = = R<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Styleable</span></span><span class="hljs-preprocessor"><span class="hljs-preprocessor">. Anyshapeimageview</span></span>_anyshapemask) {int Maskresid = a<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Getresourceid</span></span>(attr,<span class="hljs-number"><span class="hljs-number">0</span></span>)<span class="hljs-comment"><span class="hljs-comment">;</span></span>if (<span class="hljs-number"><span class="hljs-number">0</span></span>= = Maskresid) {//did not<span class="hljs-keyword"><span class="hljs-keyword">Set</span></span>Mask continue<span class="hljs-comment"><span class="hljs-comment">;</span></span>} PathInfo pi = PathManager<span class="hljs-preprocessor"><span class="hljs-preprocessor">. getinstance</span></span>()<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetPathInfo</span></span>(maskresid)<span class="hljs-comment"><span class="hljs-comment">;</span></span>If (null! = Pi) {originmaskpath = Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Path</span></span><span class="hljs-comment"><span class="hljs-comment">;</span></span>Originmaskwidth = Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Width</span></span><span class="hljs-comment"><span class="hljs-comment">;</span></span>Originmaskheight = Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Height</span></span><span class="hljs-comment"><span class="hljs-comment">;</span></span>} else {Bitmap Maskbitmap = bitmapfactory<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Decoderesource</span></span>(context<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Getresources</span></span>(), A<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Getresourceid</span></span>(attr,<span class="hljs-number"><span class="hljs-number">0</span></span>))<span class="hljs-comment"><span class="hljs-comment">;</span></span>Originmaskpath = PathManager<span class="hljs-preprocessor"><span class="hljs-preprocessor">. getinstance</span></span>()<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Getpathfrombitmap</span></span>(maskbitmap)<span class="hljs-comment"><span class="hljs-comment">;</span></span>Originmaskwidth = Maskbitmap<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetWidth</span></span>()<span class="hljs-comment"><span class="hljs-comment">;</span></span>Originmaskheight = Maskbitmap<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetHeight</span></span>()<span class="hljs-comment"><span class="hljs-comment">;</span></span>Pi = new PathInfo ()<span class="hljs-comment"><span class="hljs-comment">;</span></span>Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Height</span></span>= Originmaskheight<span class="hljs-comment"><span class="hljs-comment">;</span></span>Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Width</span></span>= Originmaskwidth<span class="hljs-comment"><span class="hljs-comment">;</span></span>Pi<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Path</span></span>= Originmaskpath<span class="hljs-comment"><span class="hljs-comment">;</span></span>PathManager<span class="hljs-preprocessor"><span class="hljs-preprocessor">. getinstance</span></span>()<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Addpathinfo</span></span>(maskresid, Pi)<span class="hljs-comment"><span class="hljs-comment">;</span></span>Maskbitmap<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Recycle</span></span>()<span class="hljs-comment"><span class="hljs-comment">;</span></span>}} else if (attr = = R<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Styleable</span></span><span class="hljs-preprocessor"><span class="hljs-preprocessor">. Anyshapeimageview</span></span>_anyshapebackcolor) {backColor = A<span class="hljs-preprocessor"><span class="hljs-preprocessor">. GetColor</span></span>(attr, Color<span class="hljs-preprocessor"><span class="hljs-preprocessor">. TRANSPARENT</span></span>)<span class="hljs-comment"><span class="hljs-comment">;</span></span>}} A<span class="hljs-preprocessor"><span class="hljs-preprocessor">. Recycle</span></span>()<span class="hljs-comment"><span class="hljs-comment">;</span></span>}public anyshapeimageview (context Context) {this (context, Null)<span class="hljs-comment"><span class="hljs-comment">;</span></span>}public anyshapeimageview (context context, attributeset attrs) {this (context, attrs,<span class="hljs-number"><span class="hljs-number">0</span></span>)<span class="hljs-comment"><span class="hljs-comment">;</span></span>}</code></pre><p><p>In fact, it is called through the Anyshapemask parameter, get the resource ID of the "mask" picture, and then generate the bitmap through the resource ID to obtain the path contained Therein. At the same time, the width and height of the bitmap object are recorded and stored in a pathinfo object.<br>PathInfo:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class=" hljs cs"><span class="hljs-keyword">public</span><span class="hljs-keyword">class</span> PathInfo { <span class="hljs-keyword">public</span> Path path; <span class="hljs-keyword">public</span><span class="hljs-keyword">int</span> width; <span class="hljs-keyword">public</span><span class="hljs-keyword">int</span> height;}</code></pre></pre><p><p>however, we have seen that the user to generate Bitmap-get path this series of time-consuming, Memory-intensive operations, The first will determine whether the cache has a match with the resource ID pathinfo, if there is, do not do this part of the Operation. If not, the PathInfo object is generated and cached based on the resource ID passed IN.<br>About this cache, as described below.</p></p><p><p>Look again at the onsizechanged method:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="hljs java"><span class="hljs-keyword">protected </span> <span class="hljs-keyword"> void </span> <span class="hljs-title">onsizechanged </span> (<span class="hljs-keyword">int </span> w, <span class=" Hljs-keyword ">int </span> h, <span class=" hljs-keyword ">int </span> oldw, <span class=" hljs-keyword ">int </span> oldh) {<span class="hljs-keyword">super </span>. onsizechanged (w, h, oldw, oldh); Vheight = GetHeight (); Vwidth = GetWidth (); <span class="hljs-keyword">if </span> (originmaskpath! = <span class="hljs-keyword">null </span>) {<span class="hljs-comment">//scale the size of the path to fit the one of this View </span> matrix matrix = <span class="hljs-keyword">new </span> Matrix (); Matrix.setscale (vwidth * <span class="hljs-number">1 </span> f/originmaskwidth, vheight * <span class="hljs-number">1 </span> f/originmaskheight); Originmaskpath.transform (matrix, realmaskpath); }}</code></pre></pre><p><p>The main purpose of the code here is to scale the path object to match the actual size of the Control. As you can see, <strong>if the shape you do not want to show is stretched or deformed, then the aspect ratio of the anyshapeimageview, preferably the width-to-height ratio of the matte picture, remains the Same. </strong></p></p><p><p>The next step is to draw the shape and brush the pieces in the Ondraw:</p></p><pre class="prettyprint"><code class=" hljs java"><span class="hljs-annotation"><span class="hljs-annotation">@Override</span></span><span class="hljs-keyword"><span class="hljs-keyword">protected</span></span> <span class="hljs-keyword"><span class="hljs-keyword">void</span></span> <span class="hljs-title"><span class="hljs-title">OnDraw</span></span>(canvas Canvas) {<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>= = Originmaskpath) {<span class="hljs-comment"><span class="hljs-comment">//if The Mask is null, the view would work as a normal ImageView</span></span> <span class="hljs-keyword"><span class="hljs-keyword">Super</span></span>. OnDraw (canvas);<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>; }<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(vwidth = =<span class="hljs-number"><span class="hljs-number">0</span></span>|| Vheight = =<span class="hljs-number"><span class="hljs-number">0</span></span>) {<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>; } Paint.reset (); Paint.setstyle (Paint.Style.STROKE);<span class="hljs-comment"><span class="hljs-comment">//get the drawable to SHOW. if not set the src,</span> would use BackColor</span>drawable showdrawable = getdrawable ();<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>! = Showdrawable) {Bitmap showbitmap = ((bitmapdrawable) showdrawable). getbitmap (); Shader Shader =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span>Bitmapshader (showbitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Matrix Shadermatrix =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span>Matrix ();<span class="hljs-keyword"><span class="hljs-keyword">float</span></span>ScaleX = Vwidth *<span class="hljs-number"><span class="hljs-number">1.0</span></span>F/showbitmap.getwidth ();<span class="hljs-keyword"><span class="hljs-keyword">float</span></span>ScaleY = Vheight *<span class="hljs-number"><span class="hljs-number">1.0</span></span>F/showbitmap.getheight (); Shadermatrix.setscale (scaleX, scaleY); Shader.setlocalmatrix (shadermatrix); Paint.setshader (shader); }<span class="hljs-keyword"><span class="hljs-keyword">Else</span></span>{<span class="hljs-comment"><span class="hljs-comment">//no src, Use the BackColor to fill the path</span></span>Paint.setcolor (backColor); } Canvas.drawpath (realmaskpath, paint);}</code></pre>Cache<p><p>Look at the above blog post, you must be aware that the resource ID passed in as a parameter is actually just to get a path object. Then we can create a integer-path mapping relationship that caches the path that has been read. The following path is required, only through the resource ID to the cache to find, after all, read path is a time-consuming and resource-based Operation.<br>In this way, we have optimized the use of anyshapeimageview, after all, the same shape of the display, we just do one time from the image to resolve the path object Operation. But That's not enough, and There's still a lag in the speed at which the pictures are first shown. (i have done statistics, need almost seconds, people can also have obvious perception.) of course, This number is also related to the size of the mask image, so it is not recommended to use too big a picture.<br>Pathinfomanager provides an initialization scheme for this Issue. Provides an initialization method that can be called at the beginning of a program, such as Application's OnCreate method, to generate a path object to Cache. Of course, It is also possible to call elsewhere, but it has no meaning until the Anyshapeimageview is Displayed.</p></p><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword"><span class="hljs-keyword"></span> public</span> <span class="hljs-keyword"><span class="hljs-keyword">void</span></span> <span class="hljs-title"><span class="hljs-title">createpaths</span></span>(context context, list<integer> Reslist) {<span class="hljs-keyword"><span class="hljs-keyword"></span> for</span>(Integer Resid:reslist) {<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(resId ><span class="hljs-number"><span class="hljs-number">0</span></span>) {pathasynctask task =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span>Pathasynctask (context); Task.execute (resId); }}}class Pathasynctask extends Asynctask <integer, Void, path> {<span class="hljs-keyword"><span class="hljs-keyword">Private</span></span>Context context;<span class="hljs-keyword"><span class="hljs-keyword"></span> public</span> <span class="hljs-title"><span class="hljs-title">Pathasynctask</span></span>(context Context) {<span class="hljs-keyword"><span class="hljs-keyword">Super</span></span>();<span class="hljs-keyword"><span class="hljs-keyword"></span> this</span>. Context = context; }<span class="hljs-annotation"><span class="hljs-annotation">@Override</span></span> <span class="hljs-keyword"><span class="hljs-keyword">protected</span></span>Path<span class="hljs-title"><span class="hljs-title">Doinbackground</span></span>(Integer ... Params) {<span class="hljs-keyword"><span class="hljs-keyword">int</span></span>ResId = params[<span class="hljs-number"><span class="hljs-number">0</span></span>]; PathInfo pi =<span class="hljs-keyword"><span class="hljs-keyword">New</span></span>PathInfo (); Bitmap Maskbitmap = Bitmapfactory.decoderesource (context.getresources (), resId); Pi.path = pathmanager.getinstance (). getpathfrombitmap (maskbitmap); Pi.width = Maskbitmap.getwidth (); Pi.height = Maskbitmap.getheight ();<span class="hljs-comment"><span class="hljs-comment">//creating is do, add the path info into the cache</span></span>Pathmanager.getinstance (). addpathinfo (resId, pi); Maskbitmap.recycle ();<span class="hljs-keyword"><span class="hljs-keyword">return</span></span> <span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>; }}</code></pre><p><p><strong>As you can see, this method uses the Asynctask to execute the initialization of path asynchronously in the background, so you can safely invoke it without worrying about blocking.</strong></p></p>Summarize<p><p>This project, it took me almost a week to complete the Intermittent. Code is not much, is not complex, hope to help everyone, or provide some ideas for EVERYONE.<br>Then put the address of the project, including demo:<br>Https://github.com/lankton/android-anyshape<br>If you feel this project, or this blog post has helped you a bit, welcome star support ~</p></p> <p><p> "Android" 0 lines of code to implement any shape picture Display--android-anyshape </p> </p></span>

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.