2012-05-08 15 views
6

He creado mi propio SurfaceView personalizado que funciona bien por sí mismo, pero cuando intento poner dos en pestañas separadas en un TabWidget, solo se muestra una, independientemente de la pestaña que se seleccione, y siempre es el SurfaceView que se dibuja por primera vez cuando se inicia la aplicación.No se pueden mostrar 2 instancias de mi SurfaceView personalizado

Para ilustrar el problema, he creado un código de muestra que se puede compilar para mostrar el problema.

El SurfaceView a continuación, llamado SurfaceViewCircle simplemente crea un mapa de bits, dibuja un círculo azul como predeterminado y luego lo muestra. Hay un método público, changeColour(), que cambiará el color del círculo en el mapa de bits.

En segundo lugar, creo un diseño XML que simplemente contiene una sola instancia de SurfaceViewCircle.

En la clase Activity, creo un TabWidget y un host etc. Integro el XML anterior dos veces pero en una instancia cambio el color de SurfaceViewCircle a rojo. Una vez que se ejecuta la aplicación, no importa qué pestaña seleccione, el círculo rojo SIEMPRE se muestra EXCEPTO para una breve instancia cuando la aplicación sale y se muestra el círculo azul.

¿Alguien puede señalar si me he perdido un paso al usar SurfaceView?

Este es el código de la actividad:

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

Ésta es mi costumbre 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 

       } 
      } 
     } 
    } 
} 
} 

El archivo 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> 

Y el 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> 

También se señaló que otra persona ha tenido un problema similar here.

Disculpas por los pobres formato, pero el editor de código es casi imposible de usar para las grandes citas de código!

Información adicional

He intentado utilizar el setVisibility()' en onvisibilityChanged() pero con el tiempo se traduce en una excepción:

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. 

Aparece llamando changedView.setvisibility() destruye la superficie cada vez.

+0

en su setupTab sólo está creando una como yo lo veo, trata de leer esto: http://stackoverflow.com/a/5034063/ 1084764 – Raykud

+0

Gracias Raykud. Lo verificará e informará un poco más tarde. – Kerry

Respuesta

1

Parece que lo que yo quiero hacer con SurfaceView no se recomienda: link

4

Creé un proyecto de prueba basado en su código y, sorprendentemente, pasé casi un par de horas jugueteando con él. ¡Voy a soltar rápidamente mis hallazgos ahora, ya que debo golpear el saco!

Antes que nada, definitivamente está creando dos pestañas, cada pestaña tiene una instancia separada de su SurfaceView personalizado. Esta bien.

Ahora, cuando se muestran los primeros Activity se pone en marcha y la primera pestaña, sólo la primera SurfaceView es inicializado y tiene surfaceCreated() llamada, momento en el que sus Thread carreras.

Cuando se selecciona la segunda pestaña, la segunda SurfaceView que la devolución de llamada createTabContent() proporciona se inicializa igual que la primera. Desde entonces, desde este punto, hasta el desmontaje de Activity, ambosSurfaceView s permanecen en su estado de superficie válido. Alternar entre pestañas nunca invoca surfaceDestroyed() en SurfaceView, por lo que SurfaceCreated() tampoco se invoca nunca más. Tampoco se invoca 'onMeasure()' después de la primera creación. Por lo tanto, esto me dice que ambos SurfaceView s permanecen en la jerarquía general View. Ambos SurfaceViews 'Thread s se están ejecutando y, si no tiene el wait() allí, ambos intentarán renderizar continuamente en la memoria de video.

Como usted sabe, un SurfaceView es muy único en la forma en que se sienta (o más bien, no se sienta) dentro de la jerarquía View. Lo que parece estar sucediendo aquí es que el primer SurfaceView que se creará es aquel cuya salida se ve en la memoria de video, independientemente de la selección de tabulación.

Una cosa que intenté por primera vez fue tener la segunda SurfaceView significativamente más pequeña que la primera, con un círculo proporcionalmente más pequeño dentro. Al cambiar de la primera pestaña (mayor SurfaceView con círculo rojo grande) a la segunda pestaña (menor SurfaceView con un círculo azul más pequeño), pude ver que el tamaño del SurfaceView visible se reducía correctamente como si el segundo SurfaceView se hiciera visible, pero de que su círculo azul más pequeño sea visible, tuve solo una gran porción del primer círculo rojo grande de SurfaceView perforando, pero recortado por el tamaño más pequeño del segundo SurfaceView.

Lo que terminé jugando con fueron los dos métodos siguientes llamadas:

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

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

Este último, bringToFront(), no parecía lograr cualquier cosa. Pero el uso de la llamada setVisibility(View.GONE) en la primera SurfaceView al igual que la segunda pestaña SurfaceView 's se ha seleccionado a continuación, tuvo que muy bien el cambio de un círculo rojo con un círculo azul.

Lo que pienso, por lo tanto hay que probar haciendo es buscando un método adecuado rellamada TabHost API para anular que se llamará every vez que se selecciona una ficha (tal vez usando TabHost.OnTabChangeListener) y usar eso como un lugar para llamar setVisibility() según corresponda en todos los SurfaceView s para controlar cuál aparece arriba.

+0

Gracias Trevor! ¡También tuve que dormir un poco después de publicar mi pregunta y ahora tengo que ir a trabajar! Voy a echar un buen vistazo a tu respuesta más adelante. – Kerry

+0

Trevor: algunas cosas para tener en cuenta. Si reemplaza el método onVisibilityChanged() verá que esto se llama cuando se cambia la selección de pestañas. Sin embargo, cuando escribo esto, me doy cuenta de que, en mi código de "producción" real (no se muestra aquí), NO llamo al supermétodo ... aunque en el ejemplo anterior no he omitido este método. Además, si agrega una tercera pestaña al ejemplo anterior que contiene un botón estándar de Android, entonces cambiar entre Botón y vista de superficie está bien, pero de nuevo no entre las vistas de la superficie.Podría ver la fuente de la clase View. – Kerry

+0

También intenté llamar al método setVisibility() pero encontré algunos problemas, pero posiblemente pueda volver a investigar ethis method nuevamente. – Kerry

Cuestiones relacionadas