参考 第14回(リストビュー)
今回は前回のサンプルにスクロールバーをつけてみます。
そこで登場するクラスがBScrollViewです。
本来であれば、スクロールバーはBScrollBarというクラスを使用して、
少々複雑なビュー階層を構築する必要があったのですが、
BScrollViewというクラスによって、非常に簡単にスクロールバーを取り付けることが出来るようになりました。
また、BScrollViewの解説だけでは寂しいので、entry_ref構造体を操作するBEntryクラスについても少々解説します。
今回のサンプルソース全体です。
// sample13.cpp
#include <Application.h> // BApplicationを使うために必要
#include <Window.h> // BWindowを使うために必要
#include <View.h> // BViewを使うために必要
#include <Alert.h> // BAlertを使うために必要
#include <Button.h> // BButtonを使うために必要
#include <Box.h> // BBoxを使うために必要
#include <StringView.h> // BStringViewを使うために必要
#include <ListView.h> // BListViewを使うために必要
#include <ListItem.h> // BStringItemを使うために必要
#include <Directory.h> // BDirectoryを使うために必要
#include <Entry.h> // entry_refを使うために必要
#include <ScrollView.h> // BScrollViewを使うために必要
// クラスの前方宣言
class MyApp;
class MyWindow;
class MyView;
// メッセージ定数
const uint32 BUTTON_CLICKED = 'bucl';
class MyApp : public BApplication
{
public:
MyApp();
private:
MyWindow* m_win;
};
class MyWindow : public BWindow
{
public:
MyWindow(BRect frame,const char* title,window_type type,uint32 flags);
virtual bool QuitRequested(); // 終了が要求されたときに呼ばれる関数
private:
MyView* m_view;
};
class MyView : public BView
{
public:
MyView(BRect frame,const char *name,uint32 resizingMode,uint32 flags);
~MyView();
virtual void AttachedToWindow(); // ビューがウィンドウに取り付けられたとき(子供として登録された後)呼ばれる関数
virtual void MessageReceived(BMessage* message);
private:
BStringView* m_string_view; // 左上の文字列
BListView* m_list_view; // ディレクトリの内容一覧をあらわす、ListView
BScrollView* m_scroll_view; // スクロールビュー
BBox* m_box; // 装飾としてのBBox
BButton* m_button; // ボタン
};
MyApp::MyApp()
:BApplication("application/x-vnd.big56-MyApp")
{
m_win = new MyWindow(BRect(100,100,350,380),"リストビュー",B_TITLED_WINDOW,B_ASYNCHRONOUS_CONTROLS);
m_win->Show();
}
MyWindow::MyWindow(BRect frame,const char* title,window_type type,uint32 flags)
:BWindow(frame,title,type,flags)
{
frame.OffsetTo(0,0); // 与えられたBRectの左上が(0,0)になるようにする
m_view = new MyView(frame,"theView",B_FOLLOW_ALL_SIDES,B_WILL_DRAW);
AddChild(m_view);
}
bool MyWindow::QuitRequested()
{
// アプリケーションを終了する
be_app->Quit();
return true;
}
MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
:BView(frame,title,resizingMode,flags)
{
m_string_view = new BStringView(BRect(10,5,240,25),"theStringView","ディレクトリをドロップしてください");
AddChild(m_string_view);
m_box = new BBox(BRect(10,30,240,240));
m_box->SetLabel("ファイル一覧");
m_list_view = new BListView(BRect(20,20,210,190),"theListView");
// m_box->AddChild(m_list_view); // リストビューは直接は加えない
m_scroll_view = new BScrollView("theScrollView",m_list_view,B_FOLLOW_LEFT | B_FOLLOW_TOP,
0,false,true);
m_box->AddChild(m_scroll_view); // スクロールビューの方を子供に加える
m_button = new BButton(BRect(140,250,240,270),"theButton",
"選択文字列の表示",new BMessage(BUTTON_CLICKED));
AddChild(m_box);
AddChild(m_button);
SetViewColor(200,200,200);
}
MyView::~MyView()
{
}
// ラジオボタンとボタンのターゲット変更
void MyView::AttachedToWindow()
{
m_button->SetTarget(this);
}
// メッセージ処理
void MyView::MessageReceived(BMessage* message)
{
switch(message->what)
{
case B_SIMPLE_DATA:
{
entry_ref ref;
message->FindRef("refs",&ref);
BDirectory dir(&ref);
// 受け取ったファイルがディレクトリで無かったら何もしない
if (dir.InitCheck() == B_OK)
{
BListItem* item; // ファイルの内容を空に
while((item = m_list_view->RemoveItem(static_cast<int32>(0) ) ) != NULL)
delete item;
char buffer[B_FILE_NAME_LENGTH]; // ファイル名を格納するバッファ
BEntry entry;
// ディレクトリの中のファイルを順々に得る
while (dir.GetNextEntry(&entry) == B_OK)
{
entry.GetName(buffer); // ファイル名の取得
// ファイルの名前を追加
m_list_view->AddItem(new BStringItem(buffer));
}
}
break;
}
case BUTTON_CLICKED:
{
if (m_list_view->IsEmpty() ) break; // リストビューが空だったら何もしない
int32 index;
index = m_list_view->CurrentSelection(); // 現在選択されている場所の位置を得る
if (index < 0) break; // なにも選択されていなかったら何もしない
BStringItem* selected_item;
selected_item = dynamic_cast<BStringItem*>(m_list_view->ItemAt(index));
// ダウンキャストした結果が失敗したらなにもしない
if (selected_item != NULL)
{
// 現在選択されているものを表示
(new BAlert("Test",selected_item->Text(),"OK"))->Go();
}
break;
}
default:
BView::MessageReceived(message);
break;
}
}
int main(int argc,char** argv)
{
MyApp app;
app.Run();
return 0;
}
前回と今回の結果を比べてみます。
| 前回 | 今回 |
|---|---|
![]() | |
両方とも、筆者の/boot/home/config/settings/を表示してみた結果ですが、
今回の方はスクロールバーがついて、たくさんファイルが入っているディレクトリの中身もちゃんと表示出来るようになっています。
しかし、レイアウト的には、スクロールバーがリストビューの横について少し不格好な印象を受けることにも注意してください。
今回の変更は実質2行です。
// m_box->AddChild(m_list_view); // リストビューは直接は加えない
m_scroll_view = new BScrollView("theScrollView",m_list_view,B_FOLLOW_LEFT | B_FOLLOW_TOP,
0,false,true);
m_box->AddChild(m_scroll_view); // スクロールビューの方を子供に加える
BScrollViewのコンストラクタは次のように宣言されています。
BScrollView(const char *name, BView *target, uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP, uint32 flags = 0, bool horizontal = false, bool vertical = false, border_style border = B_FANCY_BORDER);
コンストラクタで指定できるものは、
引数のうち、nameとresizeMaskとflagsについては、第4回を参考にしてください。
targetはスクロールバーをつけるビューへのポインタを指定します。
これはBViewへのポインタとして宣言されていますので、どんなビューにでもスクロールバーの取り付けが可能であることになります。
horizontalとverticalには、水平または垂直のスクロールバーの取り付けを有効にするかどうかをtrueまたはfalseで指定します。
デフォルトではfalseになっています。(個人的にはなぜデフォルトがfalseかわからんのですが・・・)
線の種類borderには次の値を指定することが出来ます。画像とともに示します。
| 値 | 画像 |
|---|---|
| B_FANCY_BORDER | ![]() |
| B_PLAIN_BORDER | ![]() |
| B_NO_BORDER | ![]() |
そして、スクロールバーを構築したら、スクロールビューの方を子供に登録(AddChild)して、見事スクロールバーの取り付けの完了です。
ちなみに、リストビューの方は子供には加えないでください。
というのも、スクロールバーを取り付ける上で適切なビューの階層構造を作り上げるのは、BScrollViewクラスだからです。
素のBListViewをBScrollViewが親のViewから覆い隠すことによって、スクロールバーの取り付けを実現しているわけです。
しかし、見た目にはスクロールバーの分だけ右側が太った感じに見えます。
もし、レイアウトを調整するなら、スクロールバーの幅は次の定数で示されますので、それを使用してレイアウト調整をすることになります。
これらは、ウィンドウいっぱいに、スクロールバー付きのテキストビュー(テキスト入力コントロール)を貼り付けたいときなどによく使われます。
| 値 | 説明 |
|---|---|
| B_V_SCROLL_BAR_WIDTH | 垂直スクロールバーの横幅 |
| B_H_SCROLL_BAR_HEIGHT | 水平スクロールバーの縦幅 |
これらは、ScrollBar.h内では、それぞれ14.0という定数のマクロになっています。
というのも、Beメニュー内の[Menu]-[Preference]で指定出来るスクロールバーは、すべて太さが同じだからですが、
将来的に変更する余地を残したいと思ったのでしょう。
14.0という即値よりも、上にあげた定数を使っておくほうが無難です。
スクロールバーの設定画面を以下に示します。
さて、これだけでは寂しいのでBEntryクラスの解説を少々行います。
前回、entry_ref構造体で処理していた、ファイル名をリストに追加する処理ですが、
BEntryクラスで置き換えています。
char buffer[B_FILE_NAME_LENGTH]; // ファイル名を格納するバッファ
BEntry entry;
// ディレクトリの中のファイルを順々に得る
while (dir.GetNextEntry(&entry) == B_OK)
{
entry.GetName(buffer); // ファイル名の取得
// ファイルの名前を追加
m_list_view->AddItem(new BStringItem(buffer));
}
まず、ファイル名を格納するバッファを定義して、BEntry::GetName()でファイル名を得るときに
このバッファに格納するようにしています。
BEntry::GetName()関数は次のように宣言されています。
status_t GetName(char *buffer) const;
この関数は引数のbufferに、ファイル名の文字列が格納されて返ってきます。
B_FILE_NAME_LENGTHはファイル名の最大長で、StorageDefs.h内で256(limits.h内のNAME_MAX)と定義されています。
ファイル名をバッファに格納したら前回と同じようにBListView::AddItem()で、新しく作成したBStringItemへのインスタンスを格納するようにしています。
BEntryクラスはentry_ref構造体に対する操作(メソッド)を数多く定義しているクラスです。
今回使用したのは、GetName()関数だけですが、それ以外にも様々なメンバ関数があります。
一例を表に示します。
| 宣言 | 説明 |
|---|---|
| GetPath(BPath* path) | フルパス名の取得 |
| GetParent(BEntry* entry) GetParent(BDirectory* directory) | そのファイルの親ディレクトリの取得 |
| GetRef(entry_ref* ref) | そのファイルを示すentry_ref構造体の取得 |
| GetStat(struct stat* st) | stat構造体の取得 |
| InitCheck(void) | 初期化チェック |
| Remove(void) | そのファイルの削除 |
| Rename(const char* path,bool clobber=false) | ファイルのリネーム リネーム後に同じファイル名があったら削除するかどうか? |
| MoveTo(BDirectory* dir,const char* path=NULL,bool clobbe=false) | ファイルの移動 移動先に同じファイル名があったら削除するかどうか? |
|
SetTo(const BDirectory *dir, const char *path,bool traverse = false) SetTo(const entry_ref *ref, bool traverse = false) SetTo(const char *path, bool traverse = false) |
別のファイルを参照する。 traverseはシンボリックリンクを解析するかどうか? |
| Unset(void) | ファイル参照の解放 |
| bool Exists() | ファイルが存在するかどうかを返す |
ちなみに、Exists()メンバ関数以外は、すべて戻り値はstatus_t型です。
B_OKが正常に操作が行われたという意味の戻り値です。
今回は、スクロールバーの取り付けを行いました。
単純にスクロールバーを取り付けるだけだったらBScrollViewを使用すれば非常に簡単だということがわかったと思います。
ではでは。
|
|||
|
|