2011-10-15 17 views
5

私はいくつかのカスタムスクロールを使用しているアンドロイドのgridviewを持っています。これは、2次元でスクロールさせるためです。つまり、デフォルトのスクロールは呼び出されません。すべてのタイルを描画するグリッドを強制的に

これは、これがオフスクリーンの行が見えない理由であると思われます。私は彼らがそこにいることを知っている、彼らはレイアウトやすべてに影響を与えますが、決して描くことはありません。

私の質問はこれです - 読み込まれたときにグリッドビューにすべてのタイルを描画させる方法はありますか?

ありがとうございました。

編集:明確にするために - 私のtileadapterでは、私は子供が正確に225私のGridViewにまでカウントし、のgetChildCount()への呼び出しは再び165

編集を返します。これはのみ発生したときの高さgridviewは画面の大きさよりも大きい - y軸上のオフスクリーンの子は単純に子カウントから減算されます - 子供のサイズを画面上にぴったり合う数に設定すると問題はなくなりますが、スクロールの目的。

コード!活動の

XMLレイアウト:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:theme="@style/Theme.Custom" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <TextView android:id="@+id/logmessage" 
    android:theme="@style/Theme.Custom" 
    android:layout_width="fill_parent" 
    android:layout_height="25dip" 
    android:text="LogMessage"/> 

    <RelativeLayout android:id="@+id/boardwrap" 
    android:layout_weight="1" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent" 
    android:gravity="center_vertical"> 
    <com.MyProject.GameGrid 
    android:id="@+id/board" 
    android:theme="@style/Theme.Custom" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:numColumns="15" 
    android:stretchMode="none" 
    android:verticalSpacing="0dip" 
    android:horizontalSpacing="0dip" 
    android:padding="0dip" 
    android:columnWidth="20dip" 
    android:scrollbars="none"/> 
</RelativeLayout> 
<RelativeLayout 
    android:id="@+id/toolbar" 
    android:layout_width="fill_parent" 
    android:layout_height="60dip" 
    android:background="#FFFFFFFF"/> 
</LinearLayout> 

活動:

public class GameBoardActivity extends Activity { 

    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.gameboard); 

     GameGrid Board = (GameGrid)findViewById(R.id.board); 
     Board.setAdapter(new TileAdapter(this)); 
    } 
} 

GameGrid:

public GameGrid(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     this.setNumColumns(15); 

     DisplayMetrics metrics = new DisplayMetrics(); 
     ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); 
     scale = metrics.density; 
     smallSize = Math.round(20 * scale); 
     largeSize = Math.round(40 * scale); 

     columnwidth = largeSize; 
     this.setColumnWidth(columnwidth); 
     Common.DebugMessage(Float.toString(columnwidth)); 

    } 

私はここに、大小のサイズを定義してい気づくこと - ダブル画面をタップすると、2つの画面を切り替えることができます。

スクロール(以前の私を助けたものを)

if (myState == TOUCH_STATE_SCROLLING) { 
        final int deltaX = (int) (mLastX - x); 
        final int deltaY = (int) (mLastY - y); 
        mLastX = x; 
        mLastY = y; 

        int xpos = this.getScrollX(); 
        int ypos = this.getScrollY(); 

        int maxX = (columnwidth * 15) - super.getWidth(); 
        int maxY = (columnwidth * 15) - super.getHeight(); 

        if (xpos + deltaX >= 0 && xpos + deltaX <= maxX && ypos + deltaY >= 0 && ypos + deltaY <= maxY) 
        { 
         this.scrollBy(deltaX, deltaY); 
        } 
        else { 
         this.scrollTo(xpos + deltaX <= 0 ? 0 : xpos + deltaX >= maxX ? maxX : xpos + deltaX, 
             ypos + deltaY <= 0 ? 0 : ypos + deltaY >= maxY ? maxY : ypos + deltaY); 
        } 
        Common.DebugMessage(this.getChildCount()); 

       } 

Common.DebugMessageがLogCat

TileAdapterにデバッグメッセージを印刷するためだけのヘルパーメソッドです:

