Wann am besten layouts laden

Hallo,

beschfätige mich seit ein paar Tagen mit Android und wollte jetzt mal nach einer Best Practice zum Thema Layouts / Views fragen.

Im allgemeinen hat jedes Layout seine eigene Layout XML und eine dazugehörige Controller Klasse. Meine “main” Klasse beinhaltet das allgemeine Menü:


	private MenueController menueController;
	private LinearLayout menueLayout;
	private ContentLayout contentLayout;
	private ViewChanger viewChanger;

	// differnet layouts

	private View welcomeView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();

	}

	private void init() {
		
		// init objects
		menueController = new MenueController();
		viewChanger = new ViewChanger(getLayoutInflater());
		
		// load Views
		welcomeView = viewChanger.getViewToChange(R.layout.welcome_view);

		// Get Views
		contentLayout = (ContentLayout) findViewById(R.id.content_layout);
		menueLayout = (LinearLayout) findViewById(R.id.menue_layout);
		RelativeLayout welcomeLayout = (RelativeLayout) welcomeView
				.findViewById(R.id.welcome_layout);

		// stadart view of contentLayout
		changeContentView(welcomeLayout);

	

		// probably needs some check if it is a TextView object later
		for (int i = 0; i < menueLayout.getChildCount(); i++) {
			if (menueLayout.getChildAt(i) instanceof TextView) {
				ListView tmpView = (ListView) menueLayout.getChildAt(i);
				tmpView.setOnClickListener(menueController);
			}

		}

		findViewById(R.id.txtViewExit).setOnClickListener(
				new ControllViewController());
		findViewById(R.id.txtViewHidde).setOnClickListener(
				new ControllViewController());
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}```
Das contentView beinhaltet später alle Views, die sich auf drücken der einzelnen Menüpunkten ändert. 
Im Menü Controller sollen später die neuen Layouts aus der XML geladen werden und der contentView hinzugefügt werden. Dazu habe ich erstmal diese Klasse geschrieben: 
```public class ViewChanger {

	private HashMap<Integer, View> views;
	private LayoutInflater inflater;

	public ViewChanger(LayoutInflater inflater) {
	
		this.inflater = inflater;
                       this.views = new Hashmap<>();
	}

	public View getViewToChange(int layoutId) {

		View tmpView = views.get(layoutId);

		if (tmpView == null) {
			tmpView = loadXML(layoutId);
			views.put(layoutId, tmpView);
		}

		return tmpView;

	}
	

	private View loadXML(int layoutID) {

		return inflater.inflate(layoutID, null);
	}
}```
Macht das Design so überhaupt Sinn?

Nein. Schau dir mal Fragmente an. Jedes Fragment arbeitet dabei mit einem Layout und auf der Activity wechselst du dann einfach die Fragmente. Die einzelnen Fragmente kannst du dann auch wunderbar wiederverwenden.

Auf diese bin ich in der Android Dokumentation auch gestoßen. Leider kann ich die nicht verwenden, da man die größe nicht zur Runtime ändern kann.

Leider kann ich die nicht verwenden, da man die größe nicht zur Runtime ändern kann.

Doch.

Okay, ich hab keine entsprechende Methode gefunden. Könntest du mir ein kurzes Codesnippet zeigen? Bei Layouts kann man das ja normal über ein Layoutparam machen.

Ein Fragment ist mehr als eine View. Es ist ein Container der Logik und die View bündelt. Wenn du die View innerhalb des Fragments ändern willst geht das ganz genauso wie außerhalb des Fragments.

Die Frage ist viel mehr was du erreichen möchtest? Normalerweise lässt man das alles im XML und kümmert sich dann nicht mehr um die UI.

Naja es gibt die Möglichkeit ein bestimmte View ein bzw auszublenden. Wenn diese View ausgeblendet ist, hat der andere Teil meine “Content View” mehr Platz der auch genutzt werden soll.

Wenn diese View ausgeblendet ist, hat der andere Teil meine „Content View“ mehr Platz der auch genutzt werden soll.

Das passiert von alleine wenn die Views dementsprechend im XML angelegt sind. Z.b. wenn man in einem LinearLayout zwei TextViews untereinander hat und darunter eine ListView die so hoch und breit ist wie Platz da, dehnt sich die automatisch aus wenn man bei einer der TextViews die Visibility auf gone setzt.

Aber du kannst im Fragment auch die LayoutParams neu setzen wenn dir das lieber ist.

Aha! Auf gone also. Ich habe mich schon gewundert wofür das gut ist. Also passt sich das Fragment die größe vom View an?

Das Fragment in deinem XML wird mit der View die es in onCreateView erzeugt ersetzt. Direkt aus der Android-Doku:

When the system creates this activity layout, it instantiates each fragment specified in the layout and calls the onCreateView() method for each one, to retrieve each fragment’s layout. The system inserts the View returned by the fragment directly in place of the element.

Das ist das was tatsächlich in der View-Hierarchie da ist. Das kannst du auch wie gewohnt innerhalb deines Fragment-Objekts manipulieren und das passt sich entsprechend deiner Programmierung an (oder auch nicht).

So das mit dem Fragment funktioniert jetzt prima.

Wenn ich nun ein View replacen will habe ich mir folgende Methode geschrieben:

		
		FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
		
transaction.replace(id, contentFragment);


		// Commit the transaction
		transaction.commit();
	}```

