2012-05-08 12 views
6

Stworzyłem własny niestandardowy SurfaceView, który działa dobrze na własną rękę, ale kiedy próbuję umieścić dwa na osobnych kartach w TabWidget tylko jeden jest wyświetlany niezależnie od tego, która karta jest wybrana, oraz zawsze jest to SurfaceView, który jest najpierw narysowany po uruchomieniu aplikacji.Nie mogę wyświetlić 2 wystąpienia mojego niestandardowego SurfaceView

Aby zilustrować problem, stworzyłem przykładowy kod, który można skompilować w celu wyświetlenia problemu.

SurfaceView poniżej, o nazwie SurfaceViewCircle, po prostu tworzy bitmapę, rysuje niebieskie kółko jako domyślne, a następnie wyświetla je. Istnieje publiczna metoda changeColour(), która zmieni kolor koła w bitmapie.

Po drugie, tworzę układ XML, który po prostu zawiera pojedyncze wystąpienie obiektu SurfaceViewCircle.

W klasie Activity tworzę obiekt TabWidget i host itd. Następnie nadymam powyższy XML dwukrotnie, ale w jednym przypadku zmieniam kolor SurfaceViewCircle na czerwony. Po uruchomieniu aplikacji, bez względu na wybraną kartę, czerwone kółko jest ZAWSZE pokazane, Z WYJĄTKIEM, dla krótkiego wystąpienia, gdy aplikacja wychodzi i wyświetla się niebieskie kółko.

Czy ktoś może wskazać, czy pominięto krok podczas korzystania z SurfaceView?

Jest to kod aktywny:

public class TestActivity extends Activity { 
/** Called when the activity is first created. */ 

private TabHost mTabHost; 
private Context mTabHostContext; 
private View surfaceView1, surfaceView2; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    /* 
    * Setup tabs 
    */ 
    setContentView(R.layout.maintabs); 
     setupTabHost(); //Prepares the TabHost from code rather than XML; 
    mTabHost.getTabWidget().setDividerDrawable(R.drawable.tab_divider); //Sets a thin dividing line 
    mTabHostContext = mTabHost.getContext(); 
    surfaceView1 = LayoutInflater.from(mTabHostContext).inflate(R.layout.surfaceviewindependent, null); 
    SurfaceViewCircle s = (SurfaceViewCircle)surfaceView1.findViewById(R.id.circle1); 
    /* 
    * Change the colour to red 
    */ 
    s.changeColour(getResources().getColor(R.color.red_square)); 

    /* 
    * Create a second layout containing SurfaceViewCircle but leave circle as default blue. 
    */ 
    surfaceView2 = LayoutInflater.from(mTabHostContext).inflate(R.layout.surfaceviewindependent, null); 
    setupTab(surfaceView1,"SurfaceView1"); 
    setupTab(surfaceView2,"SurfaceView2"); 


} 

private void setupTabHost() { 
    mTabHost = (TabHost) findViewById(android.R.id.tabhost); 
    mTabHost.setup(); 
} 

private void setupTab(final View view, final String tag) { 
    View tabview = createTabView(mTabHost.getContext(), tag); // This creates a view to be used in the TAB only 

    /* this creates the tab content AND applies the TAB created in the previous step in one go */ 
    TabSpec setContent = mTabHost.newTabSpec(tag).setIndicator(tabview).setContent(new TabContentFactory() { 
     public View createTabContent(String tag) {return view;} 
    }); 
    mTabHost.addTab(setContent); 

} 

private static View createTabView(final Context context, final String text) { 
    View view = LayoutInflater.from(context).inflate(R.layout.tabs_bg, null); 
    TextView tv = (TextView) view.findViewById(R.id.tabsText); 
    tv.setText(text); 

    return view; 
} 
} 

To jest mój zwyczaj SurfaceView:

public class SurfaceViewCircle extends SurfaceView implements SurfaceHolder.Callback{ 

private Paint paint, circlePaint; 
private Bitmap bitmap = null; 
private int w; 
private int h; 
private int colour = 0; 
private Resources r = null; 
private _Thread t = null; 
private boolean surfaceIsCreated; 

public SurfaceViewCircle(Context context) { 
    super(context); 
    initialise(); 
} 

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

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

private void initialise(){ 
    r = getResources(); 
    getHolder().addCallback(this); 
    paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    paint.setFilterBitmap(true); 
    colour = R.color.blue_square; 
    circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    circlePaint.setColor(r.getColor(colour)); 
    circlePaint.setStyle(Style.FILL); 
    circlePaint.setStrokeWidth(0.02f); 
    t = new _Thread(getHolder()); 


} 

public void changeColour(int colour){ 
    circlePaint.setColor(colour); 
    if (surfaceIsCreated){ 
     createBitmap(); 
    } 
    synchronized (t){ 
     t.notify(); 
    } 
} 

private Bitmap createBitmap(){ 
    Bitmap b = null; 
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
    Canvas c = new Canvas(b); 
    c.scale((float)w, (float)w);  //Scales the background for whatever pixel size 
    c.drawCircle(0.5f, 0.5f, 0.5f, circlePaint); 
    //c.drawColor(r.getColor(colour)); 
    return b; 
} 

public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ 
    int width = measure(widthMeasureSpec); 
    int height = measure(heightMeasureSpec); 