public TileAdapter(Context c) { 
     mContext = c; 
    } 

    @Override 
    public int getCount() { 
     return 225; 
    } 

    @Override 
    public Object getItem(int position) { 
     return null; 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     ImageView imageView; 
     int colWidth = ((GameGrid)parent).getColumnWidth(); 
     if (convertView == null) { 
      imageView = new ImageView(mContext); 
      imageView.setLayoutParams(new GridView.LayoutParams(colWidth , colWidth)); 
      imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 
      imageView.setPadding(0, 0, 0, 0); 
     } 
     else { 
      imageView = (ImageView)convertView; 
     } 
     imageView.setImageResource(R.drawable.tile); 
     return imageView; 
    } 
+0

Andreas、何かが私に起こりました....あなたの 'onTouchEvent()'(scrollBy()ステートメントの後ろにある 'ACTION_MOVE:'の最後に 'myGridView.invalidate()'を追加してみてください.... ) –

+0

私はそれを試したことがあります - 何も起こりません。そのあとでgetChildCountを呼び出すと同じ番号(165)が返されますが、垂直方向のスペースの少ないデバイスでアプリを実行すると、表示される行数* 15になります。 –

+0

Andreas、あなたの賞金が足りなくなったら、私たちはこれについてもっと目を向けることができます。こうして、無駄にならない。 –

答えて

4

正に、私の提案は、プラットフォームAPIでの使用を意図していないような方法での攻撃をやめることです。たとえいくつかの歪みによって、ベースのGridViewコードで十分なゲームをプレイして、自分がしたいことをすることができたとしても...あなたのコードは、プラットフォームとしてのGridView実装のわずかな変更進化する。

実際、この種のゲームをプレイする必要はありません。 GridViewに特別なことは何もありません。それは、水平にスクロールすることができるグリッドに物を置くビューの実装にすぎませんか?

GridViewの振る舞いがあなたの望むものでない場合、解決策はあなたが望むものをあなた自身の視点で書くことです。そしてAndroidの場合は、オープンソースプラットフォームのGridViewコードをベースにして取り込み、アプリでコンパイルしておくだけで簡単に行えます(いくつかのことを調整する必要がありますおそらくそうではありません...しかし、はありません。 SDKに対して通常のアプリケーションビルドでは実行できないことです)、そのコードを次のように変更します。あなたのアプリをあなたの心のコンテンツに変換して、それが欲しいものになるようにします。実際にあなたが望むことをしないビルトインウィジェットと戦うことなく。将来的に基盤となるGridViewの実装が変更された場合、慎重に構築されたカードハウスが崩壊する心配もありません。

+0

あなたが正しいと思います。 –

5

アンドレアス、

問題が単にonDraw()の場合 問題。あなたはGridViewにオーバーライドされたDraw(canvas)で非常に簡単に行うことができます。これには、アクティビティがロードされている間にプロセッサの要件が増加するという副作用がありますが、目的の効果が得られます。次のようなオーバーライドは次のようになります。

//This may be used in your GridView or Activity -- whichever provides the best result. 
    public void draw(Canvas canvas) 
    { int _num = myGridView.getChildCount(); 
     for (int _i = _num; --_i >= 0;) 
     { View _child = (View)myGridView.getChildAt(_i); 
      if (_child != null) 
       _child.draw(canvas); 
     } 
    } 

追加のテクニック(EDIT)

は時々draw()をオーバーライドすることの副作用を持つことができます。私たちが本当にやろうとしているのは、オブジェクトが利用可能になったときに描画するトリガーです。同様の方法でinvalidate()を無効にすると、問題のどこにあるかによって効果が得られることがあります。 draw()を上書きして奇妙な結果が出ていることがわかったので、これは次の行動のようです。

//This definitely goes in your GridView 
public void invalidate() 
{ int _num = myGridView.getChildCount(); 
    for (int _i = _num; --_i >= 0;) 
    { View _child = (View)myGridView.getChildAt(_i); 
     if (_child != null) 
      _child.invalidate(); 
    } 
} 

それはあなたがしたいときに、この技術は引き分けを強制しないことを理解すべきであるが、単にOSは、利用可能なときに再描画する準備ができていることを知ることができます。無効化では自動的にViewツリーがカスケードされないため、よりも深いネストされたImageViewがある場合は調整する必要があります。これはdraw()と組み合わせて使用​​することも、独立して使用することもできます。さらに、invalidate()ステートメントを適切に配置して使用すると、レスポンスは低下する可能性がありますが、イメージを引き伸ばすのに役立ちます。

