ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2012年6月11日月曜日

AndroidでSilverlightのWrapPanel風のViewGroupが欲しい その2

前回作ったコントロールでは、動的にadd もしくは remove した時に正常に動作しませんでした。


今回は ViewGroup を直接継承し実装してみました。
子要素の配置を行うためいに ViewGroup#onLayout をオーバーライドします。
ここで View#getWidth の戻り値は 0 となってしまうため、 View#getMeasuredWidthを使用します。
ですが、素の状態では View#getMeasuredWidth の戻り値も 0 となり、コントロールのサイズを取得できません。
そこでまず ViewGroup#onMeasure をオーバーライドし子要素に対してView#measureを呼び出します。


以下の呪文を記述
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = View.resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = View.resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        this.setMeasuredDimension(width, height);

        int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
        int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(childWidthSpec, childHeightSpec);
        }
    }

これを書けば、View#getMeasuredWidthで値が取得できるようになります。
ちゃんと理解してはいない。

あとは前回 View#onWindowFocusChangedのなかに書いてた事をView#onLayoutに記述してやればOK。

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int count = this.getChildCount();

        // 行の高さ
        int rowHeight = 0;

        // 現在の行の幅
        int currentRowWidth = 0;

        int rowMaxWidth = this.getWidth();

        int top = 0;

        for (int i = 0; i < count; i++) {
            View child = this.getChildAt(i);
            if (child.getVisibility() != View.GONE) {

                int childWidth = child.getMeasuredWidth();
                int childHeight = child.getMeasuredHeight();

                if (rowMaxWidth < currentRowWidth + childWidth) {
                    // はみ出すので演算をやり直す。
                    // 次行の位置を設定
                    top += rowHeight < childHeight ? childHeight : rowHeight;
                    // 現在行の高さを初期化
                    rowHeight = 0;
                    // 現在行の幅を初期化
                    currentRowWidth = 0;
                }

                // コントロールを適切な位置に配置
                child.layout(
                    currentRowWidth,
                    top,
                    currentRowWidth + childWidth,
                    top + childHeight);

                // 行の高さを更新する必要があるか
                rowHeight =
                    rowHeight < childHeight ? childHeight : rowHeight;

                // 現在のコントロール幅を合計値に加算する
                currentRowWidth += childWidth;
            }
        }
    }

良い感じ良い感じ。

実際にはMarginなんかが設定されるハズ。
なの以下の クラス をInner Classとして作成。
    public static class LayoutParams extends MarginLayoutParams {

        public LayoutParams(Context context, AttributeSet attr) { super(context, attr); }

        public LayoutParams(int width, int height) { super(width, height); }

        public LayoutParams(ViewGroup.LayoutParams source) { super(source); }

        public LayoutParams(MarginLayoutParams source) { super(source); }
    }

MarginLayoutParams を継承しただけ。今後を考えてInner Classとした。
このクラスを定義しただけで、(WrapLayout.LayoutParams)child.getLayoutParams() なんてした日には楽しい結末(ClassCastException)が待っているので、 以下の Method を Override
・ ViewGroup#generateLayoutParams
・ ViewGroup#generateDefaultLayoutParams
・ ViewGroup#checkLayoutParams
・ ViewGroup#generateLayoutParams
    @Override
    public WrapLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new WrapLayout.LayoutParams(getContext(), attrs);
    }

    @Override
    protected WrapLayout.LayoutParams generateDefaultLayoutParams() {
        return new WrapLayout.LayoutParams(WrapLayout.LayoutParams.WRAP_CONTENT, WrapLayout.LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof WrapLayout.LayoutParams;
    }

    @Override
    protected WrapLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new WrapLayout.LayoutParams(p);
    }

