我想这样做,以便我的片段可以更改其标记而无需重新创建自身(即无需重新创建片段并调用其构造函数)。我试过使用 ViewGroup 及其 setView() 和 removeView() 方法将一个标记替换为另一个标记,但我似乎变得更加困惑。
还有另一种方法可以解决我的问题 - 在 Activity 标记中,立即为每个标记创建两个片段,使其中一个不可见。我认为从效率和节省资源的角度来看,这不是一种有效和正确的方法。我想知道您对如何最好地解决这个问题的看法。
下面您可以看到一段代码解决了我的问题,但是重新创建了片段对象并在单击操作栏上的特定按钮之后。当您需要更改片段的标记而不重新创建它时该怎么办?
// Обработка выбора команд меню
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
FragmentManager fm = getFragmentManager(); // Необходим для транзакий фрагментов: при удалении и заменене одного фрагмента другим
// Выбор в зависимости от идентификатора MenuItem
switch (item.getItemId()) {
case R.id.shopping_cart:
return true; // Событие меню обработано
case R.id.sort:
if (fm != null) {
/*
Perform the FragmentTransaction to load in the list tab content.
Using FragmentTransaction#replace will destroy any Fragments
currently inside R.id.fragment_content and add the new Fragment
in its place. (с) Google doc
*/
FragmentTransaction ft = fm.beginTransaction(); // Начало транзакции
( (ViewGroup) getActivity().findViewById(R.id.fragment_menu) ).removeAllViews(); // Удаляет View на экране (сам список)
ft.remove(this); // Удаляет кнопки на палени действий (TODO: и вместе с ним сам фрагмент?)
// Замена фрагмента
if (currentMode == CARD_MODE) {
ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(PLATE_MODE));
}else {// currentMode == PLATE_MODE
ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(CARD_MODE));
}
ft.commit(); // Завершение транзакции
}
return true; // Событие меню обработано
}
return super.onOptionsItemSelected(item); //TODO: Разобраться зачем вообще тут нужен супер
}
如果您需要其余代码和标记,这里是完整项目代码的链接:https ://github.com/RareScrap/TennoSushi/tree/master
更新 1这是两个标记的代码,将代码显示为图块和普通列表
fragment_menu_card_list.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.webtrust.tennosushi.MenuListFragment">
<!-- TODO: Update blank fragment layout -->
<ListView
android:id="@+id/cardList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"></ListView>
</FrameLayout>
fragment_menu_plates_list.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/plates_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<GridView
android:id="@+id/platesList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:horizontalSpacing="0dp"
android:numColumns="2"
android:verticalSpacing="0sp" />
</RelativeLayout>
更新 2片段代码
package com.webtrust.tennosushi;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Простой наследник класса {@link Fragment}.
* Активити, которые содержат этот фрагмент должно реализовывать
* интерфейс {@link MenuListFragment.OnFragmentInteractionListener}
* для обработки событий взаимодействия между активностью и фрагментом.
* Используйте {@link MenuListFragment#newInstance} фабричный метод для
* создания экземпляра этого фрагмента.
* @author RareScrap
*/
public class MenuListFragment extends Fragment {
// Константы, определяющие режим отображения списка
public static int CARD_MODE = 0;
public static int PLATE_MODE = 1;
// Закомментирован, т.к. еще не изучен
//private OnFragmentInteractionListener mListener;
// Список объектов MenuItem, представляющих элементы главного меню (категории блюд)
private List<MenuItem> menuItemList = new ArrayList<>();
// ArrayAdapter связывает объекты MenuItem с элементами ListView
private MenuItemArrayAdapter menuItemArrayAdapter;
private ListView menuItemListListView; // View для вывода информации в виде списка
private GridView menuItemListGridView; // View для вывода информации в виде плиток
private int currentMode; // Текущий режим отображения списка
/**
* Необходимый пустой публичный конструктор
*/
public MenuListFragment() {
setArguments(PLATE_MODE);// режим по умолчанию
}
/**
* Метод-замена для конструктора с параметрами т.к.
* Google ОЧЕНЬ не рекомендует иметь дополнительные конструкторы
* во фрагментах
*
* @param mode Режим отображения списка
* @return this Возвращает этот же фрагмент (нужночтобы вызывать сразу после конструктора во FragmentTransaction
* */
public android.support.v4.app.Fragment setArguments(int mode) {
this.currentMode = mode; // режим по умолчанию
return this;
}
/**
* Используйте этот фабричный метод для создания новых экземпляров
* этого фрагмента с использованием продоставленных параментров
* (черт знает где эти "параметры", япросто перевел сгенерированный коммент)
*
* @return Новый объект фрагмента {@link MenuListFragment}.
*/
// TODO: Переменуйте и измените типы и количество параметров (перевод)
// TODO: разобраться зачем нужен этот метод
public static MenuListFragment newInstance() {
MenuListFragment fragment = new MenuListFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true); // у фрагмента имеются команды меню
// Inflate the layout for this fragment
if (currentMode == CARD_MODE) {
return inflater.inflate(R.layout.fragment_menu_card_list, container, false);
}else { // currentMode == PLATE_MODE
return inflater.inflate(R.layout.fragment_menu_plates_list, container, false);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// TODO: Лучше ли это место для установки адаптера?
// ArrayAdapter для связывания weatherList с weatherListView
menuItemArrayAdapter = new MenuItemArrayAdapter(getActivity(), menuItemList);
if (currentMode == CARD_MODE) {
menuItemListListView = (ListView) getView().findViewById(R.id.cardList);
menuItemListListView.setAdapter(menuItemArrayAdapter);
}else { // currentMode == PLATE_MODE
menuItemListGridView = (GridView) getView().findViewById(R.id.platesList);
menuItemListGridView.setAdapter(menuItemArrayAdapter);
}
try {
URL url = new URL("http://192.168.1.127/index.php");
GetDataTask getLocalDataTask = new GetDataTask();
getLocalDataTask.execute(url);
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
/*if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}*/
}
@Override
public void onDetach() {
super.onDetach();
//mListener = null;
}
/**
* Отображение команд меню фрагмента
* @param menu Меню
* @param inflater Инфлатер для меню
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_list_menu, menu);
}
// Обработка выбора команд меню
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
FragmentManager fm = getFragmentManager(); // Необходим для транзакий фрагментов: при удалении и заменене одного фрагмента другим
// Выбор в зависимости от идентификатора MenuItem
switch (item.getItemId()) {
case R.id.shopping_cart:
return true; // Событие меню обработано
case R.id.sort:
if (fm != null) {
/*
Perform the FragmentTransaction to load in the list tab content.
Using FragmentTransaction#replace will destroy any Fragments
currently inside R.id.fragment_content and add the new Fragment
in its place. (с) Google doc
*/
FragmentTransaction ft = fm.beginTransaction(); // Начало транзакции
( (ViewGroup) getActivity().findViewById(R.id.fragment_menu) ).removeAllViews(); // Удаляет View на экране (сам список)
ft.remove(this); // Удаляет кнопки на палени действий (TODO: и вместе с ним сам фрагмент?)
// Замена фрагмента
// TODO: Сделать так, чтобы разметка фрагмента менялась без пересоздания (читай удаления) макета
if (currentMode == CARD_MODE) {
ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(PLATE_MODE));
}else {// currentMode == PLATE_MODE
ft.replace(R.id.fragment_menu, new MenuListFragment().setArguments(CARD_MODE));
}
ft.commit(); // Завершение транзакции
}
return true; // Событие меню обработано
}
return super.onOptionsItemSelected(item); //TODO: Разобраться зачем вообще тут нужен супер
}
/**
* Этот интерфейс должно быть реализован в активити, которая содержит этот
* фрагмент, чтобы фрагмен смог взаимодействовать с активити
* и ,возможно, с другими фрагментами, содержащиеся в этой активити.
* <p>
* Подробнее смотрите в Android Training lesson: <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a>
* </p>.
*/
// Закомментирован, т.к. еще не изучен
/*public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}*/
/* Обращение к REST-совместимому (якобы) веб-сервису за данными блюд и меню
и сохранение этих данных в локальном файле HTML */
/**
* Внутренний класс {@link AsyncTask} для загрузки данных
* в формате JSON.
* @author RareScrap
*/
private class GetDataTask extends AsyncTask<URL, Void, JSONObject> {
/**
* Получение данных из сети
* @param params URL для получения JSON файла
* @return JSON файл с категориями меню и блюдами в них
*/
@Override
protected JSONObject doInBackground(URL... params) {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) params[0].openConnection(); // Для выдачи запроса достаточно открыть объект подключения
int response = connection.getResponseCode(); // Получить код ответа от веб-сервера
if (response == HttpURLConnection.HTTP_OK) {
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
}
catch (IOException e) {
e.printStackTrace();
}
return new JSONObject(builder.toString());
}else {
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
connection.disconnect(); // Закрыть HttpURLConnection
}
return null;
}
/**
* Обработка ответа JSON и обновление ListView.
*
* @param jsonObject JSON файл полученный после завершения работы doInBackground()
*/
@Override
protected void onPostExecute(JSONObject jsonObject) {
if (jsonObject != null) {
convertJSONtoArrayList(jsonObject); // Заполнение weatherList
menuItemArrayAdapter.notifyDataSetChanged(); // Связать с ListView
// Прокрутить до верха
if (currentMode == CARD_MODE) {
menuItemListListView.smoothScrollToPosition(0);
}else { // currentMode == PLATE_MODE
menuItemListGridView.smoothScrollToPosition(0);
}
}
}
}
/**
* Создание объектов MenuItem на базе JSONObject
* с последующим их заесением в menuItemList.
*
* @param jsonObject Входящий JSON файл
*/
private void convertJSONtoArrayList(JSONObject jsonObject) {
menuItemList.clear(); // Стирание старых погодных данных
try {
// Получение свойства "list" JSONArray
JSONArray list = jsonObject.getJSONArray("sushi");
// Преобразовать каждый элемент списка в объект Weather
for (int i = 0; i < list.length(); ++i) {
JSONObject deash = list.getJSONObject(i); // Данные за день
// Получить JSONObject с температурами дня ("temp")
String name = deash.getString("name");
// Получить JSONObject c описанием и значком ("weather")
String picURL = deash.getString("picURL");
// Добавить новый объект Weather в weatherList
menuItemList.add( new MenuItem(name, picURL));
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
}
通过使用 LayoutInflater 并安装和更新适配器解决了该问题。对于错误的代码,我深表歉意,但是在考虑了一个星期的问题后,这个解决方案在一分钟前就出现了,而且它有效!