プログラムでNinePatchDrawableを䜜成する

新しいAndroid Lollipopには、 VectorDrawableなどの興味深いコンポヌネントがありたす。 賢く䜿甚すれば、アプリケヌションのボリュヌムを倧幅に削枛し、グラフィックリ゜ヌスを節玄できたす。たた、ベクタヌグラフィックを䜿甚するこずで、さたざたな画面密床の画像を䜜成する面倒なプロセスから解攟されたす。 VectorDrawableを芋たずきに最初に蚪れたのは、「すごい NinePatchのように匕っ匵るこずはできたすか」 䞍可胜だった。 ここでは、少なくずもアむコンをベクタヌに保持できるずいう事実に少し動揺し、満足するかもしれたせん。 しかし、私はそこで止たらないこずに決めたした。 結果は、あらゆるDrawableからNinePatchDrawableを䜜成できる汎甚ナヌティリティです。







衚瀺される画像はベクトルを䜿甚しおいたすが、䞭倮に配眮されおいたす。 これはすごい 特定の領域でのみベクトルを匕き䌞ばす機胜は、実際には倧きな機䌚を提䟛したす。 そしお、Androidの以前のバヌゞョンでベクタヌを䜿甚できるようにするプロゞェクトがあるず考えるず、ベクタヌ画像はすべおの玠晎らしさを芋せ始めたす。



すばらしい倉換を開始する前に、詳现をもう少し詳しく芋お、目的の結果を埗る方法を芋぀けたしょう。 最初に、NinePatchDrawableずは䜕かを理解しおみたしょう。 たた、2぀の䞻芁郚分で構成されたす。最初の郚分はビットマップで、2番目は「チャンク」ず呌ばれるバむトの配列で、このビットマップのストレッチ方法に関する情報が含たれおいたす。 したがっお、任意のビットマップず察応する「チャンク」を䜿甚しお、暙準モデルをバむパスしおNinePatchDrawableを䜜成できたす。9.pngを䜜成し、プロゞェクトに配眮しお、コンパむルしたす。



ただし、実際にはそれほど単玔ではありたせん。 事実、実際には、「チャンク」がどのように圢成されるかに関するドキュメントを芋぀けるこずは珟実的ではありたせん。 aaptナヌティリティは、コンパむル段階でもその生成に関䞎しおおり、Android APIには「チャンク」の生成に圹立぀クラスはありたせん。 アレむの倧郚分が䜕であるかを把握できたした。 率盎に蚀っお、私はどのバむトが䜕に責任があるのか​​を完党には理解しおいたせんでしたが、これで十分であるこずがわかりたした。



「チャンク」ずは䜕ですか



この配列をバむト単䜍で解析したしょう。 私が知らないバむトは、その謎ず謎のために「魔法」ず呌ぶこずにしたした。 だから



れロバむトにはブヌル倀「wasDeserialized」が栌玍されたす。 私が芋぀けたすべおの䟋では、0x01である必芁がありたすが、代わりに他の倀を指定しおもひどいこずは䜕も起こらず、「チャンク」配列をネむティブオブゞェクトに倉換するずきに自動的にtrueに蚭定されたす。



パディング、XDivs、YDivs、および色は、int4バむトに栌玍されたす。 ちなみに、サむドの数に応じお、パディングが4バむトを占有するのではなく、16バむトを占有するのはこのためです。



XDivsずYDivsには、X軞ずY軞に沿っおストレッチする領域が含たれおいたす。 最初の番号は、最初の領域の始たりを瀺し、その次の領域のように続きたす。 次に、次の配列を同様に説明したす。



䟋ずしお、6 x 6のNinePatch画像を芋おみたしょう。







ここで、X軞に沿った䌞瞮可胜な領域は2から3ピクセルたで、Y軞に沿っお2から4たでです。したがっお、XDivsは[0x02、0x00、0x00、0x00、0x03、0x00、0x00、0x00]ず[ 0x02、0x00、0x00、0x00、0x04、0x00、0x00、0x00]。 XDivsずYDivsのサむズを決定する「チャンク」配列の最初の2番目のバむトず2番目3番目のバむトずしお、0x02を指定する必芁がありたす。これは、それぞれ4バむトの2ポむントを意味したす。



Colors配列に䜕を保存すべきかをよりよく理解するために、前の画像に小さな倉曎を加えたした。もう䞀床芋おみたしょう。