設定された margin を使って配置を行うように ViewGroup#onLayout に修正を加える。
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int count = this.getChildCount();

        // 行の高さ
        int rowHeight = 0;

        // 現在の行の幅
        int currentRowWidth = 0;

        int rowMaxWidth = this.getWidth();

        int top = 0;

        for (int i = 0; i < count; i++) {
            View child = this.getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                WrapLayout.LayoutParams lp =
                        (WrapLayout.LayoutParams)child.getLayoutParams();

                int childWidth = child.getMeasuredWidth();
                int childHeight = child.getMeasuredHeight();

                int childTotaWidth = childWidth + lp.rightMargin + lp.leftMargin;
                int childTotalHeight = childHeight + lp.topMargin + lp.bottomMargin;

                if (rowMaxWidth < currentRowWidth + childTotaWidth) {
                    // はみ出すので演算をやり直す。
                    // 高さを設定
                    top += rowHeight < childTotalHeight ? childTotalHeight : rowHeight;
                    // 現在行の高さを初期化
                    rowHeight = 0;
                    // 現在行の幅を初期化
                    currentRowWidth = 0;
                }

                child.layout(
                    currentRowWidth + lp.leftMargin,
                    top + lp.topMargin,
                    currentRowWidth + childWidth + lp.rightMargin,
                    top + childHeight + lp.bottomMargin);

                rowHeight =
                    rowHeight < childTotalHeight ? childTotalHeight : rowHeight;
                currentRowWidth += childTotaWidth;
            }
        }
    }

これでMarginが有効になったWrapLayoutの出来上がり。

ソースコード全文(import等は割愛)

public class WrapLayout extends ViewGroup {

    //==================================================================
    // constructor
    //==================================================================
    public WrapLayout(Context context) { super(context); }

    public WrapLayout(Context context, AttributeSet attrs) { super(context, attrs); }

    public WrapLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }

    //==================================================================
    // public methods
    //==================================================================

    @Override
    public WrapLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new WrapLayout.LayoutParams(getContext(), attrs);
    }

    //==================================================================
    // protected methods
    //==================================================================
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = View.resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = View.resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        this.setMeasuredDimension(width, height);

        int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
        int childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(childWidthSpec, childHeightSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int count = this.getChildCount();

        // 行の高さ
        int rowHeight = 0;

        // 現在の行の幅
        int currentRowWidth = 0;

        int rowMaxWidth = this.getWidth();

        int top = 0;

        for (int i = 0; i < count; i++) {
            View child = this.getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                WrapLayout.LayoutParams lp =
                        (WrapLayout.LayoutParams)child.getLayoutParams();

                int childWidth = child.getMeasuredWidth();
                int childHeight = child.getMeasuredHeight();

                int childTotaWidth = childWidth + lp.rightMargin + lp.leftMargin;
                int childTotalHeight = childHeight + lp.topMargin + lp.bottomMargin;

                if (rowMaxWidth < currentRowWidth + childTotaWidth) {
                    // はみ出すので演算をやり直す。
                    // 高さを設定
                    top += rowHeight < childTotalHeight ? childTotalHeight : rowHeight;
                    // 現在行の高さを初期化
                    rowHeight = 0;
                    // 現在行の幅を初期化
                    currentRowWidth = 0;
                }

                child.layout(
                    currentRowWidth + lp.leftMargin,
                    top + lp.topMargin,
                    currentRowWidth + childWidth + lp.rightMargin,
                    top + childHeight + lp.bottomMargin);

                rowHeight =
                    rowHeight < childTotalHeight ? childTotalHeight : rowHeight;
                currentRowWidth += childTotaWidth;
            }
        }
    }

    @Override
    protected WrapLayout.LayoutParams generateDefaultLayoutParams() {
        return new WrapLayout.LayoutParams(WrapLayout.LayoutParams.WRAP_CONTENT, WrapLayout.LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof WrapLayout.LayoutParams;
    }

    @Override
    protected WrapLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new WrapLayout.LayoutParams(p);
    }

    //==================================================================
    // inner class
    //==================================================================

    public static class LayoutParams extends MarginLayoutParams {

        public LayoutParams(Context context, AttributeSet attr) { super(context, attr); }

        public LayoutParams(int width, int height) { super(width, height); }

        public LayoutParams(ViewGroup.LayoutParams source) { super(source); }

        public LayoutParams(MarginLayoutParams source) { super(source); }
    }
}

