Android Studio Java:如何保存恢复含拖拽元素的Fragment状态?
解决方案:保持拖拽元素位置不重置
一、会话内切换Fragment不丢失状态(无需持久化存储)
当前每次切换到HomeFragment都会创建新实例,导致之前的拖拽状态丢失。只需缓存HomeFragment实例,复用即可解决:
修改MainActivity代码:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private WidgetFragment widgetFragment; private HomeFragment homeFragment; // 添加HomeFragment成员变量 private final String WIDGET_FRAGMENT_TAG = "widgetfragmenttag"; private final String HOME_FRAGMENT_TAG = "homefragmenttag"; private DrawerLayout drawerLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 原有Toolbar、DrawerLayout初始化代码不变 Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); drawerLayout = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_nav, R.string.close_nav); drawerLayout.addDrawerListener(toggle); toggle.syncState(); // 初始化Fragment实例 if (savedInstanceState != null) { widgetFragment = (WidgetFragment) getSupportFragmentManager().findFragmentByTag(WIDGET_FRAGMENT_TAG); homeFragment = (HomeFragment) getSupportFragmentManager().findFragmentByTag(HOME_FRAGMENT_TAG); } else { widgetFragment = new WidgetFragment(); homeFragment = new HomeFragment(); } } @SuppressLint("NonConstantResourceId") @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()){ case R.id.nav_home: getSupportFragmentManager() .beginTransaction() .replace(R.id.fragment_container, homeFragment, HOME_FRAGMENT_TAG) .commit(); break; // 其他case代码不变 case R.id.nav_settings: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new SettingsFragment()).commit(); break; case R.id.nav_widget: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,widgetFragment, WIDGET_FRAGMENT_TAG).commit(); break; case R.id.nav_location: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new LocationFragment()).commit(); break; case R.id.fragment_temp_api_demo: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new TempApiDemoFragment()).commit(); break; case R.id.nav_education: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new EducationFragment()).commit(); break; case R.id.nav_shopping: getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,new ShoppingFragment()).commit(); break; case R.id.nav_logout: Toast.makeText(this, "Logout Button Pressed",Toast.LENGTH_SHORT).show(); break; } drawerLayout.closeDrawer(GravityCompat.START); return true; } // 原有onBackPressed方法不变 @Override public void onBackPressed() { if (drawerLayout.isDrawerOpen(GravityCompat.START)){ drawerLayout.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } }
这样切换Fragment时复用同一个HomeFragment实例,拖拽后的位置会自动保留。
二、跨会话持久化(退出APP再打开仍保持位置)
如果需要退出APP后重新打开依然保留拖拽状态,使用SharedPreferences完全可行——它是轻量级存储,频繁读写的性能开销可以忽略(apply()方法异步写入文件,不会阻塞UI)。
修改HomeFragment代码:
import android.content.Context; import android.content.SharedPreferences; // 其他import不变 public class HomeFragment extends Fragment implements View.OnDragListener, View.OnLongClickListener { TextView txtVw; ImageView imgVw; Button btn; private SharedPreferences prefs; // 定义存储键 private static final String KEY_TXT_PARENT = "txt_view_parent"; private static final String KEY_IMG_PARENT = "img_view_parent"; private static final String KEY_BTN_PARENT = "btn_parent"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); prefs = requireActivity().getSharedPreferences("DragStatePrefs", Context.MODE_PRIVATE); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View viewFragment = inflater.inflate(R.layout.fragment_home, container, false); txtVw = viewFragment.findViewById(R.id.lbl); txtVw.setTag("DRAGGABLE TEXTVIEW"); txtVw.setOnLongClickListener(this); imgVw = viewFragment.findViewById(R.id.ingvw); imgVw.setTag("ANDROID ICON"); imgVw.setOnLongClickListener(this); btn = viewFragment.findViewById(R.id.btnDrag); btn.setTag("DRAGGABLE BUTTON"); btn.setOnLongClickListener(this); // 设置拖拽监听 LinearLayout layout1 = viewFragment.findViewById(R.id.layout1); LinearLayout layout2 = viewFragment.findViewById(R.id.layout2); LinearLayout layout3 = viewFragment.findViewById(R.id.layout3); layout1.setOnDragListener(this); layout2.setOnDragListener(this); layout3.setOnDragListener(this); // 恢复之前的拖拽状态 restoreDragState(layout1, layout2, layout3); return viewFragment; } // 恢复元素位置 private void restoreDragState(LinearLayout layout1, LinearLayout layout2, LinearLayout layout3) { // 恢复TextView int txtParentId = prefs.getInt(KEY_TXT_PARENT, R.id.layout1); // 默认layout1 moveViewToParent(txtVw, txtParentId, layout1, layout2, layout3); // 恢复ImageView int imgParentId = prefs.getInt(KEY_IMG_PARENT, R.id.layout1); moveViewToParent(imgVw, imgParentId, layout1, layout2, layout3); // 恢复Button int btnParentId = prefs.getInt(KEY_BTN_PARENT, R.id.layout1); moveViewToParent(btn, btnParentId, layout1, layout2, layout3); } // 将View移动到指定父容器 private void moveViewToParent(View view, int parentId, LinearLayout... layouts) { ViewGroup currentParent = (ViewGroup) view.getParent(); if (currentParent.getId() != parentId) { currentParent.removeView(view); for (LinearLayout layout : layouts) { if (layout.getId() == parentId) { layout.addView(view); break; } } } } @Override public boolean onDrag(View v, DragEvent event) { int action = event.getAction(); switch (action) { // 原有其他case代码不变 case DragEvent.ACTION_DRAG_STARTED: if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { return true; } return false; case DragEvent.ACTION_DRAG_ENTERED: v.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN); v.invalidate(); return true; case DragEvent.ACTION_DRAG_LOCATION: return true; case DragEvent.ACTION_DRAG_EXITED: v.getBackground().clearColorFilter(); v.invalidate(); return true; case DragEvent.ACTION_DROP: ClipData.Item item = event.getClipData().getItemAt(0); String dragData = item.getText().toString(); Toast.makeText(getActivity(), "Dragged data is " + dragData, Toast.LENGTH_SHORT).show(); v.getBackground().clearColorFilter(); v.invalidate(); View vw = (View) event.getLocalState(); ViewGroup owner = (ViewGroup) vw.getParent(); owner.removeView(vw); LinearLayout container = (LinearLayout) v; container.addView(vw); vw.setVisibility(View.VISIBLE); // 保存当前元素的父容器ID到SharedPreferences saveDragState(vw, container.getId()); return true; case DragEvent.ACTION_DRAG_ENDED: v.getBackground().clearColorFilter(); v.invalidate(); if (event.getResult()) Toast.makeText(getActivity(), "The drop was handled.", Toast.LENGTH_SHORT).show(); else Toast.makeText(getActivity(), "The drop didn't work.", Toast.LENGTH_SHORT).show(); return true; default: Log.e("DragDrop Example", "Unknown action type received by OnDragListener."); break; } return false; } // 保存拖拽状态 private void saveDragState(View view, int parentId) { SharedPreferences.Editor editor = prefs.edit(); if (view == txtVw) { editor.putInt(KEY_TXT_PARENT, parentId); } else if (view == imgVw) { editor.putInt(KEY_IMG_PARENT, parentId); } else if (view == btn) { editor.putInt(KEY_BTN_PARENT, parentId); } editor.apply(); // 异步写入,不阻塞UI } // 原有onLongClick方法不变 @Override public boolean onLongClick(View v) { ClipData.Item item = new ClipData.Item((CharSequence) v.getTag()); String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN}; ClipData data = new ClipData(v.getTag().toString(), mimeTypes, item); View.DragShadowBuilder dragshadow = new View.DragShadowBuilder(v); v.startDragAndDrop(data, dragshadow, v, 0); return true; } }
关于SharedPreferences的适用性
频繁移动元素完全可以使用SharedPreferences,原因:
- 每次
apply()是异步写入磁盘,不会阻塞主线程 - 存储的数据量极小(仅几个整数ID),性能开销可以忽略
- 实现简单,无需复杂的存储逻辑
内容的提问来源于stack exchange,提问作者johndo19
相关产品推荐
相关产品推荐