    int d = Math.min(width, height); 
    setMeasuredDimension(d,d); 
} 

private int measure(int measureSpec) { 
    int result = 0; 
    // Decode the measurement specifications 
    int specMode = MeasureSpec.getMode(measureSpec); 
    int specSize = MeasureSpec.getSize(measureSpec); 

    return specSize; 
} 

@Override 
protected void onSizeChanged(int w, int h, int oldW, int oldH){ 
    super.onSizeChanged(w, h, oldW, oldH); 
    //synchronized (this){ 
     this.w = Math.min(w, h); 
     this.h = w; 
    //} 
    Bitmap b = createBitmap(); 

     bitmap = b; 

    Log.i("Square", "onSizeChanged() called."); 


} 
@Override 
public void surfaceChanged(SurfaceHolder holder, int format, int width, 
     int height) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void surfaceCreated(SurfaceHolder holder) { 
    Log.i("Panel", "surfaceCreated() called."); 
    t.setRunning(true); 
    t.start(); 
    surfaceIsCreated = true; 

} 

@Override 
public void surfaceDestroyed(SurfaceHolder holder) { 
    Log.i("Square", "surfaceDestroyed() called."); 

    surfaceIsCreated = false; 
    boolean retry = true; 
    synchronized (t){ 
     t.setRunning(false); 
     t.notify(); 
    } 
    while (retry) { 
     try { 
      t.join(); 
      retry = false; 
     } catch (InterruptedException e) { 
      // we will try it again and again... 
     } 
    } 

} 

private class _Thread extends Thread { 
    private SurfaceHolder _surfaceHolder; 
    private boolean _run = false; 

    public _Thread(SurfaceHolder surfaceHolder) { 
     _surfaceHolder = surfaceHolder; 
    } 

    public void setRunning(boolean run) { 
     _run = run; 
    } 

    @Override 
    public void run() { 
     Canvas c = null; 
     while (_run){ 
      try { 
       c = _surfaceHolder.lockCanvas(null); 
       synchronized (_surfaceHolder) { 
        synchronized(bitmap){ 
         c.drawBitmap(bitmap, 0, 0, paint); 
        } 
       } 
      } finally { 
       // do this in a finally so that if an exception is thrown 
       // during the above, we don't leave the Surface in an 
       // inconsistent state 
       if (c != null) { 
        _surfaceHolder.unlockCanvasAndPost(c); 
       } 
      } 
      synchronized(this){ 
       try { 
        wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 

       } 
      } 
     } 
    } 
} 
} 

Plik maintabs.xml:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation="vertical" android:layout_width="fill_parent" 
android:layout_height="fill_parent"> 
<TabHost xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/tabhost" android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
    <LinearLayout android:orientation="vertical" 
     android:layout_width="fill_parent" android:layout_height="fill_parent"> 
     <TabWidget android:id="@android:id/tabs" 
      android:layout_width="fill_parent" android:layout_height="wrap_content" 
      android:layout_marginLeft="0dip" android:layout_marginRight="0dip" /> 
      <FrameLayout android:id="@android:id/tabcontent" 
      android:layout_width="fill_parent" android:layout_height="fill_parent" /> 
    </LinearLayout> 
    </TabHost> 
</LinearLayout> 

A surfaceviewindependent.xml:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 
<uk.co.androidcontrols.gauges.SurfaceViewCircle 
android:id="@+id/circle1" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:layout_weight="0.5" 
    android:layout_margin="1dip"> 
</uk.co.androidcontrols.gauges.SurfaceViewCircle> 
</LinearLayout> 

Zauważyłem też, że ktoś miał podobny problem here.

Przepraszamy za słabą formatowania ale edytor kodu jest prawie niemożliwe do wykorzystania na dużych cytaty kod!

Dodatkowe informacje

Próbowałem za pomocą setVisibility()' w onvisibilityChanged() ale ostatecznie powoduje wyjątek:

protected void onVisibilityChanged(View changedView, int visibility){ 
    super.onVisibilityChanged(changedView, visibility); 
    changedView.setVisibility(visibility); 
    Log.i("SurfaceViewCircle", "onVisibilityChanged() called."); 
} 

java.lang.IllegalThreadStateException: Thread already started. 

wydaje się powołanie changedView.setvisibility() niszczy powierzchnię za każdym razem.

+0

na setupTab jesteś tylko tworząc jedną, jak ja to widzę, spróbuj sprawdzić w ten sposób: http://stackoverflow.com/a/5034063/ 1084764 – Raykud

+0