2012年6月6日水曜日

AndroidでSilverlightのWrapPanel風のViewGroupが欲しい

AndroidでSilverlightのWrapPanelのように自動で折り返すViewGroupが欲しいです。
デフォルトじゃないみたいなので、いろいろ漁ってると以下の記事を発見。

Androidでボタンを横に並べて自動で折り返す

ほほー。RelativeLayoutでござるか。

使 っ た こ と な い ん で す よ ね 。

上記の記事を使いまわしが効くようにコントロール化してみました。
かなり適当。
public class WrapLayout extends RelativeLayout {

    public WrapLayout(Context context) {
        super(context);
    }

    public WrapLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WrapLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void addView(View child) {
        // idが未設定の場合は乱数でどうにかする。
        if (child.getId() == -1) { child.setId(new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); }

        super.addView(child);
    }

    @Override
    public void addView(View child, int width, int height) {
        // idが未設定の場合は乱数でどうにかする。
        if (child.getId() == -1) { child.setId(new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); }

        super.addView(child, width, height);
    }

    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        // idが未設定の場合は乱数でどうにかする。
        if (child.getId() == -1) { child.setId(new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); }

        super.addView(child, params);
    }

    @Override
    public void addView(View child, int index) {
        // idが未設定の場合は乱数でどうにかする。
        if (child.getId() == -1) { child.setId(new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); }

        super.addView(child, index);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        // idが未設定の場合は乱数でどうにかする。
        if (child.getId() == -1) { child.setId(new Random(System.currentTimeMillis()).nextInt(Integer.MAX_VALUE)); }

        super.addView(child, index, params);
      }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {

        super.onWindowFocusChanged(hasWindowFocus);

        // 子供の数を取得
        int l = this.getChildCount();
        // 無いなら何もしない
        if (l == 0) {
          return;
        }

        // ディスプレイの横幅を取得
        WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();

        // 1行最大長
        int max = display.getWidth();

        // 一番最初は基点となるので何もしない
        View pline = this.getChildAt(0);
        // 現在の1行の長さ
        int currentTotal = pline.getWidth();
        for (int i = 1; i < l; i++) {
            int w = this.getChildAt(i).getWidth();
            RelativeLayout.LayoutParams layoutParams =
                    (RelativeLayout.LayoutParams) this.getChildAt(i).getLayoutParams();

            // 横幅を超えないなら前のボタンの右に出す
            if (max > currentTotal + w) {
                // 現在長に処理対象コントロール長を加算
                currentTotal += w;
                layoutParams.addRule(RelativeLayout.ALIGN_TOP, this.getChildAt(i - 1).getId());
                layoutParams.addRule(RelativeLayout.RIGHT_OF, this.getChildAt(i - 1).getId());
            } else {
                // 横最大長を超過した場合は折り返す
                layoutParams.addRule(RelativeLayout.BELOW, pline.getId());

                // 基点を変更
                pline = this.getChildAt(i);

                // 長さをリセット
                currentTotal = pline.getWidth();
            }
        }
    }
}


わかってます。onWindowFocusChangedにaddViewしたらダメじゃね?ってのはわかってます。
とりあえずです。
[[Activity]]
public class MainActivity extends Activity {
    /** Called when the activity is first created. */

    String[] strings = { "てst1", "てst2", "てst3", "てst4", "てst5", "てst6", "てst7", "てst8", "てst9", "てst10", "てst111111a", "てst111", "てst234"};
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        WrapLayout layout = (WrapLayout)this.findViewById(R.id.FixedGridLayout);
        for (String string : strings) {
            Button btn = new Button(this);
            btn.setText(string);
            //btn.setId(random.nextInt(Integer.MAX_VALUE));
            layout.addView(btn);
        }
    }
}
[[出力結果]]

良い感じ。