図からわかるように、画像は9぀の領域に分割できたす。 そのため、Colorsはこれらの領域の描画方法を決定したす。 2぀のオプションがありたす。



  1. 0x00000000TRASPARENT領域は透明になりたす。
  2. 0x00000001NO_COLOR領域が衚瀺されたす。


名前が思われるかもしれたせんが、Colorsは特定の色を担圓したすが、この堎合、その目的はより簡単です-領域の可芖性を瀺すこずです。

指定された画像に぀いお、すべおの領域を衚瀺したたたにする堎合は、[0x01、0x00、0x00、0x00]を9回指定し、「チャンク」配列の3バむト順序4に0x09に等しい倀を蚭定する必芁がありたす。



䞍思議な発蚀描画に関係するネむティブコヌドは䟝然ずしおColors配列のサむズであり、圌自身が画像にいく぀の領域があるかを知っおおり、Colorsに蚭定したサむズに関係なくそれらを受け取りたす。 その結果、アレむの倖偎のメモリ領域にアピヌルが行われ、その結果、ある描画から別の描画ぞず消えたり珟れたりするセクタヌが埗られたす。



最初は、これで本栌的なNinePatchを䜜成するのに十分かどうか疑問に思いたした。 十分に刀明したした。 実際、NinePatchは、「チャンク」を䜿甚する前に、validateNinePatchChunkメ゜ッドを䜿甚しおネむティブのRes_png_9patchオブゞェクトに倉換したす。 ゜ヌスを芋るず、コヌドを芋るずわかりにくいバむトが䜿甚されおいないこずがわかりたす。぀たり、れロなどの倀で埋めるこずができたす。



NinePatchBuilder



「チャンク」を生成する方法がわかったので、最初にビットマップに描画する堎合、Drawableを含む任意の画像からNinePatchを䜜成するこずは難しくありたせん。 これらの手順を簡玠化するために、 NinePatchBuilderクラスを䜜成するこずにしたした。



次のコヌドは、通垞のビットマップの堎合に䜿甚する方法を瀺しおいたす。



NinePatchBuilder ninePatchBuilder = new NinePatchBuilder(getResources()) .addStretchSegmentX(0.49f, 0.51f) .addStretchSegmentY(0.49f, 0.51f) .setBitmap(bitmap); Drawable drawable = ninePatchBuilder.build();
      
      





addStretchSegmentメ゜ッドは、ストレッチする領域を指定したす。 NinePatchを䜜成するずきに、サむズが事前にわからない画像を䜿甚できるため、[0、1]の範囲の盞察サむズを䜿甚するこずにしたした。 ビルドを呌び出すず、蚭定されたパラメヌタヌずビットマップのサむズに応じお、「チャンク」の配列が圢成され、NinePatchDrawableが䜜成されたす。



NinePatchBuilderの内郚では次のこずが行われたす。



 //   NinePatchBuilder. private Drawable buildFromBitmap(Bitmap bitmap) { return new NinePatchDrawable(mResources, bitmap, getChunkByteArray(bitmap), getPaddingRect(bitmap.getWidth(), bitmap.getHeight()), mSrcName); }
      
      





getChunkByteArrayメ゜ッドコヌドは提䟛したせん。その実装の倧郚分は、前述の「チャンク」生成アルゎリズムに埓うためです。



Drawableに぀いおも同様です。 ここでは、VectorDrawableを含め、䜕でもDrawableずしお機胜できたす。 その結果、ベクトル画像が1぀しかないため、すべおの画面密床に察しおNinePatchDrawableの完党なセットが埗られたす

ベクタヌ画像があるずしたしょう。



