5

2次元でスクロールできるようにするために、カスタムスクロールを使用しているAndroidグリッドビューがあります。これは、デフォルトのスクロールが呼び出されないことを意味します。

これが、画面外の行が表示されない理由である可能性があります。私は彼らがそこにいることを知っています、彼らはレイアウトとすべてに影響を与えます、しかし彼らは決して描きません。

だから私の質問はこれです-グリッドビューがロードされたときに、表示されているタイルだけでなく、すべてのタイルを描画するように強制する方法はありますか?

ありがとう。

編集:明確にするために-私のタイルアダプターでは、子の数を正確に225に設定しました。グリッドビューでは、getChildCount()を呼び出すと165が返されます。

もう一度編集:これは、グリッドビューの高さが画面の高さよりも大きい場合にのみ発生します-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;
    }
4

4 に答える 4

5

アンドレアス

あなたの問題が単に問題である場合onDraw()。でオーバーライドすると、非常に簡単に行うことができDraw(canvas)ますGridView。これには、Activity の読み込み中にプロセッサの要件が増加するという副作用がありますが、目的の効果を生み出すことができます。このようなオーバーライドは次のようになります。

 //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);
        }
    }

追加のテクニック (編集)

をオーバーライドすると、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、 が よりも深くネストされているGridView場合は、調整する必要があります。これは、 と組み合わせて使用​​することも、draw()単独で使用することもできます。さらに、invalidate()ステートメントを適切に配置して使用すると、反応が低下する可能性がありますが、イメージを描き続けるのに役立ちます。

問題は単にレイアウトの遅延または描画の問題である可能性があります。その場合は、遅延読み込みソリューションが最適です。遅延ローダーは、必要なときにコンテンツをロードする方法であるため、通常、使用するメモリと処理が少なくなり、必要なときに必要なものが表示されます。必要性がめったにないので、今は遅延読み込みが得意ではありません。しかし、このサイトには素晴らしいコード例があります。GridView にも合わせて調整されています。

適用外と見なされる (ただし、他の人にとっては役立つ可能性があります)

私が考えている唯一の他の種類の問題は、強制終了を引き起こさないメモリ不足の問題です (そして、確かに存在します)。これらは特にとらえどころがなく、世話をするのが苦痛です。これらを見つける唯一の方法は、エラー ビューを選択して、ロードまたはスクロール中に LogCat で確認することです。または、LogCat ダンプ全体を調べることもできます (最初にアプリを実行する前に、ログをクリアすることをお勧めします)。

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

さらに、System.gc() をコードの重要な場所に追加すると、ガベージ コレクションが強制的に実行され、より多くのメモリが解放される頻度が高くなります。(これはメモリの解放を保証するものではありませんが、そうでない場合よりも頻繁に機能します)。

最良の解決策はおそらく 3 つすべてを組み合わせたものですが、この質問で得た情報よりも少し多くの情報が必要になります。たとえば、draw()and onMeasure()andonLayout()およびその他の詳細をオーバーライドしたかどうかを確認する必要があります。

于 2011-10-19T07:10:54.470 に答える
4

正直なところ、私の提案は、明らかに使用を意図していない方法でプラットフォームAPIを攻撃するのをやめることです。いくつかのゆがみを経ても、ベースのGridViewコードを使用して、やりたいことを実行するのに十分なゲームをプレイできたとしても、プラットフォームとしてのGridView実装にわずかな変更を加えても、コードが引き続き機能することを確信できます。進化します。

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

GridViewの動作が希望どおりでない場合、解決策は、希望どおりの動作をする独自のビューを作成することです。また、Androidでは、オープンソースプラットフォームからGridViewコードをベースとして取得し、アプリでコンパイルすることができるため、これはさらに簡単です(コードがそのままの状態であるため、いくつかの調整が必要になる可能性があります) SDKに対して純粋に記述する必要はないので、おそらくそうではありません...しかし何もありませんSDKに対する通常のアプリビルドでは実行できないことを実行し、アプリ内のそのコードを心ゆくまで変更して、必要な処理を実行します。実際にあなたが望むことをしない組み込みのウ​​ィジェットと戦うことなく。また、基盤となるGridViewの実装が将来変更された場合に、慎重に構築されたカードの家が崩壊することを恐れることはありません。