2009年11月7日土曜日

任意形式のXMLリクエスト・レスポンスの送受信


0. 準備① すでにEclipse、WTP、Axis2の設定は完了していること。
Eclipse + Axis2連携手順 及び簡易サービス作成手順 」参照
1. サーバ側プロジェクトの作成① ファイル -> 新規 -> プロジェクト -> Web -> 動的Webプロジェクト を選択する
② 構成の変更からプロジェクト・フィアセットを開き、以下の項目のみを有効にする
Axis2 Webアプリケーション
Java
動的Webモジュール
③ 任意のプロジェクト名を入力
④ 完了を押下
2. サーバ側プロジェクト初期設定① Eclipseのワークスペース \ 作成したプロジェクト\ WebContent \ WEB-INF \ lib ディレクトリに
Axis2を展開したディレクトリ\lib ディレクトリ配下のjarファイルをコピーする
② プロジェクトをリフレッシュ
③ Web.xmlを書き換える
org.apache.axis2.transport.http.AxisAdminServlet

org.apache.axis2.webapp.AxisAdminServlet
3. サービスの作成① 以下のクラスを作成する
public class test {
    public OMElement echo( OMElement request ){ return request; }
}

echoメソッドは送信されてきたメッセージを、そのまま返却するものである
4. Webサービスの登録① 作成したクラスを右クリック -> Webサービス -> Webサービスの作成を実行
② デフォルトの設定で十分なので、なにも設定せずそのまま完了ボタンを押下
この作業でWSDLが作成される。
5. クライアントプロジェクトの作成① ファイル -> 新規 -> プロジェクト -> JAVA -> JAVAプロジェクト を選択
② 任意のプロジェクト名を入力
③ 完了を押下
6. クライアントプロジェクト初期設定① 作成したプロジェクトのプロパティを開く
② JAVAのビルド・パスを表示
③ 外部JARの追加を押下
④ Axis2を展開したディレクトリ \ lib 配下のJarをすべて追加
7. クライアントを作成① 以下のクラスを作成する
# 適宜編集してください
public class Client {
    static public void main (String[] args ) {
        {
        // エンドポイントURL
        // ~/services/以降がクラス名
        String endpointStr = "http://localhost:8080/サーバ側プロジェクト名/services/test/";
        ServiceClient sender = null;
        Options options = new Options();
        try {
            sender = new ServiceClient
        } catch(AxisFault e ){
            e.printStackTrace();
           return;
        }

        // オプションの設定
        // エンドポイントの設定
        options.setTo(new EndpointReference( endpointStr ) );

        // プロトコルの設定
        options.setTransportInProtocol( Constants.TRANSPORT_HTTP);

        // オプションを反映する
        sender.setOptions( options );//送信メッセージの作成
        OMFactory fac = OMAbstractFactory.getOMFactory( );

        // ネームスペースの生成
        OMNamespace omNs = fac.createOMNamespace("http://作成したサービスのパッケージ" , "Request" );

        // 要素の作成
        OMElement request =
            fac.createOMElement(
                "echo", // 実行するメソッド
                omNs);
        OMElement msg = fac.createOMElement("Msg", omNs);
        // テキストの追加
        msg.setText ( "Hello, World !!!" );

        // 要素の追加
        request.addChild( msg );
        try{
            // リクエストの送信、レスポンスの受信
            OMElement response = sender.sendReceive( request );

            // レスポンスをコンソールに表示
            System.out.println( "レスポンス : " + response.toString() );
        }catch(AxisFault e ){
            e.printStackTrace();
           return;
        }
    }
}
8. 通信の確認① サーバを起動する。
② クライアントを実行する。
③ コンソール上に以下の表示があれば完了です
# 以下の文は適宜改行しています。
本来は一行で表示されます。
レスポンス :
<echoresponse xmlns:ns="http://サービスパッケージ名">
<return>
<msg xmlns:request="http://サービスパッケージ名">
Hello, World !!!
</msg>
</return>
</echoresponse>