Adsense_top

2010年3月31日水曜日

Android開発のメモ その3 SHARP-開発者ページ OPEN

 シャープが昨日(3月30日)auのAndroid携帯「IS01」の発表に合わせて、、開発者向けウェブサイト「SH Developers Square」をオープンしています。
 ここで、以前紹介した「Xperia」の add-on と同様に 「JN-DK01(IS01)」のadd-onが公開されています。導入方法は同ページに書かれています。



 また、Androidのアプリケーション開発者向け専用端末の5月以降の製品化を予告しています。ニュースリリースによると、オペレーティングシステム(OS)の書き換えができるようです。
 SHシリーズのAPIの解説も公開されています。



 詳しくは「SH Developers Square」を覗いてみてください。


2010年3月21日日曜日

C# TreeViewNodeの文字列複数行表示

TreeViewのノードで長いラベルを使うと下図のようにスクロールバーが表示されたり、幅が広くなるのが気になりましたので、ラベルの複数行表示するように考えてみました。


FormにTreeVeiwをtreeView1として配置しています。
何をしているかはコメントを参考にしてください。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
treeView1.Nodes.Clear();
// ラベル部分のみ手動で描画する
treeView1.DrawMode = TreeViewDrawMode.OwnerDrawText;
// ノードごとには高さが変えられないので、
// 今回は2行分の高さにしておく
treeView1.ItemHeight =26;
// 仮に今回はお気に入りを登録しています。
// リストに登録しているのは、単に子ノードを複数登録するので
// 同じ事を複数回書かずにループで回すためで、実際に使用する時は、
// メソッドを分けた方が良いと思います。
List<TreeNodeText> lst = new List<TreeNodeText>();
lst.Add(new TreeNodeText(
"Android Developer\r\n[ http://developer.android.com/ ]"));
lst.Add(new TreeNodeText(
"MSDN ホームページ\r\n[ http://msdn.microsoft.com/ ]"));
lst.Add(new TreeNodeText(
"MSDN Code Gallery\r\n[ http://code.msdn.microsoft.com/ ]"));
lst.Add(new TreeNodeText(
"Eclipse.org home\r\n[ http://www.eclipse.org/ ]"));
TreeNode rootNode = treeView1.Nodes.Add("お気に入り");
Graphics graphics = treeView1.CreateGraphics();
Font font = treeView1.Font;
foreach (TreeNodeText treeNodeText in lst)
{
TreeNode node = new TreeNode();
// ノードのタグにTreeNodeTextクラスのインスタンスを登録
node.Tag = treeNodeText;
// ラベルの領域幅は文字列の幅で決まるために、複数行中の
// 一番幅の広い文字列を仮のラベルとしておく
node.Text =
MaxWidthString(graphics, font, treeNodeText.TextList);
rootNode.Nodes.Add(node);
}
font.Dispose();
graphics.Dispose();
}
private void treeView1_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
// タグに TreeNodeText のインスタンスが登録されている場合は、
// インスタンスに登録されている文字列を描画し、それ以外は
// ラベルに登録されている文字列を描画する
// 通常のノードの描画を e.DrawDefault を使ってシステムに任すと、
// 左上に描画されるために、オーナードローしている。
string text =
e.Node.Tag == null ? e.Node.Text :
((TreeNodeText)e.Node.Tag).DisplayText;
TextRenderer.DrawText(
e.Graphics, text, treeView1.Font, e.Bounds, Color.Black,
TextFormatFlags.VerticalCenter | TextFormatFlags.Left);
}
/// <summary>
/// 文字列のリストからTreeViewに描画した場合に、
/// 幅が最長の文字列を返します。
/// </summary>
/// <param name="graphics">TreeViewのGraphicsオブジェクト</param>
/// <param name="font">文字列の描画に使用するTreeViewのフォント</param>
/// <param name="list">文字列のリスト</param>
/// <returns>最長の文字列</returns>
private string MaxWidthString( Graphics graphics, Font font,
List<string> list)
{
int maxWidth = 0;
string maxString = string.Empty;
foreach (string text in list)
{
TextRenderer.DrawText(
graphics, text, font, new Point(0, 0), Color.Black);
Size stringSize =
TextRenderer.MeasureText(graphics, text, font);
if (maxWidth < stringSize.Width)
{
maxWidth = stringSize.Width;
maxString = text;
}
}
return maxString;
}
/// <summary>
/// TreeViewのタグに登録する情報をラップするクラス
/// </summary>
class TreeNodeText
{
private List<string> textList = new List<string>();

public TreeNodeText(string text)
{
string[] array =
text.Split(new string[]{"\r\n","\n"},
StringSplitOptions.RemoveEmptyEntries);
foreach(string s in array)
textList.Add(s);
}
// 表示する文字列を各行ごとのリストを返します。
public List<string> TextList
{
get { return textList; }
set { textList = value; }
}
// 表示する文字列を行ごとに改行を挟む形で返します。
public string DisplayText
{
get { return string.Join("\r\n", textList.ToArray()); }
}
}
}