Nun habe ich es jedoch so gemacht das jedes Layout seine eigene Layout ressourcen XML besitzt. Ist das so üblich? Ich muss eigentlich bei der Methode dann eine layoutID verlangen und diese zuerst inflaten oder?

Ich muss eigentlich bei der Methode dann eine layoutID verlangen und diese zuerst inflaten oder?

Wenn du das Fragment austauschen willst, musst du es über den Tag identifizieren können. Das hat nichts mit dem Layout per se zu tun. Das passiert ja im onCreateView.

Macht es überhaupt Sinn jedes “Layout” in einer extra layout ressource zu haben? Wenn ja könnte ja jedes Layout sein eigenes Fragment haben.

Generell ein klares Ja. Das macht Sinn. Im speziellen Fall muss man aufpassen, das man sich ein zusammenhängendes Layout nicht künstlich auftrennt und dann mit includes wieder zusammen setzt, aber ansonsten macht man das schon so.

Das ist auch der Sinn dahinter. Ein Layout ist im Normalfall eine gebündelte Komponente. Diese enthält auch ihre dazugehörige Logik. Oder in den Worten der Google-Doku:

Kommt mir aber irgendwie ziemlich undynamisch vor. So brauche ich für jedes Layout eine eigene Klasse die von Fragment erbt.
So etwas funktioniert nämlich nicht:


private int layoutId;

	public CustomFragment(int layoutId) {
		// TODO Auto-generated constructor stub
this.layoutId = layoutId; 
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// Inflate the layout for this fragment
		return inflater.inflate(layoutId, container, false);
	}

}```

Der Fehler: 

> This fragment should provide a default constructor (a public constructor with no arguments) (customlayouts.CustomFragment)

Es kann natürlich sein das das mit dem Gui Builder von Android zusammenhängt und ich die Warnung getrost ignorieren kann, da ich es ja in meinem Code habe und nicht in einem xml.

Jop, weil du das Konzept noch nicht verstanden hast.

Ein Fragment ist eine Komponente. Da geht’s um mehr als nur den ViewContainer mit Elementen zu füllen. Da hängen dann auch die ganzen EventHandler etc. drinnen. Nach außen hin, zur Activity z.B., werden nur die nötigsten Methoden exposed die dazu dienen Werte oder ähnliches abzufragen.

Das ist der springende Punkt. Du baust also keine ultra-generische Fragmentklasse die nichts tut außer die View zu laden sondern du baust für jede Aktivität die du in deiner View abbilden willst ein Fragment und gestaltest da dazugehörig ein eigenes Layout. Diese Fragments instanzierst du dann in deiner Activity. Wenn du dann die Funktionalität von Fragment A brauchst tauscht du das gerade angezeigte Fragment C damit aus. Die Kommunikation zur Activity - falls nötig - sollte dann über bestimmte Interfaces (z.B. eigene EventHandler) laufen.

Im Optimalfall sind die Fragments so abgetrennt das es egal ist ob du jetzt ein einziges anzeigst oder zwei nebeneinander - für die Tabletansicht z.B.

Jetzt macht es langsam klick. Das Fragment ist eine Art Controller und kümmert sich alleine um die View, die es beinhaltet. Fragmente hingegen können zur runtime beliebig gewechselt werden mit der FragmentTransaction.

Edit: Kennst du vielleicht irgendeine opensource App, die das Prinzip richtig durchzieht?