問題は、レイアウトが遅延しているか、問題が発生している可能性があります。そのような場合は、レイジーローディングソリューションが最適かもしれません。遅延ローダーは、コンテンツが必要なときに読み込む方法で、通常は必要なメモリや処理量を減らし、必要なときに必要な情報を表示します。私はめったに必要がないので、Lazy Loadingですばらしいわけではありません。しかし、this siteには大きなコード例があります。 GridViewに合わせて調整されています。

みなし適用(ただし、他人のために有用である可能性がある)

私はそれがかもしれないと思う問題の唯一の他の種類は強制クローズの原因ではないメモリの問題のうちで(はいていません、彼らは存在する)。これらは特に気にかからず、痛いものです。これらを特定する唯一の方法は、ロードビューまたはスクロール中にLogCatでエラービューを選択することです。または、LogCatダンプ全体を調べることができます(アプリを実行する前にログをクリアすることをお勧めします)。

この種の問題では、イメージのライフサイクルがどのように機能するかを知ることが重要になります。あなたのイメージが表示するためにサムネイルサイズに縮小されている場合は、フルサイズのイメージと一緒に実際のサムネイルを作成することを検討します。ランタイムでイメージを縮小すると、元のサイズに維持するよりも一時的にメモリが必要になるためです。これがすべて一挙に起こっているので、これは一時的な問題であり、永久に出現する可能性があります。これにより、メモリ要件が大幅に削減されます。 (たとえば、2x2イメージには16バイトとヘッダーが必要ですが、4x4イメージには64バイトとヘッダーが必要ですが[400倍はサイズの2倍になります])

さらに、System.gcコード内の重要な場所にガベージコレクションを強制し、より多くのメモリを解放します。 (これはメモリを解放することを保証するものではありませんが、それよりも頻繁に動作します)。

ベストソリューションはおそらく3つすべての組み合わせですが、この質問で私たちが持っているものよりも少し詳しい情報が必要です。たとえば、draw()onMeasure()onLayout()をオーバーライドしているかどうかを確認する必要があります。

+0

良い男、私はあなたのすべての提案を試してみます。私は問題がメモリ不足の例外ではないことを確かに知っています。私は多くのアンドロイドデバイスでプログラムを試してみましたが、問題は常に同じです。グリッドビューを最初に読み込んだときに表示されない行は描画されませんが、どれくらいがオフスクリーンであるかは関係ありません。私はすぐにイラストをアップロードします。 –

+0

他のものに関して - 私はdraw、onMeasureまたはonLayoutをオーバーライドしていません。私はしかし、GridViewのサブクラスではなく、何か違いがあれば、私のコーディングを行っています。私も、あなたが提供したオーバーライドされたdrawメソッドを試しました。最も興味深い結果を出すために - this.getChildCount()は、実際の数値が225の165を返します(loadtimeの画面上の子の数と一致します)。オーバーライドされたときに最初のタイルを描画するのは、すべての子に対してコールドローを実行しても何らかの理由でのみ描画されます。 –

+0

それは本当に面白いです...それは(ビューポートに関係なく)すべての子供を描く必要があります。 OK、それはメモリ不足ではありません。間違いなく描画問題です。それは知っておきましょう...あなたのコードは主に 'GridView'にあるので、' GridView'や 'Activity'に' draw() 'コードがありましたか?あなたの引き分け()が描かれているものに大きな影響を与えることができるからです。私は、親の 'View'が他の言語のようなものであれば、子の' View'が無効にならないことに驚きました。 * P.S。 ROFL * –

0

私は最初に信じていた問題とは明らかに異なるため、別の答えを提出しました。しかし、以前の回答のテクニックは忘れてはならず、他の関連する問題のための素晴らしいリソースであるので、私はそこに保管しています。

問題:

のGridViewは現在(ちょっと)描画が、恐ろしいアーチファクトをもたらす、何度も何度も同じ画像を繰り返しています。結果として生じるアーティファクトは非常に悪いので、他のビューが存在するかどうかを示す良い指標はありません。この問題は、実際には「あまりにも多くの透明性」に関する問題が原因です。

Androidと透明性

アンドロイドはトップビューがフォーカスしている間に描画されるオブジェクト下の現在のビューを可能にし、透明性を扱う素晴らしい仕事をしていません。しかし、すべてのビューが透過的であれば、リフレッシュが必要なときにAndroidのすべての背後に描画するものはありません。通常、これは問題ではありません。