以下のように表示されます。



各ノードの高さは、ラベル表示が出来る分をあらかじめ設定しています。
TreeViewのDrawModeをOwnerDrawAllにすれば細かな調整が出来そうですが、+/-ボタンや、ノード間の線の描画、およびインデントの幅等も計算もする必要が出てきそうなので、今回は多少の格好の悪さは我慢しこのようにしました。



2010年3月14日日曜日

Android Tips アプリケーション一覧の取得

起動可能なアプリケーションの一覧を取得する方法です。
リファレンスが英語のため、こんな簡単なことでも、内容が理解するために翻訳しながらしないといけないので、メモっておきます。



// 起動可能なIntent
Intent intent=new Intent(Intent.ACTION_MAIN,null);
// デスクトップから可能なIntent(つまり通常のアプリケーション)
intent.addCategory(Intent.CATEGORY_LAUNCHER);
// 通常のアプリケーションのリストを取得
PackageManager manager=getPackageManager();
List<ResolveInfo6gt; infoes=manager.queryIntentActivities(intent,0);
TextView textView = (TextView) findViewById(R.id.textView);
for (int i = 0;i < infes.size(); i++) {
ResolveInfo info=infes.get(i);
// TextViewにアプリケーション名を出力
textView.append(info.loadLabel(manager) + "\n");
}

ここではアプリケーション名をResolveInfoから取得していますが、アイコンや起動に必要なパッケージやクラスの名前の取得もできます。


2010年3月6日土曜日

C# 画像を使った変形ウインドウの改良

以前書いたエントリー「C# 画像を使った変形ウインドウを作ってみた。」で
「for文2つとwhileの中にGetPixel()2つはものすごい時間がかかりそう。
Byte配列で処理するように変更すれば実用性がでそうだね。

とのコメントをいただきましたので、透過色以外の部分をRegionとして構成する部分をバイト配列での処理に再考し変更してみました。
今回は、変更したメソッド部のみ書いておきますので、他の部分は「C# 画像を使った変形ウインドウを作ってみた。」や「C# 変形ウインドウにマウス操作を追加した。」をご覧ください。

//
// 画像からウインドウの領域を作成し返します。
//
// 引数 bmp:対象画像
// 引数 transparencyKey:透過色
private Region BitmapToRegion(Bitmap bmp,
Color transparencyKey)
{
int height = bmp.Height;
int width = bmp.Width;
int xStart = 0;
System.Drawing.Drawing2D.GraphicsPath path =
new System.Drawing.Drawing2D.GraphicsPath();
// 縦方向の走査
for (int i = 0; i < height; i++)
{
// 横方向の走査
for (int j = 0; j < width; j++)
{
if (bmp.GetPixel(j, i) != transparencyKey)
{
// 透過色でない部分の連続を1ピクセル高の矩形にまとめ、
// GraphicsPathに追加していく
// (点ごとに登録処理するより効率がよいので)
xStart = j;
while ((j < width) &&
(bmp.GetPixel(j, i) != transparencyKey))
j++;
path.AddRectangle(
new Rectangle(xStart, i, j - xStart, 1));
}
}
}
// GraphicsPathからRegionを作成し返す。
Region region = new Region(path);
path.Dispose();
return region;
}

を以下のように変更しました。結構長くなってしまいましたの、内容はコメントを参考にMSDNの各クラスのリファレンスをお読みください。

// 以下の3つの行は、前のコードでは使用していなかったり
// コード内部でフルに書いていたため記述していなかった
// namespaceですので以前の分に追加が必要です。
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