于 2011-10-31T03:10:29.423 に答える
0

アダプタをオーバーライドgetCount()し、アダプタの基になる配列の長さを返します。次に、mGrid.getChildCountの代わりにmAdapter.getCountを使用します。getChildCountは表示されている子のみを返しますが、getCountはデータセット内の子の総数を示します。

于 2012-02-16T09:43:04.233 に答える
0

明確化した後、問題は私が最初に信じていたものとは明らかに異なるため、代わりに別の回答を提出しました。ただし、前の回答のテクニックは忘れてはなりません。また、他の関連する問題の優れたリソースであるため、ここに保持しています。

問題:

GridViewは(ちょっと)描画しますが、同じ画像を何度も繰り返しているため、ひどいアーティファクトが発生します。結果として生じるアーティファクトは非常に悪いため、他のビューが存在するかどうかについては実際には適切な指標にはなりません。この問題は、実際には「透明度が高すぎる」という問題が原因で発生します。

Androidと透明性

Androidは透明性を処理する素晴らしい仕事をしており、上面図に焦点が合っている間、現在のビューのにあるオブジェクトを描画できます。ただし、すべてのビューが透過的である場合、Androidが更新する必要があるときにすべての背後に描画するものはありません。通常、これは問題ではありません。

一般的に、開発者は私たちが提供するツールを使用し、それらを使っていくつかのきちんとしたことを実現しようとします。(イェーイ!)そして、私たちがそれらを使用している限り、Androidは(ほとんど)「私は何をすべきか知っています!!」と言います。ただし、カスタムビューから始めると、特に適切な描画に関しては、Androidが少しおかしくなります。

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

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

つまり、「ウィンドウ」は黒く見えるかもしれませんが、実際には黒ではありません...

解決策(パートI):

したがって、答えは、拡張GridViewビューまたはその親ビューの1つに不透明な背景を設定することです。これにより、スクロール時に表示されるアーティファクトが解決されます。また、他のビューがどのようにレンダリングされているかを適切に確認できるはずです。最終的には、すべてのビューを含む最上位のビューに背景を適用してみてください。背景は、背景色を設定するのと同じくらい簡単にすることができます(不透明なドローアブルを作成するため)。

次のステップ:

関連するコードが省略されていることに気づきました。私は本当にAdapterあなたのTileAdapter拡張の種類とそれがその基本情報をどのように取得するかを知る必要があります。これは、抽選イベントの獲得と流用に影響を与える可能性があります。それがわかったら、残りの修正に取り組むことができます。

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

最終結果では、レイアウト、メジャー、または描画を調整する必要がある場合がありますが、何が起こっているのかを正確に表現するまでわかりません。

代替結果

何か、スクロールゲームでもっと頻繁に起こることを願っています。それは、ズームアウト位置から開始し、ズームイン位置に「移動」できる場合です。これにより、画面にきちんと収まる場合に言ったように、ChildCountを簡単に解決できます。それらはすべて描画します。そして、最初のズームインは、すべてがロードされた後に発生する可能性があります。次に、ロードが完了したことを示す優れたグラフィック効果があります。ズームアウトは通常のアニメーションなので、簡単に実装できます。そして、あなたはあなたの子供たち全員がロードされていることを知っています。さらに、正しく機能させるために入力する必要のあるコードが制限される場合があります。最後に、すべて同じGameGridオブジェクトを使用して実行できます。:)

これについて考えたかどうかはわかりませんが、「ねえ、これは1つの石で2羽以上の鳥を殺す簡単な方法のようだ」と思いました。

于 2011-10-26T02:49:42.517 に答える