Dzięki Raykud. Sprawdzi to i zgłoś się nieco później. – Kerry

Odpowiedz

1

Wygląda na to, co chcę zrobić z SurfaceView nie jest zalecane: link

4

Zbudowałem projekt testowy oparty na twoim kodzie i niespodziewanie spędziłem prawie kilka godzin na nim bawiąc się. Szybko wyrzucę moje odkrycia, bo powinienem trafić do worka!

Przede wszystkim zdecydowanie tworzysz dwie karty, przy czym każda karta ma osobne wystąpienie niestandardowego SurfaceView. W porządku.

Teraz gdy Activity pierwsze uruchamia się i pierwsza karta jest widoczna, tylko pierwszy SurfaceView zostaje zainicjowany i ma surfaceCreated() nazwie, w którym momencie jego Thread przebiegów.

Po wybraniu drugiej zakładki, druga SurfaceView, do której jest dostarczane wywołanie zwrotne createTabContent(), jest inicjowana tak samo jak pierwsza. Odtąd od tego momentu, aż do wygaśnięcia, pozostają one w prawidłowym stanie powierzchni. Przełączanie między kartami nigdy nie wywołuje surfaceDestroyed() na SurfaceView, więc SurfaceCreated() nie jest nigdy ponownie wywoływana. Ani "onMeasure()" nie jest wywoływany ponownie po pierwszym utworzeniu. Dlatego to mówi mi, że zarówno SurfaceView s pozostają w ogólnej hierarchii View. Zarówno SurfaceViews 'Thread s są uruchomione, a jeśli nie masz tam wait(), obie będą stale próbować renderować do pamięci wideo.

Jak wiadomo, numer SurfaceView jest bardzo unikalny pod względem sposobu, w jaki siedzi (lub raczej nie siedzi) w hierarchii View. To, co się tutaj dzieje, polega na tym, że pierwsze SurfaceView do utworzenia jest tym, którego wynik jest widoczny w pamięci wideo, niezależnie od wyboru karty.

Jedna rzecz, którą po raz pierwszy spróbowałem, to mieć drugi numer SurfaceView znacznie mniejszy niż pierwszy, z proporcjonalnie mniejszym okręgiem w środku. Po przełączeniu z pierwszej zakładki (większa SurfaceView z dużym czerwonym kółkiem) na drugą zakładkę (mniejszą SurfaceView z mniejszym niebieskim kółkiem), mogłem zobaczyć, że rozmiar widocznego SurfaceView został zmniejszony poprawnie tak, jakby drugi SurfaceView był widoczny, ale raczej niż jego mniejsze niebieskie koło było widoczne, miałem dużą część pierwszego dużego czerwonego kółka SurfaceView, ale przycięte przez mniejszy rozmiar drugiego SurfaceView.

Co skończyło się z gry były następujące dwie metody połączenia:

((SurfaceView)surfaceView1.findViewById(R.id.circle1)).setVisibility(View.GONE); 

((SurfaceView)view.findViewById(R.id.circle1)).bringToFront(); 

Ten ostatni, bringToFront(), nie wydaje się cokolwiek osiągnąć. Ale wybranie numeru setVisibility(View.GONE) na karcie SurfaceView tak, jak wybrano kartę sekunda, sprawiło, że ładnie przechodziło od czerwonego koła do niebieskiego koła.

Co myślę, że dlatego trzeba próbować robić szuka odpowiedniego TabHost API metody wywołania zwrotnego aby zastąpić który będzie nosił nazwę every razem karta jest zaznaczona (być może przy użyciu TabHost.OnTabChangeListener) i użyć jej jako miejsca zadzwonić setVisibility() jako odpowiedni na wszystkich SurfaceView s, aby kontrolować, który pojawia się na górze.

+0

Dzięki Trevor! Musiałem też trochę przespać po zamieszczeniu mojego pytania, a teraz muszę iść do pracy! W dalszej części zajmiemy się dobrze Twoją odpowiedzią. – Kerry

+0

Trevor: Kilka rzeczy do zapamiętania. Jeśli nadpiszesz metodę onVisibilityChanged(), zobaczysz, że zostanie ona wywołana, gdy zmieni się wybór karty. Zdaję sobie jednak sprawę z tego, że pisząc to, w moim rzeczywistym kodzie "produkcyjnym" (nie pokazanym tutaj) NIE wzywam się do super metody ... chociaż w powyższym przykładzie nie przekręciłem tej metody. Również jeśli dodasz trzecią kartę do powyższego przykładu, który zawiera standardowy przycisk Androida, przełączanie między przyciskiem a widokiem powierzchni jest w porządku, ale znowu nie między wyświetleniami powierzchni.Mogę spojrzeć na źródło dla klasy View. – Kerry

+0

Próbowałem również wywołać metodę setVisibility(), ale wystąpiły problemy, ale mogę ponownie zbadać metodę ethis. – Kerry

Powiązane problemy