一般的に、開発者は提供されているツールを使用しています。 (YAY!)そして、私たちがそれらを使う限り、Androidは(かなり)「私は何をすべきか知っている」と言います。しかし、カスタムビューで作業を開始するときは、特に適切に描画する場合には、Androidが少し気になることがあります。

GridViewは実際にはGridViewではありません。 GridViewです。 Androidビューの拡張機能として、Androidはどのように描画されるべきかについての仮定はありません。その結果、GridViewの通常の不透明な背景は存在しません。 1つは、と思うだろうが、私はRelativeLayoutでそれを持っている。それで十分ではない?答えはいいえです。レイアウトオブジェクトはレイアウトオブジェクトです。私たちが指定しない限り、彼らは背景がありません。

これは、スクロールやその他の同様の動きやアニメーションを行うと悪化します。このような場合、Androidはキャッシュしようとします。キャッシングは、ビューのすべて以下のように発生しますが、結果はすべて透過的なので、プロセス全体が表示されます。キャッシュをクリアする必要があるまではリセットされません。これがなぜ醜くて醜いのですか?

言い換えれば、「ウィンドウ」は黒表示されますが、実際には黒ではないことがあります...

ソリューション(パートI):

だから、答えはに不透明な背景を設定することです拡張GridViewまたはその親ビューのいずれかに移動します。これにより、スクロールするときに表示される成果物が解決されます。また、他のビューがどのようにレンダリングされているかを正しく見ることができます。最終的には、すべてのビューを含む一番上のビューに背景を適用してみたいとします。バックグラウンドは、背景色を設定するだけで(不透明なドロウアブルを作成するので)簡単にすることができます。

次のステップ:

私はあなたには、いくつかの適切なコードを省略している気づきました。私は本当にあなたのTileAdapterがどのように拡張し、どのようにその基本情報を取得するのか知る必要があります。これは、描画イベントをどのように取得して使用するかに影響を与える可能性があります。私たちがそれを知ったら、残りの部分を修正する作業をすることができます。

タイルをどのように配置するかを知る必要があります(ポジショニングコードはありません)。位置決めコードがないので、ビューが実際に追加されているかどうかを知る方法はありません。 ImageViewsのデフォルトの動作では、少なくとも一部が表示されていない場合、それらが表示されるまで階層に追加されません。その問題を強制的に実行したい

最終的な結果には、レイアウト、測定、描画を調整する必要がありますが、何が起こっているのかを正確に把握するまではわかりません。

代替結果

何か、私はより頻繁にスクロールゲームで起こったwishが彼らがズームアウト位置とを開始し、我々は位置にズームに「移動」できた。これは、簡単にできればありますChildCountを解決してください。スクリーンにきちんと合っていれば、彼らはすべて引き分けます。そして、すべてがすべてロードされた後、最初のズームインが発生する可能性があります。次に、ロードが完了したことを示す素晴らしいグラフィック効果があります。ズームアウトは通常のアニメーションなので、簡単に実装できます。あなたの子供のすべてが積み込まれていることを知っています。さらに、正しく動作させるためには、入力しなければならないコードが制限されることがあります。最後に、すべて同じGameGridオブジェクトで行うことができます。 :)

あなたはこれについて考えているのか分かりませんが、私は "ねえ、これは一石で2匹以上の鳥を殺す簡単な方法のようです。"

+0

こんにちは、私の問題に関心をお寄せいただきありがとうございます。今私が答えることができるのは、TileAdapterがBaseAdapterを拡張していることです。グリッドから始めると、225人の子供がすべて見えますが、ズームインすると165人の子供に戻ります。気づいたことがあるかもしれませんが、私のズームは実際にはすべての子供のサイズの倍増です。何もないので、あなたはポジションコードを見たことがありません。その部分を処理するためにGridViewを信頼していました。それ以外は、キャンバスなどを使用している可能性があるからです。 –

+0

それは絶対にうまいです。私はちょうどそれらの分野のボットンで何が起こっていたのか知​​る必要があった。ズームインすると、それらの子供たちは自分自身を階層から削除しますか? –

+0

私はそう思っています。以前は225で、ズームインすると画面に収まらない行が削除されました。 –

0

アダプターのgetCount()をオーバーライドし、アダプターの基本配列の長さを返します。次に、mGrid.getChildCountの代わりにmAdapter.getCountを使用します。 getChildCountは可視の子のみを返し、getCountはデータセットの子を返します。

関連する問題