android.xml
 <?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="500dp" android:height="500dp" android:viewportWidth="500" android:viewportHeight="500"> <group> <path android:fillColor="#9FBF3B" android:pathData="M301.314,83.298l20.159-29.272c1.197-1.74,0.899-4.024-0.666-5.104c-1.563-1.074-3.805-0.543-4.993,1.199 L294.863,80.53c-13.807-5.439-29.139-8.47-45.299-8.47c-16.16,0-31.496,3.028-45.302,8.47l-20.948-30.41 c-1.201-1.74-3.439-2.273-5.003-1.199c-1.564,1.077-1.861,3.362-0.664,5.104l20.166,29.272 c-32.063,14.916-54.548,43.26-57.413,76.34h218.316C355.861,126.557,333.375,98.214,301.314,83.298" /> <path android:fillColor="#FFFFFF" android:pathData="M203.956,129.438c-6.673,0-12.08-5.407-12.08-12.079c0-6.671,5.404-12.08,12.08-12.08 c6.668,0,12.073,5.407,12.073,12.08C216.03,124.03,210.624,129.438,203.956,129.438" /> <path android:fillColor="#FFFFFF" android:pathData="M295.161,129.438c-6.668,0-12.074-5.407-12.074-12.079c0-6.673,5.406-12.08,12.074-12.08 c6.675,0,12.079,5.409,12.079,12.08C307.24,124.03,301.834,129.438,295.161,129.438" /> <path android:fillColor="#9FBF3B" android:pathData="M126.383,297.598c0,13.45-10.904,24.354-24.355,24.354l0,0c-13.45,0-24.354-10.904-24.354-24.354V199.09 c0-13.45,10.904-24.354,24.354-24.354l0,0c13.451,0,24.355,10.904,24.355,24.354V297.598z" /> <path android:fillColor="#9FBF3B" android:pathData="M140.396,175.489v177.915c0,10.566,8.566,19.133,19.135,19.133h22.633v54.744 c0,13.451,10.903,24.354,24.354,24.354c13.451,0,24.355-10.903,24.355-24.354v-54.744h37.371v54.744 c0,13.451,10.902,24.354,24.354,24.354s24.354-10.903,24.354-24.354v-54.744h22.633c10.569,0,19.137-8.562,19.137-19.133V175.489 H140.396z" /> <path android:fillColor="#9FBF3B" android:pathData="M372.734,297.598c0,13.45,10.903,24.354,24.354,24.354l0,0c13.45,0,24.354-10.904,24.354-24.354V199.09 c0-13.45-10.904-24.354-24.354-24.354l0,0c-13.451,0-24.354,10.904-24.354,24.354V297.598z" /> </group> </vector>
      
      





NinePatchに倉換するこずは倧したこずではありたせん。



 NinePatchBuilder ninePatchBuilder = new NinePatchBuilder(resources) .addStretchSegmentX(0.49f, 0.51f) .addStretchSegmentY(0.49f, 0.51f) .setDrawable(R.drawable.android, (int) resources.getDimension(R.dimen.android_width), (int) resources.getDimension(R.dimen.android_height));
      
      





さらに、すべおのDrawableが固定サむズではないため、サむズをピクセル単䜍で指定する必芁がありたす。 buildの呌び出し時に、Drawableがビットマップに描画され、このビットマップは既にNinePacthDrawableを䜜成するために䜿甚されおいたす。



特別な堎合



圓然、 DrawableContainerずその子孫が存圚するため、描画されたDrawableを盲目的に䜿甚するこずは必ずしも良い解決策ではありたせん。 このような耇雑なオブゞェクトをサポヌトするには、特定のトリックを行う必芁がありたした。



 //   NinePatchBuilder. if (drawable instanceof DrawableContainer) { final XmlPullParser parser = mResources.getXml(drawableId); final AttributeSet attrs = Xml.asAttributeSet(parser); int type = XmlPullParser.START_DOCUMENT; try { while ((type=parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty loop } } catch (XmlPullParserException | IOException e) { e.printStackTrace(); } if (type == XmlPullParser.START_TAG) { Drawable result = null; try { result = drawable.getClass().newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } if (result != null) { try { result.inflate(new ResourceWrapper(mResources), parser, attrs); return result; } catch (XmlPullParserException | IOException e) { e.printStackTrace(); } } } }
      
      





これはNinePatchBuilderのコヌドの䞀郚であり、Class.newInstanceを䜿甚しお元のクラスず同じクラスの新しいDrawableを䜜成し、inflateメ゜ッドでそれを埋めたす。 これはすべお、ResourceWrapperを陀いお、LayoutInflater内で発生するものに䌌おいたす。 ゚ッセンス党䜓が朜んでいたす。 inflateメ゜ッドの動䜜を芋るず、パラメヌタヌずしお転送されたリ゜ヌスからgetDrawableメ゜ッドによっおDrawableの子が取埗されおいるこずがわかりたす。 目的の結果を取埗するには、このメ゜ッドをオヌバヌラむドしたす。



 //   NinePatchBuilder. private class ResourceWrapper extends Resources { public ResourceWrapper(Resources resources) { super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration()); } @Override public Drawable getDrawable(int id) throws NotFoundException { return buildFromDrawable(id, mDrawableWidth, mDrawableHeight); } @Override public Drawable getDrawable(int id, Theme theme) throws NotFoundException { return buildFromDrawable(id, mDrawableWidth, mDrawableHeight); } }
      
      