private Region BitmapToRegion(Bitmap bmp, Color transparencyKey)
{
// if (bmp == null)
// throw new ArgumentNullException(
// "Bitmap", "Bitmapがnullを参照しています");
int height = bmp.Height;
int width = bmp.Width;
// 画像データをバイト配列にコピー
BitmapData bmpdat =
bmp.LockBits(new Rectangle(Point.Empty, bmp.Size),
ImageLockMode.ReadOnly, bmp.PixelFormat);
// コピー先の配列に領域確保
// bmpdat.Strideはスキャン幅
byte[] bytes = new byte[bmpdat.Stride * bmp.Height];
Marshal.Copy(bmpdat.Scan0, bytes, 0, bytes.Length);
bmp.UnlockBits(bmpdat);

GraphicsPath path =
new System.Drawing.Drawing2D.GraphicsPath();
// 8ビット・24ビット画像時の、BitmapDataで行が4バイト毎に行が
// 切り上げられるために、実際の画像幅との差、
// および24ビット・32ビット画像時のループカウンタと配列位置の
// 差を埋めるために使用する値を、変数diffに設定します。
int diff = bmpdat.Stride - width
// ループカウンタと配列位置の差を格納する変数
int adjust = 0;
int xStart = 0;
// 8ビットインデックス付きカラーデータ
if (bmp.PixelFormat == PixelFormat.Format8bppIndexed)
{
// カラーパレット(Color配列)から、透過色のインデックスを取得
int index =
Array.IndexOf(bmp.Palette.Entries, transparencyKey);

for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 一次配列のため、位置を調節しながら透過色と異なるか確認
if (bytes[i * width + j + adjust] != index)
{
// 透過色でないピクセルを連続するパスとしてパスを追加
xStart = j;
while ((j < width) &&
(bytes[i * width + j + adjust] != index))
j++;
path.AddRectangle(
new Rectangle(xStart, i, j - xStart, 1));
}
}
adjust += diff;
}
}
// 24ビットカラーデータ
else if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
{
// 透過色をバイト配列に変換
// (後で比較する時に分かり易いように、RGB順でなくBGR順にしています)
Byte[] transBytes =
new Byte[] { transparencyKey.B, transparencyKey.G,
transparencyKey.R };
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 一次配列のため、位置を調節しながら透過色と異なるか確認
// バイト配列に前方方向からBGRの順に値が入っている
if (bytes[i * width + j * 3 + adjust] != transBytes[0] ||
bytes[i * width + j * 3 + 1 + adjust] != transBytes[1] ||
bytes[i * width + j * 3 + 2 + adjust] != transBytes[2])
{
// 透過色でないピクセルを連続するパスとしてパスを追加
// 以下の配列の比較はC# 3.0では、SequenceEqualが使えます。
xStart = j;
while ((j < width) &&
(bytes[i * width + j * 3 + adjust] != transBytes[0] ||
bytes[i * width + j * 3 + 1 + adjust] != transBytes[1] ||
bytes[i * width + j * 3 + 2 + adjust] != transBytes[2]))
j++;
path.AddRectangle(new Rectangle(xStart, i, j - xStart, 1));
}
}
adjust += diff;
}
}
// 32ビットカラーデータ
// ビットマップ側のアルファ値は実質無視しています。
else if (bmp.PixelFormat == PixelFormat.Format32bppArgb)
{
// 透過色をバイト配列に変換
// (後で比較する時に分かり易いように、ARGB順でなくBGRA順にしています)
Byte[] transBytes =
new Byte[] { transparencyKey.B, transparencyKey.G,
transparencyKey.R,transparencyKey.A };
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 一次配列のため、位置を調節しながら透過色と異なるか確認
// バイト配列に前方方向からBGRAの順に値が入っている
if (bytes[i * width + j * 4 + adjust] != transBytes[0] ||
bytes[i * width + j * 4 + 1 + adjust] != transBytes[1] ||
bytes[i * width + j * 4 + 2 + adjust] != transBytes[2] ||
bytes[i * width + j * 4 + 3 + adjust] != transBytes[3])
{
// 透過色でないピクセルを連続するパスとしてパスを追加
xStart = j;
while ((j < width) &&
(bytes[i * width + j * 4 + adjust] != transBytes[0] ||
bytes[i * width + j * 4 + 1 + adjust] != transBytes[1] ||
bytes[i * width + j * 4 + 2 + adjust] != transBytes[2] ||
bytes[i * width + j * 4 + 3 + adjust] != transBytes[3]))
j++;
path.AddRectangle(new Rectangle(xStart, i, j - xStart, 1));
}
}
adjust += diff;
}
}
else
{
// 今回は上記の3つのみ対応したため他のPixelFormatは
// 例外をスローします。
throw new Exception(
string.Format("'{0}'はサポートされていないカラーデータ形式です。",
bmp.PixelFormat.ToString()));
}
Region region = new Region(path);
path.Dispose();
return region;
}

Stopwatchクラスで計測してみたところ、何と平均で100倍前後高速でした。
コメントをいただいた方(名前は入力されていませんでした)ご指摘ありがとうございました。