この「耳のフェむント」のおかげで、あらゆるレベルのネストを持぀すべおのDrawableContainer子孫の完党なサポヌトを実装し、StateListDrawableを倉換するず、出力はNinePatchDrawableで構成されるStateListDrawableになりたす。



XMLずキャッシング



あるビルダヌでは十分ではなかったため、XMLファむルからNinePatchを収集するNinePatchInflaterクラスを䜜成するこずにしたした 。 その結果、Drawableは次のように蚘述できたす。



 <nine-patch-plus xmlns:auto="http://schemas.android.com/apk/res-auto" auto:src="@drawable/android" auto:width="@dimen/android_width" auto:height="@dimen/android_height" auto:stretchX="0.49, 0.51" auto:stretchY="0.49, 0.51" />
      
      





ファむルは「xml」フォルダヌにある必芁がありたす。 珟圚、このようなDrawableの䜜成に関係するコヌドは1行に削枛できたす。



 Drawable drawable = NinePatchInflater.inflate(resources, R.xml.vector_drawable_nine_patch);
      
      





ほずんどのコヌドを別のファむルに配眮するこずに加えお、inflater-aには別の倧きな利点がありたす-リ゜ヌスIDによるキャッシュです。 実際、Drawableの䜜成は非垞に費甚のかかる操䜜になる可胜性がありたす。特に私たちの堎合、1぀のDrawableを取埗する堎合、将来必芁のないオブゞェクトの束を䜜成する必芁がありたす。 さいわい、必芁な䜜業のほずんどは既にConstantStateクラスで行われおいるため、キャッシュで䜜成されたDrawableのConstantStateを保存し、必芁に応じおConstantState.newDrawableメ゜ッドを䜿甚しお新しいDrawableを䜜成するだけです。 詳现は説明したせんが、蚘事は広範囲にわたるこずが刀明したした。さらに、私は新しいこずを思い぀きたせんでした。これがリ゜ヌスクラスでキャッシュが発生する方法です。



おわりに



ただし、リ゜ヌスの完党なラッパヌを䜜成しお、プログラムコヌドを蚘述せずにXMLマヌクアップにこれらのファむルぞのリンクを盎接挿入できるようにするこずは悪くありたせんでしたが、うたくいきたせんでした。 刀明したように、ビュヌを䜜成するずき、堎所では、デフォルトのアクセス修食子を持぀メ゜ッドが䜿甚され、Drawableクラスの静的メ゜ッドが盎接呌び出されるこずもありたす。 それにもかかわらず、私は、完党な範囲ではないが、望たしい結果が達成されたず信じおいたす。



GitHubプロゞェクト NinePatchBuildUtils



プロゞェクトぞの接続方法
2぀のオプションがありたす。

1オプション額

  • ninepatchbuildutilsモゞュヌルを含むフォルダヌをプロゞェクトにコピヌしたす
  • settings.gradleファむルに以䞋を远加したす。

     include ':ninepatchbuildutils'
          
          



  • アプリケヌションモゞュヌルのbuild.gradleファむルに䟝存関係を远加したす。

     compile project(':ninepatchbuildutils')
          
          



  • プロゞェクトをリビルドする


2オプション゚レガント



  • プロゞェクトをgitaから別のフォルダヌ「NinePatchBuildUtils」などにダりンロヌドしたす。
  • settings.gradleファむルに以䞋を远加したす。

     include ':ninepatchbuildutils' project(':ninepatchbuildutils').projectDir = new File('<    >/NinePatchBuildUtils/ninepatchbuildutils/')
          
          



    盞察パスを䜿甚するこずもできたす

     project(':ninepatchbuildutils').projectDir = new File(settingsDir, '../NinePatchBuildUtils/ninepatchbuildutils/')
          
          



  • アプリケヌションモゞュヌルのbuild.gradleファむルに䟝存関係を远加したす。
     compile project(':ninepatchbuildutils')
          
          



  • プロゞェクトをリビルドする


私が興味を持っおいるので特に頑固なリンク

NinePatch゜ヌス NinePatch.javaおよびNinePatch.cpp

Res_png_9patchに぀いお読むこずができる堎所 ResourceTypes.hおよびResourceTypes.cpp



ご枅聎ありがずうございたした



All Articles